Skip to content

Commit 4e1def3

Browse files
authored
align api to security spec & automated ci/cd test for json schema, bnf (#642)
* align api to security spec * add ci workflow * fix testcases
1 parent bf6de14 commit 4e1def3

12 files changed

Lines changed: 845 additions & 63 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Validate spec artifacts
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
validate:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Fetch sources
16+
uses: actions/checkout@v4
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: "3.12"
22+
23+
- name: Install validation dependencies
24+
run: python -m pip install "jsonschema[format]==4.25.1"
25+
26+
- name: Validate JSON schemas, examples, and BNF artifacts
27+
run: python tools/validate_spec_artifacts.py
28+
29+
- name: Run query schema and regex tests
30+
run: python -m unittest discover -s documentation/IDTA-01002-3/modules/ROOT/pages/http-rest-api/test/query

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__/
2+
*.pyc

Part2-API-Schemas/openapi.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -712,11 +712,11 @@ components:
712712
FieldIdentifier:
713713
type: string
714714
pattern: >-
715-
^(?:\$aas#(?:idShort|id|assetInformation\.assetKind|assetInformation\.assetType|assetInformation\.globalAssetId|assetInformation\.specificAssetIds\[(?:0|[1-9][0-9]*)\]\.(?:name|value|externalSubjectId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?)|submodels\[(?:0|[1-9][0-9]*)\]\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))|\$sm#(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|idShort|id)|\$sme(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[(?:0|[1-9][0-9]*)\])*(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[(?:0|[1-9][0-9]*)\])*)*)?#(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|idShort|value|valueType|language)|\$cd#(?:idShort|id)|\$aasdesc#(?:idShort|id|assetKind|assetType|globalAssetId|specificAssetIds\[(?:0|[1-9][0-9]*)\]\.(?:name|value|externalSubjectId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?)|endpoints\[(?:0|[1-9][0-9]*)\]\.(?:interface|protocolinformation\.href)|submodelDescriptors\[(?:0|[1-9][0-9]*)\]\.(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|idShort|id|endpoints\[(?:0|[1-9][0-9]*)\]\.(?:interface|protocolinformation\.href)))|\$smdesc#(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)\]\.(?:type|value)))?|idShort|id|endpoints\[(?:0|[1-9][0-9]*)\]\.(?:interface|protocolinformation\.href)))$
715+
^(?:\$aas#(?:idShort|id|assetInformation\.assetKind|assetInformation\.assetType|assetInformation\.globalAssetId|assetInformation\.specificAssetIds\[(?:0|[1-9][0-9]*)?\]\.(?:name|value|externalSubjectId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?)|submodels\[(?:0|[1-9][0-9]*)?\](?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?)|\$sm#(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|idShort|id)|\$sme(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[(?:0|[1-9][0-9]*)?\])*(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[(?:0|[1-9][0-9]*)?\])*)*)?#(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|idShort|value|valueType|language)|\$cd#(?:idShort|id)|\$aasdesc#(?:idShort|id|assetKind|assetType|globalAssetId|specificAssetIds\[(?:0|[1-9][0-9]*)?\]\.(?:name|value|externalSubjectId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?)|endpoints\[(?:0|[1-9][0-9]*)?\]\.(?:interface|protocolinformation\.href)|submodelDescriptors\[(?:0|[1-9][0-9]*)?\]\.(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|idShort|id|endpoints\[(?:0|[1-9][0-9]*)?\]\.(?:interface|protocolinformation\.href)))|\$smdesc#(?:semanticId(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\])?(?:\.(?:type|keys\[(?:0|[1-9][0-9]*)?\]\.(?:type|value)))?|idShort|id|endpoints\[(?:0|[1-9][0-9]*)?\]\.(?:interface|protocolinformation\.href)))$
716716
FragmentFieldIdentifier:
717717
type: string
718718
pattern: >-
719-
^(?:\$aas#(?:idShort|assetInformation\.assetType|assetInformation\.globalAssetId|assetInformation\.specificAssetIds(?:\[[0-9]*\](?:\.externalSubjectId(?:\.keys(?:\[[0-9]*\])?)?)?)?|submodels(?:\[[0-9]*\](?:\.keys(?:\[[0-9]*\])?)?)?)|\$sm#(?:semanticId(?:\.keys(?:\[[0-9]*\])?)?|supplementalSemanticIds(?:\[[0-9]*\](?:\.keys(?:\[[0-9]*\])?)?)?|idShort)|\$sme(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[[0-9]*\])*(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[[0-9]*\])*)*)?(?:#(?:semanticId(?:\.keys(?:\[[0-9]*\])?)?|supplementalSemanticIds(?:\[[0-9]*\](?:\.keys(?:\[[0-9]*\])?)?)?|idShort|value|valueType|language))?|\$cd#idShort|\$aasdesc#(?:idShort|description|displayName|extension|administration|assetKind|assetType|globalAssetId|specificAssetIds(?:\[[0-9]*\](?:\.externalSubjectId(?:\.keys(?:\[[0-9]*\])?)?)?)?|endpoints(?:\[[0-9]*\])?|submodelDescriptors(?:\[[0-9]*\](?:\.(?:semanticId(?:\.keys(?:\[[0-9]*\])?)?|supplementalSemanticIds(?:\[[0-9]*\](?:\.keys(?:\[[0-9]*\])?)?)?|idShort|endpoints(?:\[[0-9]*\])?))?)?)|\$smdesc#(?:semanticId(?:\.keys(?:\[[0-9]*\])?)?|supplementalSemanticIds(?:\[[0-9]*\](?:\.keys(?:\[[0-9]*\])?)?)?|idShort|endpoints(?:\[[0-9]*\])?))$
719+
^(?:\$aas#(?:idShort|assetInformation\.assetType|assetInformation\.globalAssetId|assetInformation\.specificAssetIds(?:\[(?:0|[1-9][0-9]*)?\](?:\.externalSubjectId(?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?)?)?|submodels(?:\[(?:0|[1-9][0-9]*)?\](?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?)?)|\$sm#(?:semanticId(?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\](?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?)?|idShort)|\$sme(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[(?:0|[1-9][0-9]*)?\])*(?:\.[A-Za-z](?:[A-Za-z0-9_-]*[A-Za-z0-9_])?(?:\[(?:0|[1-9][0-9]*)?\])*)*)?(?:#(?:semanticId(?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\](?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?)?|idShort|value|valueType|language))?|\$cd#idShort|\$aasdesc#(?:idShort|description|displayName|extension|administration|assetKind|assetType|globalAssetId|specificAssetIds(?:\[(?:0|[1-9][0-9]*)?\](?:\.externalSubjectId(?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?)?)?|endpoints(?:\[(?:0|[1-9][0-9]*)?\])?|submodelDescriptors(?:\[(?:0|[1-9][0-9]*)?\](?:\.(?:semanticId(?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\](?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?)?|idShort|endpoints(?:\[(?:0|[1-9][0-9]*)?\])?))?)?)|\$smdesc#(?:semanticId(?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?|supplementalSemanticIds(?:\[(?:0|[1-9][0-9]*)?\](?:\.keys(?:\[(?:0|[1-9][0-9]*)?\])?)?)?|idShort|endpoints(?:\[(?:0|[1-9][0-9]*)?\])?))$
720720
MultiLanguagePropertyMetadata:
721721
allOf:
722722
- $ref: "#/components/schemas/SubmodelElementAttributes"

documentation/IDTA-01002-3/modules/ROOT/pages/changelog.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Minor Changes:
3737
* docs: Added cross-spec terminology matrix reference to align Metamodel classes, API path segments, and Query/Access-Rule prefixes across specifications.
3838
* docs: Added cross-specification alignment matrix to document API compatibility with IDTA-01001 (Metamodel v3.2 instead of v3.1), DTA-01003-a, IDTA-01003-b, IDTA-01004 (Security v3.1), and IDTA-01005 (from v3.1 to v3.2).
3939
* docs: Established IDTA-01002 as authoritative source for formula grammar and JSON Schema shared between API and Security specifications.
40+
* fix: Aligned Query Language BNF and JSON Schema with IDTA-01004 Security access-rule schema, including wildcard array indexes, stricter object/reference identifiers, and artifact validation tests.
4041
* docs: Added informative Conformance Test Corpus annex providing technology-neutral test cases for Query Language, HTTP/REST API, and error handling.
4142
* Added the `ServerNotImplemented` status code to the xref:specification/interfaces-payload.adoc#table-status-codes[Status Codes]. Note that the HTTP API mentioned this status code already, therefore, no change in OpenAPI or other related files. (https://github.com/admin-shell-io/aas-specs-api/issues/499[#499])
4243
* Removed the `GetAllAssetAdministrationShellDescriptorsByAssetType` operation from the OpenAPI files for the Asset Administration Shell Registry Service Specification, as it was not intended to be included in the first place and is not implemented by any known implementation. (https://github.com/admin-shell-io/aas-specs-api/issues/515[#515])
@@ -772,4 +773,3 @@ PostConceptDescription
772773
|PutConceptDescription |PutConceptDescriptionById |Changed |Naming pattern byId
773774
| |PostConceptDescription |New |
774775
|===
775-

documentation/IDTA-01002-3/modules/ROOT/pages/http-rest-api/test/query/test_field_identifier_regex.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def test_allowed_field_identifiers(self):
6060
"$aas#assetInformation.specificAssetIds[].externalSubjectId.type",
6161
"$aas#assetInformation.specificAssetIds[].externalSubjectId.keys[].type",
6262
"$aas#assetInformation.specificAssetIds[].externalSubjectId.keys[0].value",
63+
"$aas#submodels[]",
6364
"$aas#submodels[].type",
6465
"$aas#submodels[].keys[].type",
6566
"$aas#submodels[0].keys[0].value",
@@ -141,14 +142,16 @@ def test_not_allowed_field_identifiers(self):
141142
not_allowed = [
142143
"$aas#assetInformation.specificAssetIds",
143144
"$aas#assetInformation.specificAssetIds[]",
145+
"$aas#assetInformation.specificAssetIds[01].name",
144146
"$aas#assetInformation.specificAssetIds[].externalSubjectId.keys",
145147
"$aas#assetInformation.specificAssetIds[].externalSubjectId.keys[]",
146148
"$aas#submodels",
147-
"$aas#submodels[]",
149+
"$aas#submodels[01].type",
148150
"$aas#submodels[].keys",
149151
"$aas#submodels[].keys[]",
150152
"$sm#semanticId.keys",
151153
"$sm#semanticId.keys[]",
154+
"$sm#supplementalSemanticIds[01]",
152155
"$sm#supplementalSemanticIds.keys",
153156
"$sm#supplementalSemanticIds.keys[]",
154157
"$sm#supplementalSemanticIds[].keys",
@@ -158,6 +161,7 @@ def test_not_allowed_field_identifiers(self):
158161
"$sme.AddressInformation[]",
159162
"$sme.AddressInformation#id",
160163
"$sme.AddressInformation#bogus",
164+
"$sme.AddressInformation#supplementalSemanticIds[01]",
161165
"$sme.AddressInformation#supplementalSemanticIds.keys",
162166
"$sme.AddressInformation#supplementalSemanticIds[].keys",
163167
"$sme.1Invalid#value",
@@ -173,19 +177,23 @@ def test_not_allowed_field_identifiers(self):
173177
"$aasdesc#specificAssetIds[].externalSubjectId.keys[]",
174178
"$aasdesc#endpoints",
175179
"$aasdesc#endpoints[]",
180+
"$aasdesc#endpoints[01].interface",
176181
"$aasdesc#endpoints[].protocolinformation",
177182
"$aasdesc#submodelDescriptors",
178183
"$aasdesc#submodelDescriptors[]",
184+
"$aasdesc#submodelDescriptors[01].idShort",
179185
"$aasdesc#submodelDescriptors[].supplementalSemanticIds.keys",
180186
"$aasdesc#submodelDescriptors[].supplementalSemanticIds[].keys",
181187
"$aasdesc#submodelDescriptors[].endpoints",
182188
"$aasdesc#submodelDescriptors[].endpoints[]",
183189
"$smdesc#semanticId.keys",
184190
"$smdesc#semanticId.keys[]",
185191
"$smdesc#supplementalSemanticIds.keys",
192+
"$smdesc#supplementalSemanticIds[01]",
186193
"$smdesc#supplementalSemanticIds[].keys",
187194
"$smdesc#endpoints",
188195
"$smdesc#endpoints[]",
196+
"$smdesc#endpoints[01].interface",
189197
"$smdesc#endpoints[].protocolinformation",
190198
]
191199

@@ -198,5 +206,60 @@ def test_documented_field_identifier_patterns_are_in_sync(self):
198206
self.assertEqual(self.pattern, openapi_component_pattern("FieldIdentifier"))
199207

200208

209+
class ReferenceIdentifierRegexTest(unittest.TestCase):
210+
@classmethod
211+
def setUpClass(cls):
212+
schema = json.loads(QUERY_SCHEMA.read_text(encoding="utf-8-sig"))
213+
pattern = schema["definitions"]["ReferenceIdentifier"]["pattern"]
214+
cls.pattern = pattern
215+
cls.reference_identifier = re.compile(pattern)
216+
217+
def assert_allowed(self, value):
218+
self.assertIsNotNone(
219+
self.reference_identifier.fullmatch(value),
220+
f"Expected ReferenceIdentifier to allow: {value}",
221+
)
222+
223+
def assert_not_allowed(self, value):
224+
self.assertIsNone(
225+
self.reference_identifier.fullmatch(value),
226+
f"Expected ReferenceIdentifier to reject: {value}",
227+
)
228+
229+
def test_allowed_reference_identifiers(self):
230+
allowed = [
231+
'$aas("aas-id")#assetInformation.specificAssetIds[].externalSubjectId.keys[0].value',
232+
'$sm("SubmodelID")#id',
233+
'$sm("SubmodelID")#supplementalSemanticIds[].keys[].value',
234+
'$cd("ConceptDescriptionID")#idShort',
235+
'$sme("SubmodelID-OperationalData").machineState#value',
236+
'$sme("SubmodelID").AddressInformation[0].Zipcode#semanticId.keys[].value',
237+
]
238+
239+
for value in allowed:
240+
with self.subTest(value=value):
241+
self.assert_allowed(value)
242+
243+
def test_not_allowed_reference_identifiers(self):
244+
not_allowed = [
245+
"$sm#id",
246+
'$sm("SubmodelID")#bogus',
247+
'$sm("SubmodelID")#supplementalSemanticIds[01]',
248+
'$aas("aas-id")#assetInformation.specificAssetIds[]',
249+
'$cd("ConceptDescriptionID")#description',
250+
'$sme("SubmodelID").machineState',
251+
'$sme("SubmodelID").machineState#id',
252+
'$sme("SubmodelID").1Invalid#value',
253+
'$aasdesc("aas-id")#idShort',
254+
]
255+
256+
for value in not_allowed:
257+
with self.subTest(value=value):
258+
self.assert_not_allowed(value)
259+
260+
def test_documented_reference_identifier_patterns_are_in_sync(self):
261+
self.assertEqual(self.pattern, schema_page_pattern("ReferenceIdentifier"))
262+
263+
201264
if __name__ == "__main__":
202265
unittest.main()

documentation/IDTA-01002-3/modules/ROOT/pages/http-rest-api/test/query/test_fragment_field_identifier_regex.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,43 +140,54 @@ def test_not_allowed_fragment_field_identifiers(self):
140140
"$aas#id",
141141
"$aas#assetInformation.assetKind",
142142
"$aas#assetInformation.specificAssetIds.name",
143+
"$aas#assetInformation.specificAssetIds[01]",
143144
"$aas#assetInformation.specificAssetIds[].name",
144145
"$aas#assetInformation.specificAssetIds[].value",
145146
"$aas#assetInformation.specificAssetIds.keys[]",
146147
"$aas#assetInformation.specificAssetIds.externalSubjectId.keys[]",
148+
"$aas#submodels[01]",
147149
"$aas#submodels.keys[]",
148150
"$aas#submodels[].type",
149151
"$aas#submodels[].keys[].value",
150152
"$sm#semanticId.type",
151153
"$sm#semanticId.keys[].value",
152154
"$sm#id",
155+
"$sm#supplementalSemanticIds[01]",
153156
"$sm#supplementalSemanticIds.type",
154157
"$sm#supplementalSemanticIds.keys",
155158
"$sm#supplementalSemanticIds[].type",
156159
"$sm#supplementalSemanticIds[].keys[].value",
157160
"$sme.1Invalid",
161+
"$sme.AddressInformation[01]",
158162
"$sme.AddressInformation#id",
159163
"$sme.AddressInformation#bogus",
160164
"$sme.AddressInformation#semanticId.type",
161165
"$sme.AddressInformation#semanticId.keys[].value",
166+
"$sme.AddressInformation#supplementalSemanticIds[01]",
162167
"$sme.AddressInformation#supplementalSemanticIds.type",
163168
"$sme.AddressInformation#supplementalSemanticIds.keys",
164169
"$sme.AddressInformation#supplementalSemanticIds[].keys[].value",
165170
"$cd#id",
166171
"$aasdesc#id",
167172
"$aasdesc#specificAssetIds.name",
173+
"$aasdesc#specificAssetIds[01]",
168174
"$aasdesc#specificAssetIds[].name",
175+
"$aasdesc#endpoints[01]",
169176
"$aasdesc#endpoints.protocolinformation.href",
177+
"$aasdesc#submodelDescriptors[01]",
170178
"$aasdesc#submodelDescriptors.endpoints[]",
171179
"$aasdesc#submodelDescriptors[].id",
180+
"$aasdesc#submodelDescriptors[].supplementalSemanticIds[01]",
172181
"$aasdesc#submodelDescriptors[].supplementalSemanticIds.type",
173182
"$aasdesc#submodelDescriptors[].supplementalSemanticIds.keys",
174183
"$aasdesc#submodelDescriptors[].supplementalSemanticIds[].keys[].value",
175184
"$aasdesc#submodelDescriptors[].endpoints.protocolinformation.href",
176185
"$smdesc#id",
186+
"$smdesc#supplementalSemanticIds[01]",
177187
"$smdesc#supplementalSemanticIds.type",
178188
"$smdesc#supplementalSemanticIds.keys",
179189
"$smdesc#supplementalSemanticIds[].keys[].value",
190+
"$smdesc#endpoints[01]",
180191
"$smdesc#endpoints.protocolinformation.href",
181192
]
182193

0 commit comments

Comments
 (0)