diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 3d56bf0ff9c57a0312f159c63c2f2c9bfe51d669..9074af7c3ccfd6ab3777c021a92dbce128c144a3 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 7d77510728354e40b9cd8a1424672a9fc499d4b9..45865e39b68f1263f9058edab376e94319ac2830 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 4492e86e9b1d5b3ad4a733ada87df51efae84248..edc4ec0b7877313f28af8508c3da4833e9161c55 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 0000000000000000000000000000000000000000..9cc61837bb9fdd7bde636a12ce4b0531d3ef2798 --- /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 eb5409d778c019e41ae82c85376ec48fcf4bdcc6..384dd59f0945885f54bfd5c51b51f477b3a30f69 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 c21f26cc1c2ce9fdcab5871c14f552d3c7d5eb06..7a38a234a7e0ba98dea56081b07e0bcc8be21c7e 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 1c739b74340ba9c64c0ca81d9109500c4e08aa0e..f81baac86f23d5abb4e85d84b61ac84edccaeb92 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 104bea75db4a4f9e74b3f537e60538372e5ed726..f9fb37fcfa38159a5d92c1aca8a9bd0aea2c5051 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 7642debea0c2739b1a5845db4404f35c5205884a..112e078f39c78a1545ada8d14be18dbd61da7858 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 a79b3b01b111f4aaf200d70b028bc6f487dd20db..fb45a9576e6917e66af9370dab3a69435bdf20db 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"):