diff --git a/docs/internals/extensions/metamodel.md b/docs/internals/extensions/metamodel.md index c14a91dcf..e02201b14 100644 --- a/docs/internals/extensions/metamodel.md +++ b/docs/internals/extensions/metamodel.md @@ -1,7 +1,7 @@ (metamodel)= # score_metamodel -The `score_metamodel` extension is a core extension/component of the Docs-As-Code. +The `score_metamodel` extension is a core extension/component of the Docs-As-Code. It provides metamodel definitions, validation checks, and project layout management for Sphinx documentation. ## Overview @@ -33,7 +33,7 @@ The extension implements a multi-tier checking system: - Require access to the complete needs graph - Examples: Link validation, dependency checking, cross-reference verification -This extension comes with Docs-As-Code. +This extension comes with Docs-As-Code. Add `score_metamodel` to your extensions in `conf.py`: ```python @@ -44,6 +44,23 @@ extensions = [ ] ``` +## need types + +Each type of needs is defined in the `needs_types` section of the `metamodel.yaml` file. Each need type has attributes, links, tags, and other properties that define its structure and behavior within the documentation system. + +Each need type is introduced via `:` followed by its properties indented under it. + +Properties: +- **title**: The title of the need type. +- **prefix**: A unique prefix used to identify the need type. Default is the type name followed by `__`. +- **mandatory_options**: A list of mandatory options that must be provided for the need type. + `id` is worth mentioning as it is automatically included and must be unique. Default is the prefix followed by `[a-z0-9_]`. +- **optional_options**: A list of optional options that can be provided for the need type. +- **mandatory_links**: A list of mandatory links to other need types that must be included. +- **optional_links**: A list of optional links to other need types that can be included. +- **tags**: A list of tags associated with the need type. +- **parts**: The number of parts (separated by `__`) within the need ID. + ## Creating New Validation Checks The extension automatically discovers checks from the `checks/` directory and the metamodel.yaml config. There are several types of checks you can implement: @@ -73,23 +90,23 @@ needs_types: ``` ### 2. Generic Graph Checks (Configuration-Based) -Generic graph checks are defined in the metamodel.yaml under `graph_checks`. +Generic graph checks are defined in the metamodel.yaml under `graph_checks`. These checks all follow the same structure: ```yaml : needs: - include: , #list of your needs + include: , #list of your needs condition: check: : - explanation: ``` > *Note:* You can also use multiple conditions or negate conditions in either the needs or check part. -A complete example might look like so: +A complete example might look like so: ```yaml graph_checks: @@ -101,14 +118,14 @@ graph_checks: - safety != QM - status == valid check: - implements: + implements: and: - safety != QM - status == valid explanation: An safety architecture element can only link other safety architecture elements. ``` -What does this check do? +What does this check do? This check will go through each of the needs mentioned in 'include' that match the condition, and then for every single one of them check the needs that are linked inside the 'implements' attribute. Go inside those needs and check if they also fulfill the condition described. If one of them does not fulfill the condition the check fails and will let you know with a warning that it did so. @@ -126,7 +143,7 @@ prohibited_words_checks: - < word to forbid > ``` -An example might look like this: +An example might look like this: ```yaml prohibited_words_checks: content_check: @@ -201,7 +218,7 @@ score_metamodel/ ├── __init__.py ├── rst │ ├── attributes - │ │ └── ... + │ │ └── ... │ ├── conf.py │ ├── graph │ │ └── test_metamodel_graph.rst diff --git a/src/extensions/score_metamodel/checks/check_options.py b/src/extensions/score_metamodel/checks/check_options.py index 8ac264239..c2a4719a3 100644 --- a/src/extensions/score_metamodel/checks/check_options.py +++ b/src/extensions/score_metamodel/checks/check_options.py @@ -46,28 +46,18 @@ def _validate_value_pattern( pattern: str, need: NeedsInfoType, field: str, - log: CheckLogger, - as_info: bool = False, -) -> None: +): """Check if a value matches the given pattern and log the result. - If ``as_info`` is True, mismatches are reported as info (non-failing) - messages, otherwise as warnings. + Returns true if the value matches the pattern, False otherwise. """ try: - if not re.match(pattern, value): - log.warning_for_option( - need, - field, - f"does not follow pattern `{pattern}`.", - is_new_check=as_info, - ) - except TypeError: - log.warning_for_option( - need, - field, - f"pattern `{pattern}` is not a valid regex pattern.", - ) + return re.match(pattern, value) is not None + except TypeError as e: + raise TypeError( + f"Error in metamodel.yaml at {need['type']}->{field}: " + f"pattern `{pattern}` is not a valid regex pattern." + ) from e def validate_fields( @@ -102,23 +92,21 @@ def remove_prefix(word: str, prefixes: list[str]) -> str: log.warning_for_need( need, f"is missing required {field_type}: `{field}`." ) - continue # Skip empty optional fields - # Try except used to add more context to Error without passing variables - # just for that to function - try: - values = _normalize_values(raw_value) - except ValueError as err: - raise ValueError( - f"An Attribute inside need {need['id']} is " - "not of type str. Only Strings are allowed" - ) from err - # The filter ensures that the function is only called when needed. + continue # Nothing to validate if not present + + values = _normalize_values(raw_value) + for value in values: if allowed_prefixes: value = remove_prefix(value, allowed_prefixes) - _validate_value_pattern( - value, pattern, need, field, log, as_info=optional_link_as_info - ) + if not _validate_value_pattern(value, pattern, need, field): + msg = f"does not follow pattern `{pattern}`." + log.warning_for_option( + need, + field, + msg, + is_new_check=optional_link_as_info, + ) # req-Id: tool_req__docs_req_attr_reqtype @@ -137,19 +125,17 @@ def check_options( Checks that required and optional options and links are present and follow their defined patterns. """ - production_needs_types = app.config.needs_types - - need_options = get_need_type(production_needs_types, need["type"]) + need_type = get_need_type(app.config.needs_types, need["type"]) # If undefined this is an empty list allowed_prefixes = app.config.allowed_external_prefixes # Validate Options and Links field_validations = [ - ("option", need_options["mandatory_options"], True), - ("option", need_options["optional_options"], False), - ("link", need_options["mandatory_links"], True), - ("link", need_options["optional_links"], False), + ("option", need_type["mandatory_options"], True), + ("option", need_type["optional_options"], False), + ("link", need_type["mandatory_links"], True), + ("link", need_type["optional_links"], False), ] for field_type, field_values, is_required in field_validations: diff --git a/src/extensions/score_metamodel/metamodel.yaml b/src/extensions/score_metamodel/metamodel.yaml index 84c3e77da..ee19fc16f 100644 --- a/src/extensions/score_metamodel/metamodel.yaml +++ b/src/extensions/score_metamodel/metamodel.yaml @@ -49,47 +49,45 @@ prohibited_words_checks: - absolutely needs_types: + # See metamodel.md for how to define a new need type + ############################################################################## # Process Metamodel ############################################################################## # TSF tsf: - title: "TSF" - prefix: "tsf__" + title: TSF mandatory_options: - id: "^tsf__[0-9a-zA-Z_-]*$" - status: "^(draft|valid)$" + id: ^tsf__[0-9a-zA-Z_-]*$ + status: ^(draft|valid)$ optional_links: - links: "^.*$" + links: ^.*$ parts: 3 tenet: - title: "Tenet" - prefix: "tenet__" + title: Tenet mandatory_options: - id: "^tenet__[0-9a-zA-Z_-]*$" - status: "^(draft|valid)$" + id: ^tenet__[0-9a-zA-Z_-]*$ + status: ^(draft|valid)$ optional_links: links: "^.*$" parts: 3 assertion: - title: "Assertion" - prefix: "^assertion__" + title: Assertion mandatory_options: - id: "assertion__[0-9a-zA-Z_-]*$" - status: "^(draft|valid)$" + id: ^assertion__[0-9a-zA-Z_-]*$ + status: ^(draft|valid)$ optional_links: - links: "^.*$" + links: ^.*$ parts: 3 # Standard Requirement and Work Product # req-Id: tool_req__docs_stdreq_types std_req: title: Standard Requirement - prefix: std_req__ mandatory_options: - id: std_req__(iso26262|isosae21434|isopas8926|aspice_40)__[0-9a-zA-Z_-]*$ + id: ^std_req__(iso26262|isosae21434|isopas8926|aspice_40)__[0-9a-zA-Z_-]*$ status: ^(valid)$ optional_links: links: ^.*$ @@ -97,9 +95,8 @@ needs_types: std_wp: title: Standard Work Product - prefix: std_wp__ mandatory_options: - id: std_wp__(iso26262|isosae21434|isopas8926|aspice_40)__[0-9a-z_]*$ + id: ^std_wp__(iso26262|isosae21434|isopas8926|aspice_40)__[0-9a-z_]*$ status: ^(valid)$ parts: 3 @@ -109,7 +106,6 @@ needs_types: title: Workflow prefix: wf__ mandatory_options: - id: ^wf__[0-9a-z_]*$ status: ^(valid|draft)$ mandatory_links: input: ^wp__.*$ @@ -125,9 +121,7 @@ needs_types: # req-Id: tool_req__docs_req_types gd_req: title: Process Requirements - prefix: gd_req__ mandatory_options: - id: ^gd_req__[0-9a-z_]*$ # req-Id: tool_req__docs_common_attr_status status: ^(valid|draft)$ content: ^[\s\S]+$ @@ -142,9 +136,7 @@ needs_types: gd_temp: title: Process Template - prefix: gd_temp__ mandatory_options: - id: ^gd_temp__[0-9a-z_]*$ status: ^(valid|draft)$ optional_links: complies: std_req__(iso26262|isodae21434|isopas8926|aspice_40)__.*$ @@ -152,32 +144,27 @@ needs_types: gd_chklst: title: Process Checklist - prefix: gd_chklst__ mandatory_options: - id: ^gd_chklst__[0-9a-z_]*$ status: ^(valid|draft)$ optional_links: - complies: std_req__(iso26262|isodae21434|isopas8926|aspice_40)__.*$ + complies: ^std_req__(iso26262|isodae21434|isopas8926|aspice_40)__.*$ parts: 2 gd_guidl: title: Process Guideline - prefix: gd_guidl__ mandatory_options: - id: ^gd_guidl__[0-9a-z_]*$ status: ^(valid|draft)$ optional_links: - complies: std_req__(iso26262|isosae21434|isopas8926|aspice_40)__.*$ + complies: ^std_req__(iso26262|isosae21434|isopas8926|aspice_40)__.*$ parts: 2 gd_method: title: Process Method prefix: gd_meth__ mandatory_options: - id: ^gd_meth__[0-9a-z_]*$ status: ^(valid|draft)$ optional_links: - complies: std_req__(iso26262|isosae21434|isopas8926|aspice_40)__.*$ + complies: ^std_req__(iso26262|isosae21434|isopas8926|aspice_40)__.*$ parts: 2 # S-CORE Workproduct @@ -185,18 +172,15 @@ needs_types: title: Workproduct prefix: wp__ mandatory_options: - id: ^wp__[0-9a-z_]*$ status: ^(valid|draft)$ optional_links: - complies: std_(wp__iso26262|wp__isosae21434|wp__isopas8926|iic_aspice_40)__.*$ + complies: ^std_(wp__iso26262|wp__isosae21434|wp__isopas8926|iic_aspice_40)__.*$ parts: 2 # Role role: title: Role prefix: rl__ - mandatory_options: - id: ^rl__[0-9a-z_]*$ optional_links: contains: ^rl__.*$ parts: 2 @@ -204,17 +188,13 @@ needs_types: # Documents, process_description only doc_concept: title: Concept Definition - prefix: doc_concept__ mandatory_options: - id: ^doc_concept__[0-9a-z_]*$ status: ^(valid|draft)$ parts: 2 doc_getstrt: - title: Getting Startet - prefix: doc_getstrt__ + title: Getting Started mandatory_options: - id: ^doc_getstrt__[0-9a-z_]*$ status: ^(valid|draft)$ parts: 2 @@ -223,7 +203,6 @@ needs_types: title: Generic Document prefix: doc__ mandatory_options: - id: ^doc__[0-9a-z_]*$ status: ^(valid|draft|invalid)$ optional_options: safety: "^(QM|ASIL_B)$" @@ -239,9 +218,7 @@ needs_types: # req-Id: tool_req__docs_doc_types doc_tool: title: Tool Verification Report - prefix: doc_tool__ mandatory_options: - id: ^doc_tool__[0-9a-z_]*$ # req-Id: tool_req__docs_tvr_status status: ^(draft|evaluated|qualified|released|rejected)$ version: ^.*$ @@ -263,9 +240,7 @@ needs_types: # req-Id: tool_req__docs_req_types stkh_req: title: Stakeholder Requirement - prefix: stkh_req__ mandatory_options: - id: ^stkh_req__[0-9a-z_]*$ # req-Id: tool_req__docs_req_attr_reqtype reqtype: ^(Functional|Interface|Process|Non-Functional)$ # req-Id: tool_req__docs_common_attr_safety @@ -294,10 +269,8 @@ needs_types: # req-Id: tool_req__docs_req_types feat_req: title: Feature Requirement - prefix: feat_req__ style: node mandatory_options: - id: ^feat_req__[0-9a-z_]*$ # req-Id: tool_req__docs_req_attr_reqtype reqtype: ^(Functional|Interface|Process|Non-Functional)$ # req-Id: tool_req__docs_common_attr_security @@ -326,9 +299,7 @@ needs_types: # req-Id: tool_req__docs_req_types comp_req: title: Component Requirement - prefix: comp_req__ mandatory_options: - id: ^comp_req__[0-9a-z_]*$ # req-Id: tool_req__docs_req_attr_reqtype reqtype: ^(Functional|Interface|Process|Non-Functional)$ # req-Id: tool_req__docs_common_attr_security @@ -357,9 +328,7 @@ needs_types: # req-Id: tool_req__docs_req_types tool_req: title: Tool Requirement - prefix: tool_req__ mandatory_options: - id: ^tool_req__[0-9a-z_]*$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -390,9 +359,7 @@ needs_types: # req-Id: tool_req__docs_req_types aou_req: title: Assumption of Use Requirement - prefix: aou_req__ mandatory_options: - id: ^aou_req__[0-9a-z_]*$ # req-Id: tool_req__docs_req_attr_reqtype reqtype: ^(Functional|Interface|Process|Non-Functional)$ # req-Id: tool_req__docs_common_attr_security @@ -421,11 +388,9 @@ needs_types: # req-Id: tool_req__docs_arch_views feat_arc_sta: title: Feature & Feature Package Diagram - prefix: feat_arc_sta__ color: #FEDCD2 style: card mandatory_options: - id: ^feat_arc_sta__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -445,11 +410,9 @@ needs_types: # req-Id: tool_req__docs_arch_views feat_arc_dyn: title: Feature Sequence Diagram - prefix: feat_arc_dyn__ color: #FEDCD2 style: card mandatory_options: - id: ^feat_arc_dyn__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -468,11 +431,9 @@ needs_types: # req-Id: tool_req__docs_arch_views logic_arc_int: title: Logical Interface & Feature Interface View - prefix: logic_arc_int__ color: #FEDCD2 style: card mandatory_options: - id: ^logic_arc_int__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -491,11 +452,9 @@ needs_types: # req-Id: tool_req__docs_arch_types logic_arc_int_op: title: Logical Interface Operation - prefix: logic_arc_int_op__ color: #FEDCD2 style: card mandatory_options: - id: ^logic_arc_int_op__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -512,11 +471,8 @@ needs_types: # req-Id: tool_req__docs_arch_views mod_view_sta: title: Module Architecture Static View - prefix: mod_view_sta__ color: #FEDCD2 style: card - mandatory_options: - id: ^mod_view_sta__[0-9a-z_]+$ mandatory_links: includes: ^comp_arc_sta__.+$ tags: @@ -526,11 +482,8 @@ needs_types: # No process requirement mod_view_dyn: title: Module Architecture Dynamic View - prefix: mod_view_dyn__ color: #FEDCD2 style: card - mandatory_options: - id: ^mod_view_dyn__[0-9a-z_]+$ parts: 3 # Architecture Element & View @@ -538,11 +491,9 @@ needs_types: # req-Id: tool_req__docs_arch_views comp_arc_sta: title: Component & Component Package Diagram - prefix: comp_arc_sta__ color: #FEDCD2 style: card mandatory_options: - id: ^comp_arc_sta__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -563,11 +514,9 @@ needs_types: # req-Id: tool_req__docs_arch_views comp_arc_dyn: title: Component Sequence Diagram - prefix: comp_arc_dyn__ color: #FEDCD2 style: card mandatory_options: - id: ^comp_arc_dyn__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -586,11 +535,9 @@ needs_types: # req-Id: tool_req__docs_arch_views real_arc_int: title: Interface & Component Interface - prefix: real_arc_int__ color: #FEDCD2 style: card mandatory_options: - id: ^real_arc_int__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -609,11 +556,9 @@ needs_types: # req-Id: tool_req__docs_arch_types real_arc_int_op: title: Interface Operation - prefix: real_arc_int_op__ color: #FEDCD2 style: card mandatory_options: - id: ^real_arc_int_op__[0-9a-z_]+$ # req-Id: tool_req__docs_common_attr_security security: ^(YES|NO)$ # req-Id: tool_req__docs_common_attr_safety @@ -631,10 +576,8 @@ needs_types: # - architecture end - review_header: - prefix: review__header title: Review Header mandatory_options: - id: ^review__header__[0-9a-z_]*$ reviewers: ^.*$ approvers: ^.*$ hash: ^.*$ @@ -644,11 +587,9 @@ needs_types: # Implementation dd_sta: title: Static detailed design - prefix: dd_sta__ color: #FEDCD2 style: card mandatory_options: - id: ^dd_sta__[0-9a-z_]*$ security: ^(YES|NO)$ safety: ^(QM|ASIL_B)$ status: ^(valid|invalid)$ @@ -661,11 +602,9 @@ needs_types: dd_dyn: title: Dynamic detailed design - prefix: dd_dyn__ color: #FEDCD2 style: card mandatory_options: - id: ^dd_dyn__[0-9a-z_]*$ security: ^(YES|NO)$ safety: ^(QM|ASIL_B)$ status: ^(valid|invalid)$ @@ -676,9 +615,7 @@ needs_types: sw_unit: title: Software unit - prefix: sw_unit__ mandatory_options: - id: ^sw_unit__[0-9a-z_]*$ security: ^(YES|NO)$ safety: ^(QM|ASIL_B)$ status: ^(valid|invalid)$ @@ -686,11 +623,9 @@ needs_types: sw_unit_int: title: Software unit interfaces - prefix: sw_unit_int__ color: #FEDCD2 style: card mandatory_options: - id: ^sw_unit_int__[0-9a-z_]*$ security: ^(YES|NO)$ safety: ^(QM|ASIL_B)$ status: ^(valid|invalid)$ @@ -700,9 +635,7 @@ needs_types: # No requirement!! plat_saf_dfa: title: Feature Dependent Failure Analysis - prefix: plat_saf_dfa__ mandatory_options: - id: ^plat_saf_dfa__[0-9a-z_]+$ failure_id: ^.*$ failure_effect: ^.*$ sufficient: ^(yes|no)$ @@ -719,9 +652,7 @@ needs_types: # req-Id: tool_req__docs_saf_types feat_saf_dfa: title: Feature DFA (Dependent Failure Analysis) - prefix: feat_saf_dfa__ mandatory_options: - id: ^feat_saf_dfa__[0-9a-z_]+$ # req-Id: tool_req__docs_saf_attr_dfa_failure_id failure_id: ^.*$ failure_effect: ^.*$ @@ -748,9 +679,7 @@ needs_types: # req-Id: tool_req__docs_saf_types comp_saf_dfa: title: Component DFA (Dependent Failure Analysis) - prefix: comp_saf_dfa__ mandatory_options: - id: ^comp_saf_dfa__[0-9a-z_]+$ # req-Id: tool_req__docs_saf_attr_dfa_failure_id failure_id: ^.*$ failure_effect: ^.*$ @@ -778,9 +707,7 @@ needs_types: # req-Id: tool_req__docs_saf_types feat_saf_fmea: title: Feature FMEA (Failure Mode and Effects Analysis) - prefix: feat_saf_fmea__ mandatory_options: - id: ^feat_saf_fmea__[0-9a-z_]+$ # req-Id: tool_req__docs_saf_attr_fmea_fault_id fault_id: ^.*$ failure_effect: ^.*$ @@ -807,9 +734,7 @@ needs_types: # req-Id: tool_req__docs_saf_types comp_saf_fmea: title: Component FMEA (Failure Mode and Effects Analysis) - prefix: comp_saf_fmea__ mandatory_options: - id: ^comp_saf_fmea__[0-9a-z_]+$ # req-Id: tool_req__docs_saf_attr_fmea_fault_id fault_id: ^.*$ failure_effect: ^.*$ @@ -833,9 +758,6 @@ needs_types: testcase: title: Testcase Needs parsed from test.xml files - prefix: testcase__ - mandatory_options: - id: ^testcase__ optional_options: name: ^.*$ file: ^.*$ @@ -851,9 +773,7 @@ needs_types: # https://eclipse-score.github.io/process_description/main/permalink.html?id=gd_temp__change_decision_record dec_rec: title: Decision Record - prefix: dec_rec__ mandatory_options: - id: ^dec_rec__.*__.*$ status: ^(proposed|accepted|deprecated|rejected|superseded)$ context: ^.*$ decision: ^.*$ diff --git a/src/extensions/score_metamodel/tests/test_check_options.py b/src/extensions/score_metamodel/tests/test_check_options.py index 1a072386e..096039319 100644 --- a/src/extensions/score_metamodel/tests/test_check_options.py +++ b/src/extensions/score_metamodel/tests/test_check_options.py @@ -118,28 +118,3 @@ def test_unknown_option_present_in_neither_req_opt_neither_opt_opt(self): "has these extra options: `other_option`.", expect_location=False, ) - - @add_test_properties( - partially_verifies=["tool_req__docs_metamodel"], - test_type="requirements-based", - derivation_technique="requirements-analysis", - ) - def test_invalid_option_value_type_raises_value_error(self): - """Given a need with an option of wrong type (list with non-str)""" - need_1 = need( - target_id="tool_req__002", - id="tool_req__002", - type="tool_req", - some_required_option=123, - docname=None, - lineno=None, - ) - - logger = fake_check_logger() - app = Mock(spec=Sphinx) - app.config = Mock() - app.config.needs_types = self.NEED_TYPE_INFO - app.config.allowed_external_prefixes = [] - - with pytest.raises(ValueError, match="Only Strings are allowed"): # type: ignore[attr-defined] - check_options(app, need_1, cast(CheckLogger, logger)) diff --git a/src/extensions/score_metamodel/tests/test_metamodel_load.py b/src/extensions/score_metamodel/tests/test_metamodel_load.py index 423817545..3cb679653 100644 --- a/src/extensions/score_metamodel/tests/test_metamodel_load.py +++ b/src/extensions/score_metamodel/tests/test_metamodel_load.py @@ -39,7 +39,11 @@ def test_load_metamodel_data(): assert result.needs_types[0]["prefix"] == "T1" assert result.needs_types[0].get("color") == "blue" assert result.needs_types[0].get("style") == "bold" - assert result.needs_types[0]["mandatory_options"] == {"opt1": "value1"} + assert result.needs_types[0]["mandatory_options"] == { + # default id pattern: prefix + digits, lowercase letters and underscores + "id": "^T1[0-9a-z_]+$", + "opt1": "value1", + } assert result.needs_types[0]["optional_options"] == { "opt2": "value2", "opt3": "value3", diff --git a/src/extensions/score_metamodel/yaml_parser.py b/src/extensions/score_metamodel/yaml_parser.py index de26850a1..b63dcc82e 100644 --- a/src/extensions/score_metamodel/yaml_parser.py +++ b/src/extensions/score_metamodel/yaml_parser.py @@ -133,7 +133,7 @@ def load_metamodel_data() -> MetaModelData: one_type: ScoreNeedType = { "directive": directive_name, "title": directive_data["title"], - "prefix": directive_data["prefix"], + "prefix": directive_data.get("prefix", f"{directive_name}__"), "tags": directive_data.get("tags", []), "parts": directive_data.get("parts", 3), "mandatory_options": directive_data.get("mandatory_options", {}), @@ -143,6 +143,11 @@ def load_metamodel_data() -> MetaModelData: "optional_links": directive_data.get("optional_links", {}), } + # Ensure ID regex is set + if "id" not in one_type["mandatory_options"]: + prefix = one_type["prefix"] + one_type["mandatory_options"]["id"] = f"^{prefix}[0-9a-z_]+$" + if "color" in directive_data: one_type["color"] = directive_data["color"] if "style" in directive_data: