From e57632b881de925002acf69745fd519b2b8f5982 Mon Sep 17 00:00:00 2001
From: Neda Moeini <neda.moeini@geant.org>
Date: Mon, 28 Oct 2024 10:58:23 +0100
Subject: [PATCH] Add summary view to the bunch of workflows.

---
 gso/workflows/iptrunk/create_iptrunk.py       | 23 ++++++++++-
 gso/workflows/iptrunk/modify_isis_metric.py   |  8 +++-
 gso/workflows/router/create_router.py         |  6 ++-
 gso/workflows/shared.py                       | 41 +++++++++++++++++++
 gso/workflows/site/create_site.py             | 20 ++++++++-
 test/workflows/iptrunk/test_create_iptrunk.py |  2 +
 .../iptrunk/test_modify_isis_metric.py        |  1 +
 test/workflows/router/test_create_router.py   |  4 +-
 test/workflows/site/test_create_site.py       |  2 +
 test/workflows/site/test_modify_site.py       |  4 ++
 10 files changed, 102 insertions(+), 9 deletions(-)
 create mode 100644 gso/workflows/shared.py

diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index 3d56bf0f..9074af7c 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -45,6 +45,7 @@ from gso.utils.types.interfaces import JuniperLAGMember, LAGMember, LAGMemberLis
 from gso.utils.types.netbox_router import NetboxEnabledRouter
 from gso.utils.types.tt_number import TTNumber
 from gso.utils.workflow_steps import prompt_sharepoint_checklist_url
+from gso.workflows.shared import create_summary_form
 
 
 def initial_input_form_generator(product_name: str) -> FormGenerator:
@@ -160,8 +161,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         side_b_ae_members: ae_members_side_b
 
     user_input_side_b = yield CreateIptrunkSideBForm
-
-    return (
+    input_forms_data = (
         initial_user_input.model_dump()
         | verify_minimum_links.model_dump()
         | user_input_router_side_a.model_dump()
@@ -169,6 +169,25 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         | user_input_router_side_b.model_dump()
         | user_input_side_b.model_dump()
     )
+    summary_form_data = input_forms_data | {"side_a_node": router_a_fqdn, "side_b_node": router_b_fqdn}
+    summary_fields = [
+        "geant_s_sid",
+        "iptrunk_type",
+        "iptrunk_speed",
+        "iptrunk_description",
+        "iptrunk_minimum_links",
+        "side_a_node",
+        "side_a_ae_iface",
+        "side_a_ae_members",
+        "side_a_ae_geant_a_sid",
+        "side_b_node",
+        "side_b_ae_iface",
+        "side_b_ae_members",
+        "side_b_ae_geant_a_sid",
+    ]
+    yield from create_summary_form(summary_form_data, product_name, summary_fields)
+
+    return input_forms_data
 
 
 @step("Create subscription")
diff --git a/gso/workflows/iptrunk/modify_isis_metric.py b/gso/workflows/iptrunk/modify_isis_metric.py
index 7d775107..45865e39 100644
--- a/gso/workflows/iptrunk/modify_isis_metric.py
+++ b/gso/workflows/iptrunk/modify_isis_metric.py
@@ -13,6 +13,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form
 from gso.products.product_types.iptrunk import Iptrunk
 from gso.services.lso_client import LSOState, lso_interaction
 from gso.utils.types.tt_number import TTNumber
+from gso.workflows.shared import modify_summary_form
 
 
 def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
@@ -24,8 +25,11 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
         isis_metric: int = subscription.iptrunk.iptrunk_isis_metric
 
     user_input = yield ModifyIptrunkForm
-
-    return user_input.model_dump()
+    user_input = user_input.model_dump()
+    yield from modify_summary_form(
+        {"iptrunk_isis_metric": user_input["isis_metric"]}, subscription.iptrunk, ["iptrunk_isis_metric"]
+    )
+    return user_input
 
 
 @step("Update subscription")
diff --git a/gso/workflows/router/create_router.py b/gso/workflows/router/create_router.py
index 4492e86e..edc4ec0b 100644
--- a/gso/workflows/router/create_router.py
+++ b/gso/workflows/router/create_router.py
@@ -33,6 +33,7 @@ from gso.utils.workflow_steps import (
     prompt_sharepoint_checklist_url,
     run_checks_after_base_config,
 )
+from gso.workflows.shared import create_summary_form
 
 
 def initial_input_form_generator(product_name: str) -> FormGenerator:
@@ -65,8 +66,11 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
             return self
 
     user_input = yield CreateRouterForm
+    user_input = user_input.model_dump()
+    summary_fields = ["hostname", "router_site", "vendor", "ts_port", "router_role"]
+    yield from create_summary_form(user_input, product_name, summary_fields)
 
-    return user_input.model_dump()
+    return user_input
 
 
 @step("Create subscription")
diff --git a/gso/workflows/shared.py b/gso/workflows/shared.py
new file mode 100644
index 00000000..9cc61837
--- /dev/null
+++ b/gso/workflows/shared.py
@@ -0,0 +1,41 @@
+"""Shared functions for the workflows."""
+
+from collections.abc import Generator
+from typing import TypeAlias, cast
+
+from orchestrator.domain.base import ProductBlockModel
+from orchestrator.forms import FormPage
+from orchestrator.forms.validators import MigrationSummary, migration_summary
+from pydantic import ConfigDict
+
+
+def summary_form(product_name: str, summary_data: dict) -> Generator:
+    """Generate a summary form for the product."""
+    ProductSummary: TypeAlias = cast(type[MigrationSummary], migration_summary(summary_data))  # type: ignore[no-redef]
+
+    class SummaryForm(FormPage):
+        model_config = ConfigDict(title=f"{product_name} summary")
+
+        product_summary: ProductSummary
+
+    yield SummaryForm
+
+
+def create_summary_form(user_input: dict, product_name: str, fields: list[str]) -> Generator:
+    """Create a summary form for the product."""
+    columns = [[str(user_input[nm]) for nm in fields]]
+    yield from summary_form(product_name, {"labels": fields, "columns": columns})
+
+
+def modify_summary_form(user_input: dict, block: ProductBlockModel, fields: list[str]) -> Generator:
+    """Modify the summary form for the product."""
+    before = [str(getattr(block, nm)) for nm in fields]
+    after = [str(user_input[nm]) for nm in fields]
+    yield from summary_form(
+        block.subscription.product.name,
+        {
+            "labels": fields,
+            "headers": ["Before", "After"],
+            "columns": [before, after],
+        },
+    )
diff --git a/gso/workflows/site/create_site.py b/gso/workflows/site/create_site.py
index eb5409d7..384dd59f 100644
--- a/gso/workflows/site/create_site.py
+++ b/gso/workflows/site/create_site.py
@@ -15,6 +15,7 @@ from gso.services.partners import get_partner_by_name
 from gso.utils.types.base_site import BaseSiteValidatorModel
 from gso.utils.types.coordinates import LatitudeCoordinate, LongitudeCoordinate
 from gso.utils.types.ip_address import IPAddress
+from gso.workflows.shared import create_summary_form
 
 
 def initial_input_form_generator(product_name: str) -> FormGenerator:
@@ -25,8 +26,23 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
         partner: ReadOnlyField("GEANT", default_type=str)  # type: ignore[valid-type]
 
     user_input = yield CreateSiteForm
-
-    return user_input.model_dump()
+    user_input = user_input.model_dump()
+    summary_fields = [
+        "site_name",
+        "site_bgp_community_id",
+        "site_internal_id",
+        "site_tier",
+        "site_ts_address",
+        "site_country_code",
+        "site_city",
+        "site_country",
+        "site_latitude",
+        "site_longitude",
+        "partner",
+    ]
+    yield from create_summary_form(user_input, product_name, summary_fields)
+
+    return user_input
 
 
 @step("Create subscription")
diff --git a/test/workflows/iptrunk/test_create_iptrunk.py b/test/workflows/iptrunk/test_create_iptrunk.py
index c21f26cc..7a38a234 100644
--- a/test/workflows/iptrunk/test_create_iptrunk.py
+++ b/test/workflows/iptrunk/test_create_iptrunk.py
@@ -90,6 +90,7 @@ def input_form_wizard_data(request, router_subscription_factory, faker):
         "side_b_ae_geant_a_sid": faker.geant_sid(),
         "side_b_ae_members": side_b_members,
     }
+    summary_view_step = {}
 
     return [
         create_ip_trunk_step,
@@ -98,6 +99,7 @@ def input_form_wizard_data(request, router_subscription_factory, faker):
         create_ip_trunk_side_a_step,
         create_ip_trunk_side_b_router_name,
         create_ip_trunk_side_b_step,
+        summary_view_step,
     ]
 
 
diff --git a/test/workflows/iptrunk/test_modify_isis_metric.py b/test/workflows/iptrunk/test_modify_isis_metric.py
index 1c739b74..f81baac8 100644
--- a/test/workflows/iptrunk/test_modify_isis_metric.py
+++ b/test/workflows/iptrunk/test_modify_isis_metric.py
@@ -26,6 +26,7 @@ def test_iptrunk_modify_isis_metric_success(
     initial_iptrunk_data = [
         {"subscription_id": product_id},
         {"tt_number": faker.tt_number(), "isis_metric": new_isis_metric},
+        {},
     ]
     result, process_stat, step_log = run_workflow("modify_isis_metric", initial_iptrunk_data)
 
diff --git a/test/workflows/router/test_create_router.py b/test/workflows/router/test_create_router.py
index 104bea75..f9fb37fc 100644
--- a/test/workflows/router/test_create_router.py
+++ b/test/workflows/router/test_create_router.py
@@ -68,7 +68,7 @@ def test_create_nokia_router_success(
     mock_sharepoint_client.return_value = MockedSharePointClient
 
     #  Run workflow
-    initial_router_data = [{"product": product_id}, router_creation_input_form_data]
+    initial_router_data = [{"product": product_id}, router_creation_input_form_data, {}]
     result, process_stat, step_log = run_workflow("create_router", initial_router_data)
 
     state = extract_state(result)
@@ -176,7 +176,7 @@ def test_create_nokia_router_lso_failure(
 
     #  Run workflow
     product_id = get_product_id_by_name(ProductName.ROUTER)
-    initial_router_data = [{"product": product_id}, router_creation_input_form_data]
+    initial_router_data = [{"product": product_id}, router_creation_input_form_data, {}]
     result, process_stat, step_log = run_workflow("create_router", initial_router_data)
 
     result, step_log = assert_lso_interaction_success(result, process_stat, step_log)
diff --git a/test/workflows/site/test_create_site.py b/test/workflows/site/test_create_site.py
index 7642debe..112e078f 100644
--- a/test/workflows/site/test_create_site.py
+++ b/test/workflows/site/test_create_site.py
@@ -25,6 +25,7 @@ def test_create_site(responses, faker):
             "site_tier": SiteTier.TIER1,
             "site_ts_address": faker.ipv4(),
         },
+        {},
     ]
     result, _, _ = run_workflow("create_site", initial_site_data)
     assert_complete(result)
@@ -66,6 +67,7 @@ def test_site_name_is_incorrect(responses, faker):
             "site_ts_address": faker.ipv4(),
             "partner": "GEANT",
         },
+        {},
     ]
 
     with pytest.raises(FormValidationError, match=expected_exception_msg):
diff --git a/test/workflows/site/test_modify_site.py b/test/workflows/site/test_modify_site.py
index a79b3b01..fb45a957 100644
--- a/test/workflows/site/test_modify_site.py
+++ b/test/workflows/site/test_modify_site.py
@@ -15,6 +15,7 @@ def test_modify_site(responses, site_subscription_factory, faker):
             "site_internal_id": faker.pyint(),
             "site_ts_address": faker.ipv4(),
         },
+        {},
     ]
     result, _, _ = run_workflow("modify_site", initial_site_data)
     assert_complete(result)
@@ -39,6 +40,7 @@ def test_modify_site_with_duplicate_bgp_community_id(faker, site_subscription_fa
         {
             "site_bgp_community_id": duplicate_bgp_community_id,
         },
+        {},
     ]
 
     with pytest.raises(FormValidationError, match="site_bgp_community_id must be unique"):
@@ -57,6 +59,7 @@ def test_modify_site_with_duplicate_internal_id(faker, site_subscription_factory
         {
             "site_internal_id": duplicate_internal_id,
         },
+        {},
     ]
 
     with pytest.raises(FormValidationError, match="site_internal_id must be unique"):
@@ -75,6 +78,7 @@ def test_modify_site_with_duplicate_ts_address(faker, site_subscription_factory)
         {
             "site_ts_address": duplicate_ts_address,
         },
+        {},
     ]
 
     with pytest.raises(FormValidationError, match="site_ts_address must be unique"):
-- 
GitLab