From a5ff8c59c71cb35e68503610577a7e8ca8c7d3e8 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Sun, 25 Aug 2024 19:20:16 +0200 Subject: [PATCH 01/17] post-processing via LinkML schemaView --- resources/schemas/thing_description.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/resources/schemas/thing_description.yaml b/resources/schemas/thing_description.yaml index 59b82dc..bc44ee4 100644 --- a/resources/schemas/thing_description.yaml +++ b/resources/schemas/thing_description.yaml @@ -32,9 +32,6 @@ prefixes: rdf: prefix_prefix: rdf prefix_reference: http://www.w3.org/1999/02/22-rdf-syntax-ns# - dcterms: - prefix_prefix: dcterms - prefix_reference: http://purl.org/dc/terms/ schema: prefix_prefix: schema prefix_reference: http://schema.org/ @@ -144,7 +141,7 @@ classes: multivalued: true none_of: - equals_string: 'tm:ThingModel' - + # start of main classes, required for RDF generation VersionInfo: @@ -466,4 +463,4 @@ enums: meaning: td:readProperty it: description: Italian - meaning: td:readProperty + meaning: td:readProperty \ No newline at end of file From 393bb9313bf50c50598e5d781b5658c1f0d59298 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Sun, 25 Aug 2024 19:22:40 +0200 Subject: [PATCH 02/17] postprocessing with LinkML SchemaView --- main.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index d624116..13421b1 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import argparse +import json import subprocess from linkml.generators.jsonschemagen import JsonSchemaGenerator @@ -7,7 +8,9 @@ from linkml.generators.jsonldcontextgen import ContextGenerator from linkml.generators.docgen import DocGenerator from linkml.generators.linkmlgen import LinkmlGenerator +from linkml_runtime.utils.schemaview import SchemaView from pathlib import Path +from pyld import jsonld RESOURCES_PATH = Path('resources') GENS_PATH = RESOURCES_PATH / 'gens' @@ -25,28 +28,44 @@ def generate_docs(): doc_generator = DocGenerator(YAML_SCHEMA_PATH, mergeimports=False) doc_generator.serialize(directory=str(DOCDIR)) + +def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) -> str: + serialized_schema_json = json.loads(serialized_schema) + for slot in schema_view.all_slots().values(): + if slot.slot_uri is not None and 'InLanguage' in slot.slot_uri and isinstance(serialized_schema_json['@context'][slot.name], dict): + serialized_schema_json['@context'][slot.name]['@container'] = '@language' + if slot.multivalued and str(slot.range) == 'Any' and isinstance(serialized_schema_json['@context'][slot.name], dict): + if '@type' in serialized_schema_json['@context'][slot.name].keys(): + del serialized_schema_json['@context'][slot.name]['@type'] + serialized_schema_json['@context'][slot.name]['@container'] = '@set' + return json.dumps(serialized_schema_json, indent=3) + + def main(generate_docs_flag, serve_docs_flag): if not YAML_SCHEMA_PATH.exists(): print(f"LinkML schema file does not exist: {YAML_SCHEMA_PATH}") return + linkml_schema_view = SchemaView(YAML_SCHEMA_PATH, merge_imports=True) + # TODO: add pre processing for LinkML if needed for generator in GENERATORS: output_dir = GENS_PATH / generator output_dir.mkdir(parents=True, exist_ok=True) if generator == 'jsonschema': # json_schema_generator = JsonSchemaGenerator(yaml_content, top_class="Thing") - json_schema_generator = JsonSchemaGenerator(YAML_SCHEMA_PATH, mergeimports=True) + json_schema_generator = JsonSchemaGenerator(linkml_schema_view.schema, mergeimports=True) (output_dir / 'jsonschema.json').write_text(json_schema_generator.serialize()) elif generator == 'shacl': - shacl_generator = ShaclGenerator(YAML_SCHEMA_PATH, mergeimports=False, closed=True, suffix='Shape') + shacl_generator = ShaclGenerator(linkml_schema_view.schema, mergeimports=False, closed=True, suffix='Shape') (output_dir / 'shapes.shacl.ttl').write_text(shacl_generator.serialize()) elif generator == 'owl': - owl_generator = OwlSchemaGenerator(YAML_SCHEMA_PATH,) + owl_generator = OwlSchemaGenerator(linkml_schema_view.schema,) (output_dir / 'ontology.owl.ttl').write_text(owl_generator.serialize()) elif generator == 'jsonldcontext': - context_generator = ContextGenerator(YAML_SCHEMA_PATH, mergeimports=False) - (output_dir / 'context.jsonld').write_text(context_generator.serialize()) + context_generator = ContextGenerator(linkml_schema_view.schema, mergeimports=True) + (output_dir / 'context.jsonld').write_text(post_process_jsonldcontext(linkml_schema_view, + context_generator.serialize())) elif generator == 'linkml': - linkml_generator = LinkmlGenerator(YAML_SCHEMA_PATH, mergeimports=True, format='yaml', output='linkml.yaml') + linkml_generator = LinkmlGenerator(linkml_schema_view.schema, mergeimports=True, format='yaml', output='linkml.yaml') (output_dir / 'linkml.yaml').write_text(linkml_generator.serialize()) else: print(f"Unknown generator: {generator}") @@ -69,3 +88,4 @@ def main(generate_docs_flag, serve_docs_flag): help='Boolean for serving the generated documentation.') args = parser.parse_args() main(args.local_docs, args.serve_docs) + From f6b7977058cdf1e2605388db04d146274b16e9d4 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Sun, 25 Aug 2024 20:33:36 +0200 Subject: [PATCH 03/17] rdf langString flag defined as type --- resources/schemas/thing_description.yaml | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/resources/schemas/thing_description.yaml b/resources/schemas/thing_description.yaml index bc44ee4..afc5207 100644 --- a/resources/schemas/thing_description.yaml +++ b/resources/schemas/thing_description.yaml @@ -57,16 +57,21 @@ imports: - jsonschema types: - MultiLanguage: - name: MultiLanguage - description: Description with a language tag. - from_schema: rdf - exact_mappings: - - rdf:langString - base: rdf:PlainLiteral - uri: rdf:PlainLiteral - structured_pattern: - syntax: "{text}@{tag}" + langString: + name: langString + description: A boolean flag indicating that the string could also include language tag. + base: Bool + uri: xsd:boolean +# MultiLanguage: +# name: MultiLanguage +# description: Description with a language tag. +# from_schema: rdf +# exact_mappings: +# - rdf:langString +# base: rdf:PlainLiteral +# uri: rdf:PlainLiteral +# structured_pattern: +# syntax: "{text}@{tag}" slots: id: @@ -84,9 +89,7 @@ slots: Provides multi-language human-readable titles (e.g., display a text for UI representation in different languages). slot_uri: td:titleInLanguage multivalued: true - exactly_one_of: - - range: string - - range: MultiLanguage + range: langString inlined_as_list: true description: description: >- @@ -97,6 +100,7 @@ slots: slot_uri: td:descriptionInLanguage multivalued: true inlined: true + range: langString # currently less restrictions are applied, with further restrictions according to the json schema, the following can be partially utilized # exactly_one_of: # - range: type_declaration_string From 78d3b27ce7af052910ab6693ec76471bd6d2d38e Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Sun, 25 Aug 2024 20:34:10 +0200 Subject: [PATCH 04/17] langString postprocessing updated according to the langString flag type --- main.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 13421b1..56b1b89 100644 --- a/main.py +++ b/main.py @@ -9,6 +9,7 @@ from linkml.generators.docgen import DocGenerator from linkml.generators.linkmlgen import LinkmlGenerator from linkml_runtime.utils.schemaview import SchemaView +from linkml_runtime.linkml_model.meta import AnonymousSlotExpression from pathlib import Path from pyld import jsonld @@ -32,8 +33,25 @@ def generate_docs(): def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) -> str: serialized_schema_json = json.loads(serialized_schema) for slot in schema_view.all_slots().values(): - if slot.slot_uri is not None and 'InLanguage' in slot.slot_uri and isinstance(serialized_schema_json['@context'][slot.name], dict): + # Update JSON-LD context as a workaround for no langString support in LinkML + is_langString = slot.range == 'langString' + is_exactly_one_langString = ( + slot.range is None and + any( + expr.range == 'langString' + for expr in slot.exactly_one_of if isinstance(expr, AnonymousSlotExpression) + ) + ) + if (is_langString or is_exactly_one_langString) and isinstance(serialized_schema_json['@context'][slot.name], + dict): serialized_schema_json['@context'][slot.name]['@container'] = '@language' + if '@type' in serialized_schema_json['@context'][slot.name]: + del serialized_schema_json['@context'][slot.name]['@type'] + # if slot.slot_uri is not None and 'InLanguage' in slot.slot_uri and isinstance(serialized_schema_json['@context'][slot.name], dict): + serialized_schema_json['@context'][slot.name]['@container'] = '@language' + if '@type' in serialized_schema_json['@context'][slot.name]: + del serialized_schema_json['@context'][slot.name]['@type'] + if slot.multivalued and str(slot.range) == 'Any' and isinstance(serialized_schema_json['@context'][slot.name], dict): if '@type' in serialized_schema_json['@context'][slot.name].keys(): del serialized_schema_json['@context'][slot.name]['@type'] From b865480ef27d871df862e543ede73cc0ccca732f Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Sun, 25 Aug 2024 21:17:29 +0200 Subject: [PATCH 05/17] removed uriorcurie type --- resources/schemas/hctl.yaml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/resources/schemas/hctl.yaml b/resources/schemas/hctl.yaml index 54ecf1f..23c5a3e 100644 --- a/resources/schemas/hctl.yaml +++ b/resources/schemas/hctl.yaml @@ -51,15 +51,10 @@ classes: anchor: description: >- By default, the context, or anchor, of a link conveyed in the Link header field is the URL of the representation it is associated with, as defined in RFC7231, Section 3.1.4.1, and is serialized as a URI. - range: uriorcurie + range: uri sizes: description: >- Target attribute that specifies one or more sizes for the referenced icon. Only applicable for relation type 'icon'. The value pattern follows {Height}x{Width} (e.g., \"16x16\", \"16x16 32x32\"). - hreflang: - description: >- - The hreflang attribute specifies the language of a linked document. The value of this must be a valid language tag [[BCP47]]. - multivalued: true - pattern: "^(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)|((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$" slots: - href Form: @@ -177,7 +172,7 @@ slots: aliases: - target required: true - range: uriorcurie + range: uri enums: SubProtocolTypes: From 276ca41e59b4245724581565cf7d2f5f781dfe28 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Sun, 25 Aug 2024 21:34:30 +0200 Subject: [PATCH 06/17] minor changes --- resources/schemas/jsonschema.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/schemas/jsonschema.yaml b/resources/schemas/jsonschema.yaml index a5cb6a7..c7b411b 100644 --- a/resources/schemas/jsonschema.yaml +++ b/resources/schemas/jsonschema.yaml @@ -97,17 +97,19 @@ classes: Used to ensure that the data is valid against one of the specified schemas in the array. This can be used to describe multiple input or output schemas. slot_uri: jsonschema:oneOf + multivalued: true allOf: description: >- Used to ensure that the data is valid against all of the specified schemas in the array. multivalued: true slot_uri: jsonschema:allOf - range: DataSchema +# range: DataSchema anyOf: description: >- Used to ensure that the data is valid against any of the specified schemas in the array. slot_uri: jsonschema:anyOf range: DataSchema + multivalued: true enum: description: >- Restricted set of values provided as an array. From b7b94d7346a76443cb42dd29f409b112c098862e Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 10:48:43 +0200 Subject: [PATCH 07/17] security redefined as a slot instead of attribute --- resources/schemas/thing_description.yaml | 27 ++++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/resources/schemas/thing_description.yaml b/resources/schemas/thing_description.yaml index afc5207..5c8e16a 100644 --- a/resources/schemas/thing_description.yaml +++ b/resources/schemas/thing_description.yaml @@ -90,7 +90,6 @@ slots: slot_uri: td:titleInLanguage multivalued: true range: langString - inlined_as_list: true description: description: >- Provides additional (human-readable) information based on a default language. @@ -99,7 +98,6 @@ slots: Can be used to support (human-readable) information in different languages. slot_uri: td:descriptionInLanguage multivalued: true - inlined: true range: langString # currently less restrictions are applied, with further restrictions according to the json schema, the following can be partially utilized # exactly_one_of: @@ -114,6 +112,15 @@ slots: # - range: string # multivalued: true # inlined: false + security: + description: >- + A security configuration is a a security scheme applied to a (set of) affordance(s). + slot_uri: td:hasSecurityConfiguration + required: true + exactly_one_of: + - range: string + multivalued: true + - range: string uriVariables: description: >- Define URI template variables according to RFC6570 as collection based on schema specifications. The individual @@ -291,7 +298,7 @@ classes: description: >- JSON-LD keyword to define short-hand names called terms that are used throughout a TD document. required: true - range: uriorcurie + range: uri array: version: description: >- @@ -324,18 +331,9 @@ classes: slot_uri: td:followsProfile range: Any any_of: - - range: uriorcurie - multivalued: true - - range: uriorcurie - security: - description: >- - A security configuration is a a security scheme applied to a (set of) affordance(s). - slot_uri: td:hasSecurityConfiguration - required: true - exactly_one_of: - - range: string + - range: uri multivalued: true - - range: string + - range: uri securityDefinitions: description: >- A Thing may define abstract security schemes, used to configure the secure access of (a set of) affordance(s). @@ -389,6 +387,7 @@ classes: - descriptions - uriVariables - forms + - security enums: OperationTypes: From 5ad0980fad31663d1f506c0a5e04a459a72e9496 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 10:49:05 +0200 Subject: [PATCH 08/17] reused security slot for td schema --- resources/schemas/hctl.yaml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/resources/schemas/hctl.yaml b/resources/schemas/hctl.yaml index 23c5a3e..698a558 100644 --- a/resources/schemas/hctl.yaml +++ b/resources/schemas/hctl.yaml @@ -76,14 +76,6 @@ classes: Content codings are primarily used to allow a representation to be compressed or otherwise usefully transformed without losing the identity of its underlying media type and without loss of information. Examples of content coding include \"gzip\", \"deflate\", etc. - securityDefinitions: - description: >- - Set of security definition names, chosen from those defined in securityDefinitions. These must all be satisfied for access to resources. - slot_uri: td:hasSecurityConfiguration - exactly_one_of: - - range: string - - range: string - multivalued: true scopes: description: >- TODO Check, was not in hctl ontology, if not could be source of discrepancy. @@ -129,6 +121,7 @@ classes: multivalued: true slots: - href + - security ExpectedResponse: class_uri: hctl:ExpectedResponse description: >- From a5d38da3795afc2c7ce7febf291a5426b1e7bdec Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 10:49:45 +0200 Subject: [PATCH 09/17] inline slot changed --- resources/schemas/jsonschema.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/schemas/jsonschema.yaml b/resources/schemas/jsonschema.yaml index c7b411b..ca5d109 100644 --- a/resources/schemas/jsonschema.yaml +++ b/resources/schemas/jsonschema.yaml @@ -227,6 +227,7 @@ classes: slot_uri: jsonschema:properties range: DataSchema multivalued: true + inlined: true required: slot_uri: jsonschema:required description: >- From e2b5351253207bff6751041cf037b34a22d7f60a Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 10:50:20 +0200 Subject: [PATCH 10/17] @container=@index post processing, inline slots --- main.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 56b1b89..e0705af 100644 --- a/main.py +++ b/main.py @@ -31,6 +31,7 @@ def generate_docs(): def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) -> str: + #Addressing JSON-LD @Container keywords that support values: [@list, @set, @language, @index, @id, @type, Null] serialized_schema_json = json.loads(serialized_schema) for slot in schema_view.all_slots().values(): # Update JSON-LD context as a workaround for no langString support in LinkML @@ -48,10 +49,8 @@ def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) if '@type' in serialized_schema_json['@context'][slot.name]: del serialized_schema_json['@context'][slot.name]['@type'] # if slot.slot_uri is not None and 'InLanguage' in slot.slot_uri and isinstance(serialized_schema_json['@context'][slot.name], dict): - serialized_schema_json['@context'][slot.name]['@container'] = '@language' - if '@type' in serialized_schema_json['@context'][slot.name]: - del serialized_schema_json['@context'][slot.name]['@type'] - + if slot.inlined and isinstance(serialized_schema_json['@context'][slot.name], dict): + serialized_schema_json['@context'][slot.name]['@container'] = '@index' if slot.multivalued and str(slot.range) == 'Any' and isinstance(serialized_schema_json['@context'][slot.name], dict): if '@type' in serialized_schema_json['@context'][slot.name].keys(): del serialized_schema_json['@context'][slot.name]['@type'] From 0a50abd8fe9e5d64c120a1e55a4e74bcd9e7b778 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 15:16:47 +0200 Subject: [PATCH 11/17] linkml and linkml-runtime versions updated --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7deda29..c93ad48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ package-mode = false [tool.poetry.dependencies] python = "^3.11" -linkml-runtime = "^1.8.0rc2" +linkml-runtime = "^1.8.2" [tool.poetry-dynamic-versioning] enable = false @@ -18,7 +18,7 @@ vcs = "git" style = "pep440" [tool.poetry.dev-dependencies] -linkml = "^1.8.0rc2" +linkml = "^1.8.3" mkdocs-material = "^8.2.8" mkdocs-mermaid2-plugin = "^0.6.0" schemasheets = "^0.1.14" From 78dc919345272332cf5f216bd3267da432b265a3 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 15:17:44 +0200 Subject: [PATCH 12/17] instantiates keyword added for classification of @index keyword --- resources/schemas/hctl.yaml | 1 + resources/schemas/jsonschema.yaml | 4 ++ resources/schemas/thing_description.yaml | 82 ++++++++++++++---------- resources/schemas/wot_security.yaml | 1 + 4 files changed, 54 insertions(+), 34 deletions(-) diff --git a/resources/schemas/hctl.yaml b/resources/schemas/hctl.yaml index 698a558..7d38732 100644 --- a/resources/schemas/hctl.yaml +++ b/resources/schemas/hctl.yaml @@ -4,6 +4,7 @@ title: hctl version: "1.1-11-June-2024" description: |- LinkML schema for modelling the TD Hypermedia Control information model, in particular links and forms. +contributors: Mahda_Noura license: MIT see_also: - https://www.w3.org/TR/wot-thing-description11/ diff --git a/resources/schemas/jsonschema.yaml b/resources/schemas/jsonschema.yaml index ca5d109..6643722 100644 --- a/resources/schemas/jsonschema.yaml +++ b/resources/schemas/jsonschema.yaml @@ -4,6 +4,7 @@ title: jsonschema version: "1.1-05-July-2024" description: |- LinkML schema for modelling the TD Module for data schema specifications. +contributors: Mahda_Noura license: MIT see_also: - https://www.w3.org/TR/wot-thing-description11/ @@ -226,8 +227,11 @@ classes: Data schema nested definitions. slot_uri: jsonschema:properties range: DataSchema + instantiates: propertyName multivalued: true inlined: true + inlined_as_list: false + required: slot_uri: jsonschema:required description: >- diff --git a/resources/schemas/thing_description.yaml b/resources/schemas/thing_description.yaml index 5c8e16a..e7da120 100644 --- a/resources/schemas/thing_description.yaml +++ b/resources/schemas/thing_description.yaml @@ -7,6 +7,7 @@ description: |- according to the W3C WoT Group (https://github.com/w3c/wot). This schema is based on LinkML and is used to generate the required WoT resources e.g., JSON Schema, SHACL shapes, and RDF programmatically. license: MIT +contributors: Mahda_Noura see_also: - https://www.w3.org/TR/wot-thing-description11/ @@ -62,16 +63,7 @@ types: description: A boolean flag indicating that the string could also include language tag. base: Bool uri: xsd:boolean -# MultiLanguage: -# name: MultiLanguage -# description: Description with a language tag. -# from_schema: rdf -# exact_mappings: -# - rdf:langString -# base: rdf:PlainLiteral -# uri: rdf:PlainLiteral -# structured_pattern: -# syntax: "{text}@{tag}" + slots: id: @@ -82,7 +74,7 @@ slots: title: description: >- Provides a human-readable title (e.g., display a text for UI representation) based on a default language. - range: string + in_language: en slot_uri: td:title titles: description: >- @@ -93,6 +85,7 @@ slots: description: description: >- Provides additional (human-readable) information based on a default language. + in_language: en descriptions: description: >- Can be used to support (human-readable) information in different languages. @@ -128,7 +121,9 @@ slots: slot_uri: td:hasUriTemplateSchema range: DataSchema inlined: true + inlined_as_list: false multivalued: true + instantiates: name forms: slot_uri: td:hasForm description: >- @@ -169,28 +164,28 @@ classes: description: >- Provides a version indicator of the underlying TM. slot_uri: td:model - securityDefinitionsRange: - description: >- - TODO. - attributes: - name: - description: >- - Indexing property to store entity names when serializing them in a JSON-LD @index container. - slot_uri: wotsec:name - identifier: true - value: - inlined: true - range: Any - exactly_one_of: - - range: bearer - - range: oauth2 - - range: combo - - range: digest - - range: basic - - range: nosec - - range: auto - - range: psk - - range: apikey +# securityDefinitionsRange: +# description: >- +# TODO. +# attributes: +# name: +# description: >- +# Indexing property to store entity names when serializing them in a JSON-LD @index container. +# slot_uri: wotsec:name +# identifier: true +# value: +# inlined: true +# range: Any +# exactly_one_of: +# - range: bearer +# - range: oauth2 +# - range: combo +# - range: digest +# - range: basic +# - range: nosec +# - range: auto +# - range: psk +# - range: apikey InteractionAffordance: description: >- Metadata of a Thing that shows the possible choices to Consumers, thereby suggesting how Consumers may interact @@ -338,11 +333,22 @@ classes: description: >- A Thing may define abstract security schemes, used to configure the secure access of (a set of) affordance(s). slot_uri: td:definesSecurityScheme - range: securityDefinitionsRange + exactly_one_of: + - range: bearer + - range: oauth2 + - range: combo + - range: digest + - range: basic + - range: nosec + - range: auto + - range: psk + - range: apikey required: true minimum_cardinality: 1 inlined: true + inlined_as_list: false multivalued: true + instantiates: hasInstanceConfiguration schemaDefinitions: description: >- TODO This has to be checked, it is not in the ontologies anywhere. @@ -350,6 +356,8 @@ classes: slot_uri: td:schemaDefinitions multivalued: true range: DataSchema + inlined: true + inlined_as_list: false links: slot_uri: td:hasLink description: >- @@ -362,21 +370,27 @@ classes: slot_uri: td:hasPropertyAffordance multivalued: true inlined: true + inlined_as_list: false range: PropertyAffordance + instantiates: name actions: description: >- All Action-based interaction affordances of the Thing. slot_uri: td:hasActionAffordance multivalued: true inlined: true + inlined_as_list: false range: ActionAffordance + instantiates: name events: description: >- All Event-based interaction affordances of the Thing. slot_uri: td:hasEventAffordance multivalued: true inlined: true + inlined_as_list: false range: EventAffordance + instantiates: name slots: - id - title diff --git a/resources/schemas/wot_security.yaml b/resources/schemas/wot_security.yaml index 9d707b7..37130eb 100644 --- a/resources/schemas/wot_security.yaml +++ b/resources/schemas/wot_security.yaml @@ -4,6 +4,7 @@ title: wot_security version: "1.1-5-July-2024" description: |- LinkML schema for modelling the TD Security mechanisms. +contributors: Mahda_Noura license: MIT see_also: - https://www.w3.org/TR/wot-thing-description11/ From 9b70f555f71c14d931ea39afc5fd87a8289ba368 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 15:18:15 +0200 Subject: [PATCH 13/17] updated jsonld post-processing for @index --- main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index e0705af..eb750dd 100644 --- a/main.py +++ b/main.py @@ -30,8 +30,8 @@ def generate_docs(): doc_generator.serialize(directory=str(DOCDIR)) +#Addressing JSON-LD @Container keywords that support values: [@list, @set, @language, @index, @id, @type, Null] def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) -> str: - #Addressing JSON-LD @Container keywords that support values: [@list, @set, @language, @index, @id, @type, Null] serialized_schema_json = json.loads(serialized_schema) for slot in schema_view.all_slots().values(): # Update JSON-LD context as a workaround for no langString support in LinkML @@ -49,8 +49,10 @@ def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) if '@type' in serialized_schema_json['@context'][slot.name]: del serialized_schema_json['@context'][slot.name]['@type'] # if slot.slot_uri is not None and 'InLanguage' in slot.slot_uri and isinstance(serialized_schema_json['@context'][slot.name], dict): - if slot.inlined and isinstance(serialized_schema_json['@context'][slot.name], dict): + if slot.inlined and slot.multivalued and not slot.inlined_as_list and isinstance(serialized_schema_json['@context'][slot.name], dict): serialized_schema_json['@context'][slot.name]['@container'] = '@index' + if slot.instantiates: + serialized_schema_json['@context'][slot.name]['@index'] = slot.instantiates if slot.multivalued and str(slot.range) == 'Any' and isinstance(serialized_schema_json['@context'][slot.name], dict): if '@type' in serialized_schema_json['@context'][slot.name].keys(): del serialized_schema_json['@context'][slot.name]['@type'] From 4cb170b8318f3b465a363871ba8b6a549528dd38 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Mon, 26 Aug 2024 15:25:59 +0200 Subject: [PATCH 14/17] updated jsonld post-processing for @language: en with in_language tag in LinkML --- main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index eb750dd..995010e 100644 --- a/main.py +++ b/main.py @@ -48,7 +48,9 @@ def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) serialized_schema_json['@context'][slot.name]['@container'] = '@language' if '@type' in serialized_schema_json['@context'][slot.name]: del serialized_schema_json['@context'][slot.name]['@type'] - # if slot.slot_uri is not None and 'InLanguage' in slot.slot_uri and isinstance(serialized_schema_json['@context'][slot.name], dict): + if slot.in_language and isinstance(serialized_schema_json['@context'][slot.name], + dict): + serialized_schema_json['@context'][slot.name]['@language'] = slot.in_language if slot.inlined and slot.multivalued and not slot.inlined_as_list and isinstance(serialized_schema_json['@context'][slot.name], dict): serialized_schema_json['@context'][slot.name]['@container'] = '@index' if slot.instantiates: From 505dfd9c8fe4fb3ff47905f8d96b5c18ae853116 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Tue, 27 Aug 2024 14:06:49 +0200 Subject: [PATCH 15/17] minor changes --- resources/schemas/hctl.yaml | 2 +- resources/schemas/thing_description.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/schemas/hctl.yaml b/resources/schemas/hctl.yaml index 7d38732..7c16d9f 100644 --- a/resources/schemas/hctl.yaml +++ b/resources/schemas/hctl.yaml @@ -104,7 +104,7 @@ classes: multivalued: true range: AdditionalExpectedResponse aliases: - - additional returns + - additionalReturns subprotocol: slot_uri: hctl:forSubProtocol description: >- diff --git a/resources/schemas/thing_description.yaml b/resources/schemas/thing_description.yaml index e7da120..d5009a9 100644 --- a/resources/schemas/thing_description.yaml +++ b/resources/schemas/thing_description.yaml @@ -160,10 +160,12 @@ classes: Provides a version identicator of this TD instance. slot_uri: td:instance required: true + range: string model: description: >- Provides a version indicator of the underlying TM. slot_uri: td:model + range: string # securityDefinitionsRange: # description: >- # TODO. From a5ca4a3550c104d692a1d099d140e37b1984062d Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Tue, 27 Aug 2024 14:09:47 +0200 Subject: [PATCH 16/17] removed the apiKeyIn slot and used in because it was not found in other wot schemas --- resources/schemas/wot_security.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/schemas/wot_security.yaml b/resources/schemas/wot_security.yaml index 37130eb..4785b03 100644 --- a/resources/schemas/wot_security.yaml +++ b/resources/schemas/wot_security.yaml @@ -67,6 +67,7 @@ classes: range: uri scheme: required: true + #if this is extra in the json schema, you can remove the range, and use induced slot range: SecuritySchemeType hasInstanceConfiguration: slot_uri: td:hasInstanceConfiguration @@ -231,13 +232,11 @@ classes: In this case the key may not be using a standard token format. This scheme indicates that the key provided by the service provider needs to be supplied as part of service requests using the mechanism indicated by the \"in\" field. attributes: - apiKeyIn: - description: >- - Specifies the location of security authentication information. scheme: equals_string: apikey slots: - name + - in psk: is_a: SecurityScheme class_uri: wotsec:PSKSecurityScheme From ef09470a3725db3d12e0ce3d53e2427401a1bc89 Mon Sep 17 00:00:00 2001 From: mahdanoura Date: Tue, 27 Aug 2024 14:10:12 +0200 Subject: [PATCH 17/17] json-ld @container: @set added --- main.py | 59 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/main.py b/main.py index 995010e..49f462b 100644 --- a/main.py +++ b/main.py @@ -30,35 +30,40 @@ def generate_docs(): doc_generator.serialize(directory=str(DOCDIR)) -#Addressing JSON-LD @Container keywords that support values: [@list, @set, @language, @index, @id, @type, Null] -def post_process_jsonldcontext(schema_view: SchemaView, serialized_schema: str) -> str: +def post_process_jsonld_context(schema_view: SchemaView, serialized_schema: str) -> str: serialized_schema_json = json.loads(serialized_schema) + generated_context = serialized_schema_json.get('@context', {}) for slot in schema_view.all_slots().values(): - # Update JSON-LD context as a workaround for no langString support in LinkML - is_langString = slot.range == 'langString' - is_exactly_one_langString = ( - slot.range is None and - any( - expr.range == 'langString' - for expr in slot.exactly_one_of if isinstance(expr, AnonymousSlotExpression) - ) + slot_name = slot.name + context_entry = generated_context.get(slot_name, {}) + # Update JSON-LD context for slots with multi-language support + is_langstring = slot.range == 'langString' + is_exactly_one_language = ( + slot.range is None and + any( + expr.range == 'langString' + for expr in slot.exactly_one_of if isinstance(expr, AnonymousSlotExpression) + ) ) - if (is_langString or is_exactly_one_langString) and isinstance(serialized_schema_json['@context'][slot.name], - dict): - serialized_schema_json['@context'][slot.name]['@container'] = '@language' - if '@type' in serialized_schema_json['@context'][slot.name]: - del serialized_schema_json['@context'][slot.name]['@type'] - if slot.in_language and isinstance(serialized_schema_json['@context'][slot.name], - dict): - serialized_schema_json['@context'][slot.name]['@language'] = slot.in_language - if slot.inlined and slot.multivalued and not slot.inlined_as_list and isinstance(serialized_schema_json['@context'][slot.name], dict): - serialized_schema_json['@context'][slot.name]['@container'] = '@index' + if is_langstring or is_exactly_one_language and isinstance(context_entry, dict): + context_entry['@container'] = '@language' + context_entry.pop('@type', None) + # Update JSON-LD context for slots with a specific encoded language + if slot.in_language and isinstance(context_entry, dict): + context_entry['@language'] = slot.in_language + # inlined and multivalued slot conditions are used to identify dictionaries + if slot.inlined and slot.multivalued and not slot.inlined_as_list and isinstance(context_entry, dict): + context_entry['@container'] = '@index' if slot.instantiates: - serialized_schema_json['@context'][slot.name]['@index'] = slot.instantiates - if slot.multivalued and str(slot.range) == 'Any' and isinstance(serialized_schema_json['@context'][slot.name], dict): - if '@type' in serialized_schema_json['@context'][slot.name].keys(): - del serialized_schema_json['@context'][slot.name]['@type'] - serialized_schema_json['@context'][slot.name]['@container'] = '@set' + context_entry['@index'] = slot.instantiates + generated_context[slot_name] = context_entry + #The multivalued slots which do not already have a @container are assigned to a @set + for slot in schema_view.all_slots().values(): + context_entry = generated_context.get(slot.name, {}) + if slot.multivalued and isinstance(context_entry, dict) and '@container' not in context_entry: + context_entry['@container'] = '@set' + context_entry.pop('@type', None) + generated_context[slot.name] = context_entry return json.dumps(serialized_schema_json, indent=3) @@ -83,8 +88,8 @@ def main(generate_docs_flag, serve_docs_flag): (output_dir / 'ontology.owl.ttl').write_text(owl_generator.serialize()) elif generator == 'jsonldcontext': context_generator = ContextGenerator(linkml_schema_view.schema, mergeimports=True) - (output_dir / 'context.jsonld').write_text(post_process_jsonldcontext(linkml_schema_view, - context_generator.serialize())) + (output_dir / 'context.jsonld').write_text(post_process_jsonld_context(linkml_schema_view, + context_generator.serialize())) elif generator == 'linkml': linkml_generator = LinkmlGenerator(linkml_schema_view.schema, mergeimports=True, format='yaml', output='linkml.yaml') (output_dir / 'linkml.yaml').write_text(linkml_generator.serialize())