Skip to content

Commit 50d77f8

Browse files
drop id and prefix from metamodel.yaml when they are redundant (#254)
1 parent 2dc7670 commit 50d77f8

File tree

6 files changed

+83
-176
lines changed

6 files changed

+83
-176
lines changed

docs/internals/extensions/metamodel.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(metamodel)=
22
# score_metamodel
33

4-
The `score_metamodel` extension is a core extension/component of the Docs-As-Code.
4+
The `score_metamodel` extension is a core extension/component of the Docs-As-Code.
55
It provides metamodel definitions, validation checks, and project layout management for Sphinx documentation.
66

77
## Overview
@@ -33,7 +33,7 @@ The extension implements a multi-tier checking system:
3333
- Require access to the complete needs graph
3434
- Examples: Link validation, dependency checking, cross-reference verification
3535

36-
This extension comes with Docs-As-Code.
36+
This extension comes with Docs-As-Code.
3737
Add `score_metamodel` to your extensions in `conf.py`:
3838

3939
```python
@@ -44,6 +44,23 @@ extensions = [
4444
]
4545
```
4646

47+
## need types
48+
49+
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.
50+
51+
Each need type is introduced via `<type-name>:` followed by its properties indented under it.
52+
53+
Properties:
54+
- **title**: The title of the need type.
55+
- **prefix**: A unique prefix used to identify the need type. Default is the type name followed by `__`.
56+
- **mandatory_options**: A list of mandatory options that must be provided for the need type.
57+
`id` is worth mentioning as it is automatically included and must be unique. Default is the prefix followed by `[a-z0-9_]`.
58+
- **optional_options**: A list of optional options that can be provided for the need type.
59+
- **mandatory_links**: A list of mandatory links to other need types that must be included.
60+
- **optional_links**: A list of optional links to other need types that can be included.
61+
- **tags**: A list of tags associated with the need type.
62+
- **parts**: The number of parts (separated by `__`) within the need ID.
63+
4764
## Creating New Validation Checks
4865

4966
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:
7390
```
7491
7592
### 2. Generic Graph Checks (Configuration-Based)
76-
Generic graph checks are defined in the metamodel.yaml under `graph_checks`.
93+
Generic graph checks are defined in the metamodel.yaml under `graph_checks`.
7794
These checks all follow the same structure:
7895

7996
```yaml
8097
<name of the check>:
8198
needs:
82-
include: <need1>, <need2> #list of your needs
99+
include: <need1>, <need2> #list of your needs
83100
condition: <your condition(s) that need to be fulfilled>
84101
check:
85102
<link attribute to check>: <condition to be checked in each need inside the link attribute>
86-
explanation: <A short sentence that explains what is required to be adhered to. This will be
103+
explanation: <A short sentence that explains what is required to be adhered to. This will be
87104
< part of the error message if the check fails>
88105
```
89106

90107
> *Note:* You can also use multiple conditions or negate conditions in either the needs or check part.
91108

92-
A complete example might look like so:
109+
A complete example might look like so:
93110

94111
```yaml
95112
graph_checks:
@@ -101,14 +118,14 @@ graph_checks:
101118
- safety != QM
102119
- status == valid
103120
check:
104-
implements:
121+
implements:
105122
and:
106123
- safety != QM
107124
- status == valid
108125
explanation: An safety architecture element can only link other safety architecture elements.
109126
```
110127

111-
What does this check do?
128+
What does this check do?
112129
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.
113130
If one of them does not fulfill the condition the check fails and will let you know with a warning that it did so.
114131

@@ -126,7 +143,7 @@ prohibited_words_checks:
126143
- < word to forbid >
127144
```
128145

129-
An example might look like this:
146+
An example might look like this:
130147
```yaml
131148
prohibited_words_checks:
132149
content_check:
@@ -201,7 +218,7 @@ score_metamodel/
201218
├── __init__.py
202219
├── rst
203220
│ ├── attributes
204-
│ │ └── ...
221+
│ │ └── ...
205222
│ ├── conf.py
206223
│ ├── graph
207224
│ │ └── test_metamodel_graph.rst

src/extensions/score_metamodel/checks/check_options.py

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -46,28 +46,18 @@ def _validate_value_pattern(
4646
pattern: str,
4747
need: NeedsInfoType,
4848
field: str,
49-
log: CheckLogger,
50-
as_info: bool = False,
51-
) -> None:
49+
):
5250
"""Check if a value matches the given pattern and log the result.
5351
54-
If ``as_info`` is True, mismatches are reported as info (non-failing)
55-
messages, otherwise as warnings.
52+
Returns true if the value matches the pattern, False otherwise.
5653
"""
5754
try:
58-
if not re.match(pattern, value):
59-
log.warning_for_option(
60-
need,
61-
field,
62-
f"does not follow pattern `{pattern}`.",
63-
is_new_check=as_info,
64-
)
65-
except TypeError:
66-
log.warning_for_option(
67-
need,
68-
field,
69-
f"pattern `{pattern}` is not a valid regex pattern.",
70-
)
55+
return re.match(pattern, value) is not None
56+
except TypeError as e:
57+
raise TypeError(
58+
f"Error in metamodel.yaml at {need['type']}->{field}: "
59+
f"pattern `{pattern}` is not a valid regex pattern."
60+
) from e
7161

7262

7363
def validate_fields(
@@ -102,23 +92,21 @@ def remove_prefix(word: str, prefixes: list[str]) -> str:
10292
log.warning_for_need(
10393
need, f"is missing required {field_type}: `{field}`."
10494
)
105-
continue # Skip empty optional fields
106-
# Try except used to add more context to Error without passing variables
107-
# just for that to function
108-
try:
109-
values = _normalize_values(raw_value)
110-
except ValueError as err:
111-
raise ValueError(
112-
f"An Attribute inside need {need['id']} is "
113-
"not of type str. Only Strings are allowed"
114-
) from err
115-
# The filter ensures that the function is only called when needed.
95+
continue # Nothing to validate if not present
96+
97+
values = _normalize_values(raw_value)
98+
11699
for value in values:
117100
if allowed_prefixes:
118101
value = remove_prefix(value, allowed_prefixes)
119-
_validate_value_pattern(
120-
value, pattern, need, field, log, as_info=optional_link_as_info
121-
)
102+
if not _validate_value_pattern(value, pattern, need, field):
103+
msg = f"does not follow pattern `{pattern}`."
104+
log.warning_for_option(
105+
need,
106+
field,
107+
msg,
108+
is_new_check=optional_link_as_info,
109+
)
122110

123111

124112
# req-Id: tool_req__docs_req_attr_reqtype
@@ -137,19 +125,17 @@ def check_options(
137125
Checks that required and optional options and links are present
138126
and follow their defined patterns.
139127
"""
140-
production_needs_types = app.config.needs_types
141-
142-
need_options = get_need_type(production_needs_types, need["type"])
128+
need_type = get_need_type(app.config.needs_types, need["type"])
143129

144130
# If undefined this is an empty list
145131
allowed_prefixes = app.config.allowed_external_prefixes
146132

147133
# Validate Options and Links
148134
field_validations = [
149-
("option", need_options["mandatory_options"], True),
150-
("option", need_options["optional_options"], False),
151-
("link", need_options["mandatory_links"], True),
152-
("link", need_options["optional_links"], False),
135+
("option", need_type["mandatory_options"], True),
136+
("option", need_type["optional_options"], False),
137+
("link", need_type["mandatory_links"], True),
138+
("link", need_type["optional_links"], False),
153139
]
154140

155141
for field_type, field_values, is_required in field_validations:

0 commit comments

Comments
 (0)