@twin.org/federated-catalogue-service 0.0.3-next.13 → 0.0.3-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/entities/dataset.js +8 -0
- package/dist/es/entities/dataset.js.map +1 -1
- package/dist/es/federatedCatalogueRoutes.js +19 -15
- package/dist/es/federatedCatalogueRoutes.js.map +1 -1
- package/dist/es/index.js +1 -0
- package/dist/es/index.js.map +1 -1
- package/dist/es/models/IFederatedCatalogueServiceConfig.js +4 -0
- package/dist/es/models/IFederatedCatalogueServiceConfig.js.map +1 -0
- package/dist/es/models/IFederatedCatalogueServiceConstructorOptions.js +0 -2
- package/dist/es/models/IFederatedCatalogueServiceConstructorOptions.js.map +1 -1
- package/dist/es/services/federatedCatalogueService.js +48 -17
- package/dist/es/services/federatedCatalogueService.js.map +1 -1
- package/dist/types/entities/dataset.d.ts +4 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/models/IFederatedCatalogueServiceConfig.d.ts +5 -0
- package/dist/types/models/IFederatedCatalogueServiceConstructorOptions.d.ts +11 -0
- package/dist/types/services/federatedCatalogueService.d.ts +0 -7
- package/docs/changelog.md +53 -25
- package/docs/open-api/spec.json +23 -19
- package/docs/reference/classes/Dataset.md +67 -59
- package/docs/reference/classes/FederatedCatalogueService.md +7 -7
- package/docs/reference/functions/datasetEntityToModel.md +2 -2
- package/docs/reference/index.md +1 -0
- package/docs/reference/interfaces/IFederatedCatalogueServiceConfig.md +3 -0
- package/docs/reference/interfaces/IFederatedCatalogueServiceConstructorOptions.md +33 -4
- package/package.json +5 -4
|
@@ -19,6 +19,10 @@ let Dataset = class Dataset {
|
|
|
19
19
|
* The date the entity was last modified (required for sync).
|
|
20
20
|
*/
|
|
21
21
|
dateModified;
|
|
22
|
+
/**
|
|
23
|
+
* The tenant id of the publisher captured at write time.
|
|
24
|
+
*/
|
|
25
|
+
tenantId;
|
|
22
26
|
/**
|
|
23
27
|
* The JSON-LD context for the dataset.
|
|
24
28
|
*/
|
|
@@ -148,6 +152,10 @@ __decorate([
|
|
|
148
152
|
property({ type: "string", isSecondary: true }),
|
|
149
153
|
__metadata("design:type", String)
|
|
150
154
|
], Dataset.prototype, "dateModified", void 0);
|
|
155
|
+
__decorate([
|
|
156
|
+
property({ type: "string", optional: true }),
|
|
157
|
+
__metadata("design:type", String)
|
|
158
|
+
], Dataset.prototype, "tenantId", void 0);
|
|
151
159
|
__decorate([
|
|
152
160
|
property({ type: "object" }),
|
|
153
161
|
__metadata("design:type", Object)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../../src/entities/dataset.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { entity, property } from \"@twin.org/entity\";\nimport type { IDcatDataset } from \"@twin.org/standards-w3c-dcat\";\n\n/**\n * Class describing a DCAT dataset for entity storage.\n * This wrapper enables efficient database indexing and querying while preserving\n * the full IDcatDataset JSON-LD structure.\n */\n@entity()\nexport class Dataset {\n\t/**\n\t * The unique identifier for the dataset (mapped from JSON-LD identifier).\n\t */\n\t@property({ type: \"string\", isPrimary: true })\n\tpublic id!: string;\n\n\t/**\n\t * The identity of the node that owns this entity (required for sync).\n\t */\n\t@property({ type: \"string\", isSecondary: true })\n\tpublic nodeIdentity!: string;\n\n\t/**\n\t * The date the entity was last modified (required for sync).\n\t */\n\t@property({ type: \"string\", isSecondary: true })\n\tpublic dateModified!: string;\n\n\t/**\n\t * The JSON-LD context for the dataset.\n\t */\n\t@property({ type: \"object\" })\n\tpublic \"@context\"!: IDcatDataset[\"@context\"];\n\n\t/**\n\t * The type of the resource (typically \"Dataset\").\n\t */\n\t@property({ type: \"string\" })\n\tpublic \"@type\"!: IDcatDataset[\"@type\"];\n\n\t/**\n\t * A name given to the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:title\"?: IDcatDataset[\"dcterms:title\"];\n\n\t/**\n\t * A free-text account of the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:description\"?: IDcatDataset[\"dcterms:description\"];\n\n\t/**\n\t * A unique identifier of the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:identifier\"?: IDcatDataset[\"dcterms:identifier\"];\n\n\t/**\n\t * Date of formal issuance (publication) of the resource.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:issued\"?: IDcatDataset[\"dcterms:issued\"];\n\n\t/**\n\t * Most recent date on which the resource was changed, updated or modified.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:modified\"?: IDcatDataset[\"dcterms:modified\"];\n\n\t/**\n\t * A language of the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:language\"?: IDcatDataset[\"dcterms:language\"];\n\n\t/**\n\t * An entity responsible for making the resource available.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:publisher\"?: IDcatDataset[\"dcterms:publisher\"];\n\n\t/**\n\t * An entity responsible for producing the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:creator\"?: IDcatDataset[\"dcterms:creator\"];\n\n\t/**\n\t * Information about who can access the resource or an indication of its security status.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:accessRights\"?: IDcatDataset[\"dcterms:accessRights\"];\n\n\t/**\n\t * A legal document under which the resource is made available.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:license\"?: IDcatDataset[\"dcterms:license\"];\n\n\t/**\n\t * Information about rights held in and over the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:rights\"?: IDcatDataset[\"dcterms:rights\"];\n\n\t/**\n\t * An established standard to which the resource conforms.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:conformsTo\"?: IDcatDataset[\"dcterms:conformsTo\"];\n\n\t/**\n\t * The nature or genre of the resource.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:type\"?: IDcatDataset[\"dcterms:type\"];\n\n\t/**\n\t * Relevant contact information for the catalogued resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:contactPoint\"?: IDcatDataset[\"dcat:contactPoint\"];\n\n\t/**\n\t * A keyword or tag describing the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:keyword\"?: IDcatDataset[\"dcat:keyword\"];\n\n\t/**\n\t * A main category of the resource. A resource can have multiple themes.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:theme\"?: IDcatDataset[\"dcat:theme\"];\n\n\t/**\n\t * A Web page that can be navigated to gain access to the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:landingPage\"?: IDcatDataset[\"dcat:landingPage\"];\n\n\t/**\n\t * Link to a description of a relationship with another resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:qualifiedRelation\"?: IDcatDataset[\"dcat:qualifiedRelation\"];\n\n\t/**\n\t * An ODRL conformant policy expressing the rights associated with the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"odrl:hasPolicy\"?: IDcatDataset[\"odrl:hasPolicy\"];\n\n\t/**\n\t * An available distribution of the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:distribution\"?: IDcatDataset[\"dcat:distribution\"];\n\n\t/**\n\t * The frequency at which the dataset is published.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:accrualPeriodicity\"?: IDcatDataset[\"dcterms:accrualPeriodicity\"];\n\n\t/**\n\t * A dataset series of which the dataset is part.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcat:inSeries\"?: IDcatDataset[\"dcat:inSeries\"];\n\n\t/**\n\t * The geographical area covered by the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:spatial\"?: IDcatDataset[\"dcterms:spatial\"];\n\n\t/**\n\t * Minimum spatial separation resolvable in a dataset, measured in meters.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:spatialResolutionInMeters\"?: IDcatDataset[\"dcat:spatialResolutionInMeters\"];\n\n\t/**\n\t * The temporal period that the dataset covers.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:temporal\"?: IDcatDataset[\"dcterms:temporal\"];\n\n\t/**\n\t * Minimum time period resolvable in the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:temporalResolution\"?: IDcatDataset[\"dcat:temporalResolution\"];\n\n\t/**\n\t * An activity that generated, or provides the business context for, the creation of the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"prov:wasGeneratedBy\"?: IDcatDataset[\"prov:wasGeneratedBy\"];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../../src/entities/dataset.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { entity, property } from \"@twin.org/entity\";\nimport type { IDcatDataset } from \"@twin.org/standards-w3c-dcat\";\n\n/**\n * Class describing a DCAT dataset for entity storage.\n * This wrapper enables efficient database indexing and querying while preserving\n * the full IDcatDataset JSON-LD structure.\n */\n@entity()\nexport class Dataset {\n\t/**\n\t * The unique identifier for the dataset (mapped from JSON-LD identifier).\n\t */\n\t@property({ type: \"string\", isPrimary: true })\n\tpublic id!: string;\n\n\t/**\n\t * The identity of the node that owns this entity (required for sync).\n\t */\n\t@property({ type: \"string\", isSecondary: true })\n\tpublic nodeIdentity!: string;\n\n\t/**\n\t * The date the entity was last modified (required for sync).\n\t */\n\t@property({ type: \"string\", isSecondary: true })\n\tpublic dateModified!: string;\n\n\t/**\n\t * The tenant id of the publisher captured at write time.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic tenantId?: string;\n\n\t/**\n\t * The JSON-LD context for the dataset.\n\t */\n\t@property({ type: \"object\" })\n\tpublic \"@context\"!: IDcatDataset[\"@context\"];\n\n\t/**\n\t * The type of the resource (typically \"Dataset\").\n\t */\n\t@property({ type: \"string\" })\n\tpublic \"@type\"!: IDcatDataset[\"@type\"];\n\n\t/**\n\t * A name given to the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:title\"?: IDcatDataset[\"dcterms:title\"];\n\n\t/**\n\t * A free-text account of the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:description\"?: IDcatDataset[\"dcterms:description\"];\n\n\t/**\n\t * A unique identifier of the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:identifier\"?: IDcatDataset[\"dcterms:identifier\"];\n\n\t/**\n\t * Date of formal issuance (publication) of the resource.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:issued\"?: IDcatDataset[\"dcterms:issued\"];\n\n\t/**\n\t * Most recent date on which the resource was changed, updated or modified.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:modified\"?: IDcatDataset[\"dcterms:modified\"];\n\n\t/**\n\t * A language of the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:language\"?: IDcatDataset[\"dcterms:language\"];\n\n\t/**\n\t * An entity responsible for making the resource available.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:publisher\"?: IDcatDataset[\"dcterms:publisher\"];\n\n\t/**\n\t * An entity responsible for producing the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:creator\"?: IDcatDataset[\"dcterms:creator\"];\n\n\t/**\n\t * Information about who can access the resource or an indication of its security status.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:accessRights\"?: IDcatDataset[\"dcterms:accessRights\"];\n\n\t/**\n\t * A legal document under which the resource is made available.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:license\"?: IDcatDataset[\"dcterms:license\"];\n\n\t/**\n\t * Information about rights held in and over the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:rights\"?: IDcatDataset[\"dcterms:rights\"];\n\n\t/**\n\t * An established standard to which the resource conforms.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:conformsTo\"?: IDcatDataset[\"dcterms:conformsTo\"];\n\n\t/**\n\t * The nature or genre of the resource.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:type\"?: IDcatDataset[\"dcterms:type\"];\n\n\t/**\n\t * Relevant contact information for the catalogued resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:contactPoint\"?: IDcatDataset[\"dcat:contactPoint\"];\n\n\t/**\n\t * A keyword or tag describing the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:keyword\"?: IDcatDataset[\"dcat:keyword\"];\n\n\t/**\n\t * A main category of the resource. A resource can have multiple themes.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:theme\"?: IDcatDataset[\"dcat:theme\"];\n\n\t/**\n\t * A Web page that can be navigated to gain access to the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:landingPage\"?: IDcatDataset[\"dcat:landingPage\"];\n\n\t/**\n\t * Link to a description of a relationship with another resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:qualifiedRelation\"?: IDcatDataset[\"dcat:qualifiedRelation\"];\n\n\t/**\n\t * An ODRL conformant policy expressing the rights associated with the resource.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"odrl:hasPolicy\"?: IDcatDataset[\"odrl:hasPolicy\"];\n\n\t/**\n\t * An available distribution of the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:distribution\"?: IDcatDataset[\"dcat:distribution\"];\n\n\t/**\n\t * The frequency at which the dataset is published.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcterms:accrualPeriodicity\"?: IDcatDataset[\"dcterms:accrualPeriodicity\"];\n\n\t/**\n\t * A dataset series of which the dataset is part.\n\t */\n\t@property({ type: \"string\", optional: true })\n\tpublic \"dcat:inSeries\"?: IDcatDataset[\"dcat:inSeries\"];\n\n\t/**\n\t * The geographical area covered by the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:spatial\"?: IDcatDataset[\"dcterms:spatial\"];\n\n\t/**\n\t * Minimum spatial separation resolvable in a dataset, measured in meters.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:spatialResolutionInMeters\"?: IDcatDataset[\"dcat:spatialResolutionInMeters\"];\n\n\t/**\n\t * The temporal period that the dataset covers.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcterms:temporal\"?: IDcatDataset[\"dcterms:temporal\"];\n\n\t/**\n\t * Minimum time period resolvable in the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"dcat:temporalResolution\"?: IDcatDataset[\"dcat:temporalResolution\"];\n\n\t/**\n\t * An activity that generated, or provides the business context for, the creation of the dataset.\n\t */\n\t@property({ type: \"object\", optional: true })\n\tpublic \"prov:wasGeneratedBy\"?: IDcatDataset[\"prov:wasGeneratedBy\"];\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Coerce, ComponentFactory, Guards, Is } from "@twin.org/core";
|
|
2
2
|
import { DataspaceProtocolCatalogTypes, DataspaceProtocolContexts } from "@twin.org/standards-dataspace-protocol";
|
|
3
3
|
import { DcatClasses } from "@twin.org/standards-w3c-dcat";
|
|
4
|
-
import {
|
|
4
|
+
import { OdrlPolicyType } from "@twin.org/standards-w3c-odrl";
|
|
5
5
|
import { HeaderHelper, HeaderTypes, HttpStatusCode } from "@twin.org/web";
|
|
6
6
|
import { transformErrorToStatusCode, transformToCatalogError } from "./utils/catalogErrorUtils.js";
|
|
7
7
|
/**
|
|
@@ -77,21 +77,25 @@ export function generateRestRoutesFederatedCatalogue(baseRouteName, componentNam
|
|
|
77
77
|
"@type": "Dataset",
|
|
78
78
|
"dcterms:title": "Energy Consumption Data",
|
|
79
79
|
"dcterms:description": "Historical energy consumption data",
|
|
80
|
-
hasPolicy:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
distribution: {
|
|
86
|
-
"@id": "urn:uuid:distribution-789",
|
|
87
|
-
"@type": "Distribution",
|
|
88
|
-
format: "application/json",
|
|
89
|
-
accessService: {
|
|
90
|
-
"@id": "urn:uuid:access-service-321",
|
|
91
|
-
"@type": "DataService",
|
|
92
|
-
endpointURL: "https://example.com/data-access"
|
|
80
|
+
hasPolicy: [
|
|
81
|
+
{
|
|
82
|
+
"@id": "urn:uuid:policy-456",
|
|
83
|
+
"@type": OdrlPolicyType.Offer,
|
|
84
|
+
assigner: "did:example:data-provider-789"
|
|
93
85
|
}
|
|
94
|
-
|
|
86
|
+
],
|
|
87
|
+
distribution: [
|
|
88
|
+
{
|
|
89
|
+
"@id": "urn:uuid:distribution-789",
|
|
90
|
+
"@type": "Distribution",
|
|
91
|
+
format: "application/json",
|
|
92
|
+
accessService: {
|
|
93
|
+
"@id": "urn:uuid:access-service-321",
|
|
94
|
+
"@type": "DataService",
|
|
95
|
+
endpointURL: "https://example.com/data-access"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
]
|
|
95
99
|
}
|
|
96
100
|
]
|
|
97
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"federatedCatalogueRoutes.js","sourceRoot":"","sources":["../../src/federatedCatalogueRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIHostingComponent,\n\tIHttpRequestContext,\n\tIRestRoute,\n\tITag\n} from \"@twin.org/api-models\";\nimport { Coerce, ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport type {\n\tICatalogRequestRequest,\n\tICatalogRequestResponse,\n\tIFederatedCatalogueComponent,\n\tIGetDatasetRequest,\n\tIGetDatasetResponse\n} from \"@twin.org/federated-catalogue-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DcatClasses, type DcatContextType } from \"@twin.org/standards-w3c-dcat\";\nimport { PolicyType } from \"@twin.org/standards-w3c-odrl\";\nimport { HeaderHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\nimport { transformErrorToStatusCode, transformToCatalogError } from \"./utils/catalogErrorUtils.js\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"federatedCatalogueRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsFederatedCatalogue: ITag[] = [\n\t{\n\t\tname: \"Federated Catalogue\",\n\t\tdescription:\n\t\t\t\"Service providing Dataspace Protocol-compliant catalogue endpoints for dataset discovery and query.\"\n\t}\n];\n\n/**\n * The REST routes for federated catalogue.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesFederatedCatalogue(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst catalogRequestRoute: IRestRoute<ICatalogRequestRequest, ICatalogRequestResponse> = {\n\t\toperationId: \"catalogRequest\",\n\t\tsummary: \"Query the federated catalogue for datasets\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/request`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tcatalogRequest(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ICatalogRequestRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage,\n\t\t\t\t\t\t\tfilter: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestNoFilterExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ICatalogRequestResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"catalogRequestResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\t\"@id\":\n\t\t\t\t\t\t\t\t\t\"urn:x-catalog:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2\",\n\t\t\t\t\t\t\t\t\"@type\": \"Catalog\",\n\t\t\t\t\t\t\t\tparticipantId: \"did:example:node-identity-123\",\n\t\t\t\t\t\t\t\tdataset: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\t\t\"@type\": \"Dataset\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\",\n\t\t\t\t\t\t\t\t\t\thasPolicy: {\n\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:policy-456\",\n\t\t\t\t\t\t\t\t\t\t\t\"@type\": PolicyType.Offer,\n\t\t\t\t\t\t\t\t\t\t\tassigner: \"did:example:data-provider-789\"\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tdistribution: {\n\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:distribution-789\",\n\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"Distribution\",\n\t\t\t\t\t\t\t\t\t\t\tformat: \"application/json\",\n\t\t\t\t\t\t\t\t\t\t\taccessService: {\n\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:access-service-321\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"DataService\",\n\t\t\t\t\t\t\t\t\t\t\t\tendpointURL: \"https://example.com/data-access\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t};\n\n\tconst getDatasetRoute: IRestRoute<IGetDatasetRequest, IGetDatasetResponse> = {\n\t\toperationId: \"getDataset\",\n\t\tsummary: \"Retrieve a specific dataset by ID\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/datasets/:datasetId`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tgetDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IGetDatasetRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"getDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tdatasetId: \"urn:uuid:dataset-123\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IGetDatasetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"getDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": DataspaceProtocolContexts.Context as unknown as DcatContextType,\n\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\"@type\": DcatClasses.Dataset,\n\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t};\n\n\treturn [catalogRequestRoute, getDatasetRoute];\n}\n\n/**\n * Handle the catalog request operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function catalogRequest(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ICatalogRequestRequest\n): Promise<ICatalogRequestResponse> {\n\ttry {\n\t\tGuards.object<ICatalogRequestRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.body), request.body);\n\t\tGuards.stringValue(ROUTES_SOURCE, \"@type\", request.body[\"@type\"]);\n\n\t\tconst hostingComponent = ComponentFactory.get<IHostingComponent>(\n\t\t\thttpRequestContext.hostingComponentType ?? \"hosting\"\n\t\t);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.query(\n\t\t\trequest.body.filter,\n\t\t\trequest.query?.cursor,\n\t\t\tCoerce.integer(request.query?.limit)\n\t\t);\n\n\t\tconst headers: ICatalogRequestResponse[\"headers\"] = {};\n\n\t\tif (Is.stringValue(result.cursor)) {\n\t\t\theaders[HeaderTypes.Link] = HeaderHelper.createLinkHeader(\n\t\t\t\tawait hostingComponent.buildPublicUrl(httpRequestContext.serverRequest.url),\n\t\t\t\t{ cursor: result.cursor },\n\t\t\t\t\"next\"\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\theaders,\n\t\t\tstatusCode: transformErrorToStatusCode(result.result),\n\t\t\tbody: result.result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the get dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function getDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IGetDatasetRequest\n): Promise<IGetDatasetResponse> {\n\ttry {\n\t\tGuards.object<IGetDatasetRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.pathParams), request.pathParams);\n\t\tGuards.stringValue(\n\t\t\tROUTES_SOURCE,\n\t\t\tnameof(request.pathParams.datasetId),\n\t\t\trequest.pathParams.datasetId\n\t\t);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.get(request.pathParams.datasetId);\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result),\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"federatedCatalogueRoutes.js","sourceRoot":"","sources":["../../src/federatedCatalogueRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIHostingComponent,\n\tIHttpRequestContext,\n\tIRestRoute,\n\tITag\n} from \"@twin.org/api-models\";\nimport { Coerce, ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport type {\n\tICatalogRequestRequest,\n\tICatalogRequestResponse,\n\tIFederatedCatalogueComponent,\n\tIGetDatasetRequest,\n\tIGetDatasetResponse\n} from \"@twin.org/federated-catalogue-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DcatClasses, type DcatContextType } from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlPolicyType } from \"@twin.org/standards-w3c-odrl\";\nimport { HeaderHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\nimport { transformErrorToStatusCode, transformToCatalogError } from \"./utils/catalogErrorUtils.js\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"federatedCatalogueRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsFederatedCatalogue: ITag[] = [\n\t{\n\t\tname: \"Federated Catalogue\",\n\t\tdescription:\n\t\t\t\"Service providing Dataspace Protocol-compliant catalogue endpoints for dataset discovery and query.\"\n\t}\n];\n\n/**\n * The REST routes for federated catalogue.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesFederatedCatalogue(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst catalogRequestRoute: IRestRoute<ICatalogRequestRequest, ICatalogRequestResponse> = {\n\t\toperationId: \"catalogRequest\",\n\t\tsummary: \"Query the federated catalogue for datasets\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/request`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tcatalogRequest(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ICatalogRequestRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage,\n\t\t\t\t\t\t\tfilter: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"catalogRequestNoFilterExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogRequestMessage\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ICatalogRequestResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"catalogRequestResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\t\t\"@id\":\n\t\t\t\t\t\t\t\t\t\"urn:x-catalog:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2\",\n\t\t\t\t\t\t\t\t\"@type\": \"Catalog\",\n\t\t\t\t\t\t\t\tparticipantId: \"did:example:node-identity-123\",\n\t\t\t\t\t\t\t\tdataset: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\t\t\"@type\": \"Dataset\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\",\n\t\t\t\t\t\t\t\t\t\thasPolicy: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:policy-456\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": OdrlPolicyType.Offer,\n\t\t\t\t\t\t\t\t\t\t\t\tassigner: \"did:example:data-provider-789\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tdistribution: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:distribution-789\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"Distribution\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"application/json\",\n\t\t\t\t\t\t\t\t\t\t\t\taccessService: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:access-service-321\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"@type\": \"DataService\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tendpointURL: \"https://example.com/data-access\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t};\n\n\tconst getDatasetRoute: IRestRoute<IGetDatasetRequest, IGetDatasetResponse> = {\n\t\toperationId: \"getDataset\",\n\t\tsummary: \"Retrieve a specific dataset by ID\",\n\t\ttag: tagsFederatedCatalogue[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/datasets/:datasetId`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tgetDataset(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IGetDatasetRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"getDatasetRequestExample\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\tdatasetId: \"urn:uuid:dataset-123\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IGetDatasetResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"getDatasetResponseExample\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\"@context\": DataspaceProtocolContexts.Context as unknown as DcatContextType,\n\t\t\t\t\t\t\t\t\"@id\": \"urn:uuid:dataset-123\",\n\t\t\t\t\t\t\t\t\"@type\": DcatClasses.Dataset,\n\t\t\t\t\t\t\t\t\"dcterms:title\": \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\"dcterms:description\": \"Historical energy consumption data\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t};\n\n\treturn [catalogRequestRoute, getDatasetRoute];\n}\n\n/**\n * Handle the catalog request operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function catalogRequest(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ICatalogRequestRequest\n): Promise<ICatalogRequestResponse> {\n\ttry {\n\t\tGuards.object<ICatalogRequestRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.body), request.body);\n\t\tGuards.stringValue(ROUTES_SOURCE, \"@type\", request.body[\"@type\"]);\n\n\t\tconst hostingComponent = ComponentFactory.get<IHostingComponent>(\n\t\t\thttpRequestContext.hostingComponentType ?? \"hosting\"\n\t\t);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.query(\n\t\t\trequest.body.filter,\n\t\t\trequest.query?.cursor,\n\t\t\tCoerce.integer(request.query?.limit)\n\t\t);\n\n\t\tconst headers: ICatalogRequestResponse[\"headers\"] = {};\n\n\t\tif (Is.stringValue(result.cursor)) {\n\t\t\theaders[HeaderTypes.Link] = HeaderHelper.createLinkHeader(\n\t\t\t\tawait hostingComponent.buildPublicUrl(httpRequestContext.serverRequest.url),\n\t\t\t\t{ cursor: result.cursor },\n\t\t\t\t\"next\"\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\theaders,\n\t\t\tstatusCode: transformErrorToStatusCode(result.result),\n\t\t\tbody: result.result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n\n/**\n * Handle the get dataset operation.\n * @param httpRequestContext The request context for the operation.\n * @param componentName The name of the component to use.\n * @param request The request.\n * @returns The response.\n */\nasync function getDataset(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IGetDatasetRequest\n): Promise<IGetDatasetResponse> {\n\ttry {\n\t\tGuards.object<IGetDatasetRequest>(ROUTES_SOURCE, nameof(request), request);\n\t\tGuards.object(ROUTES_SOURCE, nameof(request.pathParams), request.pathParams);\n\t\tGuards.stringValue(\n\t\t\tROUTES_SOURCE,\n\t\t\tnameof(request.pathParams.datasetId),\n\t\t\trequest.pathParams.datasetId\n\t\t);\n\n\t\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst result = await component.get(request.pathParams.datasetId);\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(result),\n\t\t\tbody: result\n\t\t};\n\t} catch (error) {\n\t\tconst catalogError = transformToCatalogError(error);\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,\n\t\t\tbody: catalogError\n\t\t};\n\t}\n}\n"]}
|
package/dist/es/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
3
|
export * from "./entities/dataset.js";
|
|
4
4
|
export * from "./federatedCatalogueRoutes.js";
|
|
5
|
+
export * from "./models/IFederatedCatalogueServiceConfig.js";
|
|
5
6
|
export * from "./models/IFederatedCatalogueServiceConstructorOptions.js";
|
|
6
7
|
export * from "./restEntryPoints.js";
|
|
7
8
|
export * from "./schema.js";
|
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0DAA0D,CAAC;AACzE,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./entities/dataset.js\";\nexport * from \"./federatedCatalogueRoutes.js\";\nexport * from \"./models/IFederatedCatalogueServiceConstructorOptions.js\";\nexport * from \"./restEntryPoints.js\";\nexport * from \"./schema.js\";\nexport * from \"./services/federatedCatalogueService.js\";\nexport * from \"./utils/catalogErrorUtils.js\";\nexport * from \"./utils/datasetConverters.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8CAA8C,CAAC;AAC7D,cAAc,0DAA0D,CAAC;AACzE,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./entities/dataset.js\";\nexport * from \"./federatedCatalogueRoutes.js\";\nexport * from \"./models/IFederatedCatalogueServiceConfig.js\";\nexport * from \"./models/IFederatedCatalogueServiceConstructorOptions.js\";\nexport * from \"./restEntryPoints.js\";\nexport * from \"./schema.js\";\nexport * from \"./services/federatedCatalogueService.js\";\nexport * from \"./utils/catalogErrorUtils.js\";\nexport * from \"./utils/datasetConverters.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IFederatedCatalogueServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IFederatedCatalogueServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the FederatedCatalogueService.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface IFederatedCatalogueServiceConfig {}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IFederatedCatalogueServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IFederatedCatalogueServiceConstructorOptions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"IFederatedCatalogueServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IFederatedCatalogueServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IFederatedCatalogueServiceConfig } from \"./IFederatedCatalogueServiceConfig.js\";\n\n/**\n * Options for the FederatedCatalogueService constructor.\n */\nexport interface IFederatedCatalogueServiceConstructorOptions {\n\t/**\n\t * The entity storage for datasets.\n\t * @default dataset\n\t */\n\tdatasetEntityStorageType?: string;\n\n\t/**\n\t * The logging component for the service.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * URL transformer component type used to encrypt the per-publisher tenant token into\n\t * distribution accessService URLs at write time.\n\t * @default url-transformer\n\t */\n\turlTransformerComponentType?: string;\n\n\t/**\n\t * Configuration for the federated catalogue service.\n\t */\n\tconfig?: IFederatedCatalogueServiceConfig;\n}\n"]}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
// Copyright 2025 IOTA Stiftung.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
3
1
|
import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
|
|
4
|
-
import { BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, JsonHelper, NotFoundError, ObjectHelper, Url, Urn, Validation } from "@twin.org/core";
|
|
2
|
+
import { ArrayHelper, BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, JsonHelper, NotFoundError, ObjectHelper, Url, Urn, Validation } from "@twin.org/core";
|
|
5
3
|
import { Blake2b } from "@twin.org/crypto";
|
|
6
4
|
import { JsonLdHelper, JsonLdProcessor } from "@twin.org/data-json-ld";
|
|
7
5
|
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
8
6
|
import { FederatedCatalogueFilterFactory } from "@twin.org/federated-catalogue-models";
|
|
9
|
-
import { DataspaceProtocolContexts, DataspaceProtocolDataTypes, DataspaceProtocolHelper } from "@twin.org/standards-dataspace-protocol";
|
|
7
|
+
import { DataspaceProtocolCatalogTypes, DataspaceProtocolContexts, DataspaceProtocolDataTypes, DataspaceProtocolHelper } from "@twin.org/standards-dataspace-protocol";
|
|
10
8
|
import { DublinCoreContexts, DublinCoreDataTypes } from "@twin.org/standards-dublin-core";
|
|
11
9
|
import { FoafDataTypes } from "@twin.org/standards-foaf";
|
|
12
10
|
import { DcatContexts, DcatDataTypes } from "@twin.org/standards-w3c-dcat";
|
|
@@ -32,6 +30,11 @@ export class FederatedCatalogueService {
|
|
|
32
30
|
* @internal
|
|
33
31
|
*/
|
|
34
32
|
_datasetStorage;
|
|
33
|
+
/**
|
|
34
|
+
* The URL transformer component for encrypting tenant routing tokens into distribution URLs.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
_urlTransformerComponent;
|
|
35
38
|
/**
|
|
36
39
|
* The node identity.
|
|
37
40
|
* @internal
|
|
@@ -44,6 +47,7 @@ export class FederatedCatalogueService {
|
|
|
44
47
|
constructor(options) {
|
|
45
48
|
this._logging = ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
|
|
46
49
|
this._datasetStorage = EntityStorageConnectorFactory.get(options?.datasetEntityStorageType ?? "dataset");
|
|
50
|
+
this._urlTransformerComponent = ComponentFactory.get(options?.urlTransformerComponentType ?? "url-transformer");
|
|
47
51
|
// Register JSON-LD redirects for offline processing
|
|
48
52
|
DcatDataTypes.registerRedirects();
|
|
49
53
|
DublinCoreDataTypes.registerRedirects();
|
|
@@ -109,12 +113,12 @@ export class FederatedCatalogueService {
|
|
|
109
113
|
const dataSetId = dataSet["@id"] ?? dataSet["dcterms:identifier"];
|
|
110
114
|
Guards.stringValue(FederatedCatalogueService.CLASS_NAME, "dataSetId", dataSetId);
|
|
111
115
|
// Set @id if it was derived from dcterms:identifier
|
|
112
|
-
if (
|
|
116
|
+
if (Is.empty(dataSet["@id"]) && !Is.empty(dataSet["dcterms:identifier"])) {
|
|
113
117
|
dataSet["@id"] = dataSetId;
|
|
114
118
|
}
|
|
115
119
|
// Validate @id is a valid URI (URN or URL) per DS Protocol
|
|
116
|
-
const isValidUrn = Urn.tryParseExact(dataSetId)
|
|
117
|
-
const isValidUrl = Url.tryParseExact(dataSetId)
|
|
120
|
+
const isValidUrn = !Is.empty(Urn.tryParseExact(dataSetId));
|
|
121
|
+
const isValidUrl = !Is.empty(Url.tryParseExact(dataSetId));
|
|
118
122
|
if (!isValidUrn && !isValidUrl) {
|
|
119
123
|
throw new GeneralError(FederatedCatalogueService.CLASS_NAME, "datasetIdInvalidUri", {
|
|
120
124
|
dataSetId
|
|
@@ -125,7 +129,7 @@ export class FederatedCatalogueService {
|
|
|
125
129
|
// Validate dcterms:publisher exists (required for multi-participant catalog)
|
|
126
130
|
// The publisher is used to derive participantId when returning catalog query results
|
|
127
131
|
const publisher = dataSet["dcterms:publisher"];
|
|
128
|
-
if (
|
|
132
|
+
if (Is.empty(publisher)) {
|
|
129
133
|
throw new GeneralError(FederatedCatalogueService.CLASS_NAME, "datasetMissingPublisher", {
|
|
130
134
|
dataSetId
|
|
131
135
|
});
|
|
@@ -144,6 +148,14 @@ export class FederatedCatalogueService {
|
|
|
144
148
|
};
|
|
145
149
|
const normalizedDataset = await JsonLdProcessor.compact(dataSet, storageContext);
|
|
146
150
|
const datasetEntity = datasetModelToEntity(normalizedDataset, this._nodeId ?? "", new Date().toISOString());
|
|
151
|
+
// Capture the publishing tenant from the current request context.
|
|
152
|
+
const setContextIds = await ContextIdStore.getContextIds();
|
|
153
|
+
const setTenantId = setContextIds?.[ContextIdKeys.Tenant];
|
|
154
|
+
if (Is.stringValue(setTenantId)) {
|
|
155
|
+
datasetEntity.tenantId = setTenantId;
|
|
156
|
+
// Bake the publishing tenant token into distribution accessService URLs
|
|
157
|
+
await this.bakeTenantTokenIntoDistributions(datasetEntity, setTenantId);
|
|
158
|
+
}
|
|
147
159
|
// Skip update if entity content hasn't changed to avoid unnecessary sync
|
|
148
160
|
const existingEntity = await this._datasetStorage.get(dataSetId);
|
|
149
161
|
if (existingEntity) {
|
|
@@ -213,7 +225,7 @@ export class FederatedCatalogueService {
|
|
|
213
225
|
if (!Is.empty(filter) && !Is.array(filter)) {
|
|
214
226
|
throw new GeneralError(FederatedCatalogueService.CLASS_NAME, "filterMustBeArray");
|
|
215
227
|
}
|
|
216
|
-
if (!filter
|
|
228
|
+
if (!Is.arrayValue(filter)) {
|
|
217
229
|
const result = await this._datasetStorage.query();
|
|
218
230
|
datasets = result.entities.map(entity => datasetEntityToModel(entity));
|
|
219
231
|
}
|
|
@@ -245,7 +257,7 @@ export class FederatedCatalogueService {
|
|
|
245
257
|
data: { resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) }
|
|
246
258
|
});
|
|
247
259
|
// Return CatalogError 404 when no datasets exist
|
|
248
|
-
if (datasets
|
|
260
|
+
if (!Is.arrayValue(datasets)) {
|
|
249
261
|
throw new NotFoundError(FederatedCatalogueService.CLASS_NAME, "noDatasetsFound");
|
|
250
262
|
}
|
|
251
263
|
// Get requesting participant from context (organizationId maps to participantId)
|
|
@@ -269,13 +281,13 @@ export class FederatedCatalogueService {
|
|
|
269
281
|
const ownDatasets = datasetsByParticipant.get(requestingParticipantId) ?? [];
|
|
270
282
|
const otherParticipantIds = participantIds.filter(id => id !== requestingParticipantId);
|
|
271
283
|
let catalog;
|
|
272
|
-
if (otherParticipantIds
|
|
284
|
+
if (!Is.arrayValue(otherParticipantIds)) {
|
|
273
285
|
// Only own datasets (or all datasets belong to requesting participant)
|
|
274
286
|
const catalogId = this.generateCatalogId(ownDatasets, requestingParticipantId);
|
|
275
287
|
catalog = {
|
|
276
288
|
"@context": [DataspaceProtocolContexts.Context],
|
|
277
289
|
"@id": catalogId,
|
|
278
|
-
"@type":
|
|
290
|
+
"@type": DataspaceProtocolCatalogTypes.Catalog,
|
|
279
291
|
participantId: requestingParticipantId,
|
|
280
292
|
dataset: ownDatasets
|
|
281
293
|
};
|
|
@@ -289,7 +301,7 @@ export class FederatedCatalogueService {
|
|
|
289
301
|
const subCatalog = {
|
|
290
302
|
"@context": [DataspaceProtocolContexts.Context],
|
|
291
303
|
"@id": subCatalogId,
|
|
292
|
-
"@type":
|
|
304
|
+
"@type": DataspaceProtocolCatalogTypes.Catalog,
|
|
293
305
|
participantId,
|
|
294
306
|
dataset: participantDatasets
|
|
295
307
|
};
|
|
@@ -300,7 +312,7 @@ export class FederatedCatalogueService {
|
|
|
300
312
|
catalog = {
|
|
301
313
|
"@context": [DataspaceProtocolContexts.Context],
|
|
302
314
|
"@id": rootCatalogId,
|
|
303
|
-
"@type":
|
|
315
|
+
"@type": DataspaceProtocolCatalogTypes.Catalog,
|
|
304
316
|
participantId: requestingParticipantId,
|
|
305
317
|
dataset: ownDatasets,
|
|
306
318
|
catalog: nestedCatalogs
|
|
@@ -309,8 +321,9 @@ export class FederatedCatalogueService {
|
|
|
309
321
|
// Normalize to DS Protocol compliant format
|
|
310
322
|
// This ensures the payload matches exactly what the DS Protocol mandates
|
|
311
323
|
const normalizedCatalog = await DataspaceProtocolHelper.normalize(JsonLdHelper.toNodeObject(catalog));
|
|
324
|
+
const structuredResult = JsonLdHelper.toStructuredObject(normalizedCatalog);
|
|
312
325
|
return {
|
|
313
|
-
result:
|
|
326
|
+
result: structuredResult,
|
|
314
327
|
cursor: resultCursor
|
|
315
328
|
};
|
|
316
329
|
}
|
|
@@ -345,17 +358,35 @@ export class FederatedCatalogueService {
|
|
|
345
358
|
extractPublisher(dataset) {
|
|
346
359
|
const publisher = dataset["dcterms:publisher"];
|
|
347
360
|
// Handle case where publisher is an object with @id (IFoafAgent)
|
|
348
|
-
if (publisher
|
|
361
|
+
if (Is.object(publisher)) {
|
|
349
362
|
const publisherId = publisher["@id"];
|
|
350
363
|
return Is.stringValue(publisherId) ? publisherId : undefined;
|
|
351
364
|
}
|
|
352
365
|
return Is.stringValue(publisher) ? publisher : undefined;
|
|
353
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Bake the publishing tenant token into each distribution's accessService URL.
|
|
369
|
+
* @param entity The dataset entity to modify in place.
|
|
370
|
+
* @param tenantId The publishing tenant id to bake.
|
|
371
|
+
* @internal
|
|
372
|
+
*/
|
|
373
|
+
async bakeTenantTokenIntoDistributions(entity, tenantId) {
|
|
374
|
+
// Storage uses the prefixed JSON-LD key "dcat:accessService" (not the unprefixed
|
|
375
|
+
// "accessService" that appears in compacted query responses).
|
|
376
|
+
const distributions = ArrayHelper.fromObjectOrArray(entity["dcat:distribution"]) ?? [];
|
|
377
|
+
for (const dist of distributions) {
|
|
378
|
+
if (Is.stringValue(dist?.["dcat:accessService"])) {
|
|
379
|
+
dist["dcat:accessService"] =
|
|
380
|
+
await this._urlTransformerComponent.addEncryptedQueryParamToUrl(dist["dcat:accessService"], "tenant", tenantId);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
354
384
|
/**
|
|
355
385
|
* Generate a deterministic catalog ID using canonical hash.
|
|
356
386
|
* @param datasets The datasets to include in the hash.
|
|
357
387
|
* @param participantId The participant ID to include in the hash.
|
|
358
388
|
* @returns A URN-formatted catalog ID.
|
|
389
|
+
* @internal
|
|
359
390
|
*/
|
|
360
391
|
generateCatalogId(datasets, participantId) {
|
|
361
392
|
const datasetIds = datasets
|
|
@@ -363,7 +394,7 @@ export class FederatedCatalogueService {
|
|
|
363
394
|
.filter(id => Is.stringValue(id))
|
|
364
395
|
.sort();
|
|
365
396
|
const canonicalContent = JsonHelper.canonicalize({
|
|
366
|
-
"@type":
|
|
397
|
+
"@type": DataspaceProtocolCatalogTypes.Catalog,
|
|
367
398
|
participantId,
|
|
368
399
|
datasets: datasetIds
|
|
369
400
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"federatedCatalogueService.js","sourceRoot":"","sources":["../../../src/services/federatedCatalogueService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tJsonHelper,\n\tNotFoundError,\n\tObjectHelper,\n\tUrl,\n\tUrn,\n\tValidation\n} from \"@twin.org/core\";\nimport { Blake2b } from \"@twin.org/crypto\";\nimport { JsonLdHelper, JsonLdProcessor } from \"@twin.org/data-json-ld\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport type { IFederatedCatalogueComponent } from \"@twin.org/federated-catalogue-models\";\nimport { FederatedCatalogueFilterFactory } from \"@twin.org/federated-catalogue-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolContexts,\n\tDataspaceProtocolDataTypes,\n\tDataspaceProtocolHelper,\n\ttype IDataspaceProtocolCatalog,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DublinCoreContexts, DublinCoreDataTypes } from \"@twin.org/standards-dublin-core\";\nimport { FoafDataTypes } from \"@twin.org/standards-foaf\";\nimport {\n\tDcatContexts,\n\tDcatDataTypes,\n\ttype DcatContextType,\n\ttype IDcatDataset\n} from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlContexts } from \"@twin.org/standards-w3c-odrl\";\nimport type { Dataset } from \"../entities/dataset.js\";\nimport type { IFederatedCatalogueServiceConstructorOptions } from \"../models/IFederatedCatalogueServiceConstructorOptions.js\";\nimport { transformToCatalogError } from \"../utils/catalogErrorUtils.js\";\nimport { datasetEntityToModel, datasetModelToEntity } from \"../utils/datasetConverters.js\";\n\n/**\n * Service for managing federated catalogue operations.\n * Provides Dataspace Protocol-compliant catalog endpoints for dataset registry and query.\n */\nexport class FederatedCatalogueService implements IFederatedCatalogueComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<FederatedCatalogueService>();\n\n\t/**\n\t * The logging component for the federated catalogue service.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The entity storage connector for datasets.\n\t * @internal\n\t */\n\tprivate readonly _datasetStorage: IEntityStorageConnector<Dataset>;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of FederatedCatalogueService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IFederatedCatalogueServiceConstructorOptions) {\n\t\tthis._logging = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\toptions?.loggingComponentType ?? \"logging\"\n\t\t);\n\n\t\tthis._datasetStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.datasetEntityStorageType ?? \"dataset\"\n\t\t);\n\n\t\t// Register JSON-LD redirects for offline processing\n\t\tDcatDataTypes.registerRedirects();\n\t\tDublinCoreDataTypes.registerRedirects();\n\t\tFoafDataTypes.registerRedirects();\n\t\tDataspaceProtocolDataTypes.registerRedirects();\n\n\t\t// Register DS Protocol data types for conformance checking\n\t\tDataspaceProtocolDataTypes.registerTypes();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn FederatedCatalogueService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the federated catalogue service.\n\t * @returns Nothing.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\t}\n\n\t/**\n\t * Retrieve a dataset by its unique identifier.\n\t * @param dataSetId The unique identifier of the dataset.\n\t * @returns The dataset if found, or a CatalogError if not found or an error occurs.\n\t */\n\tpublic async get(dataSetId: string): Promise<IDcatDataset | IDataspaceProtocolCatalogError> {\n\t\ttry {\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(dataSetId), dataSetId);\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"datasetRetrieve\",\n\t\t\t\tdata: { dataSetId }\n\t\t\t});\n\n\t\t\tconst datasetEntity = await this._datasetStorage.get(dataSetId);\n\n\t\t\tif (!datasetEntity) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"datasetNotFound\", dataSetId);\n\t\t\t}\n\n\t\t\tconst dataset = datasetEntityToModel(datasetEntity);\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedDataset = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(dataset)\n\t\t\t);\n\n\t\t\tconst structured = JsonLdHelper.toStructuredObject<IDcatDataset>(normalizedDataset);\n\t\t\treturn structured;\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Insert or update a dataset in the catalogue.\n\t * This method is internal and should not be exposed via REST endpoints.\n\t * @param dataSet The dataset to store.\n\t */\n\tpublic async set(dataSet: IDcatDataset): Promise<void> {\n\t\tGuards.object(FederatedCatalogueService.CLASS_NAME, nameof(dataSet), dataSet);\n\n\t\t// Normalize @id from dcterms:identifier if provided\n\t\tconst dataSetId = dataSet[\"@id\"] ?? dataSet[\"dcterms:identifier\"];\n\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(dataSetId), dataSetId);\n\n\t\t// Set @id if it was derived from dcterms:identifier\n\t\tif (!dataSet[\"@id\"] && dataSet[\"dcterms:identifier\"]) {\n\t\t\tdataSet[\"@id\"] = dataSetId;\n\t\t}\n\n\t\t// Validate @id is a valid URI (URN or URL) per DS Protocol\n\t\tconst isValidUrn = Urn.tryParseExact(dataSetId) !== undefined;\n\t\tconst isValidUrl = Url.tryParseExact(dataSetId) !== undefined;\n\t\tif (!isValidUrn && !isValidUrl) {\n\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetIdInvalidUri\", {\n\t\t\t\tdataSetId\n\t\t\t});\n\t\t}\n\n\t\t// Validate @type exists\n\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, \"@type\", dataSet[\"@type\"]);\n\n\t\t// Validate dcterms:publisher exists (required for multi-participant catalog)\n\t\t// The publisher is used to derive participantId when returning catalog query results\n\t\tconst publisher = dataSet[\"dcterms:publisher\"];\n\t\tif (!publisher) {\n\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetMissingPublisher\", {\n\t\t\t\tdataSetId\n\t\t\t});\n\t\t}\n\n\t\t// DS Protocol compliance validation\n\t\tconst validationFailures = await DataspaceProtocolHelper.validate(\n\t\t\tJsonLdHelper.toNodeObject(dataSet)\n\t\t);\n\n\t\tValidation.asValidationError(\n\t\t\tFederatedCatalogueService.CLASS_NAME,\n\t\t\t\"dataSet\",\n\t\t\tvalidationFailures\n\t\t);\n\n\t\t// Normalize dataset for storage using JSON-LD compaction\n\t\t// This ensures the dataset uses prefixed properties that entity storage expects\n\t\t// Entity storage schema uses DCAT-prefixed properties (dcat:distribution, not distribution)\n\t\t// Use a standard context with prefixes to ensure proper normalization\n\t\tconst storageContext: DcatContextType = {\n\t\t\tdcat: DcatContexts.Namespace,\n\t\t\tdcterms: DublinCoreContexts.NamespaceTerms,\n\t\t\todrl: OdrlContexts.Namespace\n\t\t};\n\t\tconst normalizedDataset = await JsonLdProcessor.compact(dataSet, storageContext);\n\n\t\tconst datasetEntity = datasetModelToEntity(\n\t\t\tnormalizedDataset,\n\t\t\tthis._nodeId ?? \"\",\n\t\t\tnew Date().toISOString()\n\t\t);\n\n\t\t// Skip update if entity content hasn't changed to avoid unnecessary sync\n\t\tconst existingEntity = await this._datasetStorage.get(dataSetId);\n\t\tif (existingEntity) {\n\t\t\tconst existingContent = ObjectHelper.omit(existingEntity, [\"nodeIdentity\", \"dateModified\"]);\n\t\t\tconst newContent = ObjectHelper.omit(datasetEntity, [\"nodeIdentity\", \"dateModified\"]);\n\n\t\t\tif (ObjectHelper.equal(existingContent, newContent, false)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"datasetSet\",\n\t\t\tdata: { dataSetId }\n\t\t});\n\n\t\tconst allIndexes: { [key: string]: unknown } = {};\n\t\tconst filterNames = FederatedCatalogueFilterFactory.names();\n\t\tfor (const filterType of filterNames) {\n\t\t\ttry {\n\t\t\t\tconst filter = FederatedCatalogueFilterFactory.get(filterType);\n\t\t\t\tconst filterIndexes = await filter.createIndex(dataSet);\n\n\t\t\t\tallIndexes[filterType] = filterIndexes;\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"filterIndexPersisted\",\n\t\t\t\t\tdata: { dataSetId, filterType, indexCount: Object.keys(filterIndexes).length }\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"filterIndexCreationFailed\",\n\t\t\t\t\tdata: { dataSetId, filterType },\n\t\t\t\t\terror: BaseError.fromError(error)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait this._datasetStorage.set(datasetEntity);\n\t}\n\n\t/**\n\t * Execute a query against the catalogue using registered filter plugins.\n\t * Returns a DS Protocol compliant Catalog object with participantId.\n\t *\n\t * The root catalog's participantId is the requesting participant (from context).\n\t * Own datasets (matching requestingParticipantId) go directly in root dataset[].\n\t * Other participants' datasets are grouped in nested catalog[] entries.\n\t *\n\t * For anonymous requests (no context), uses the first publisher found as fallback.\n\t * Returns CatalogError 404 when no datasets exist, CatalogError 400 for invalid requests.\n\t *\n\t * @param filter The filter criteria containing @type, optional cursor and limit properties.\n\t * @param cursor Optional cursor for pagination.\n\t * @param limit Optional limit for pagination.\n\t * @returns Complete IDataspaceProtocolCatalog with @context, @id, @type, participantId, dataset/catalog,\n\t * or CatalogError if validation fails or an error occurs.\n\t */\n\tpublic async query(\n\t\tfilter?: unknown[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\tresult: IDataspaceProtocolCatalog | IDataspaceProtocolCatalogError;\n\t\tcursor?: string;\n\t}> {\n\t\ttry {\n\t\t\tGuards.array(FederatedCatalogueService.CLASS_NAME, nameof(filter), filter);\n\n\t\t\tlet datasets: IDcatDataset[];\n\t\t\tlet resultCursor: string | undefined;\n\n\t\t\tif (!Is.empty(filter) && !Is.array(filter)) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"filterMustBeArray\");\n\t\t\t}\n\n\t\t\tif (!filter || filter.length === 0) {\n\t\t\t\tconst result = await this._datasetStorage.query();\n\t\t\t\tdatasets = result.entities.map(entity => datasetEntityToModel(entity));\n\t\t\t} else if (filter.length > 1) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"multipleFiltersNotSupported\");\n\t\t\t} else {\n\t\t\t\tconst singleFilter = filter[0] as { \"@type\"?: string } | undefined;\n\n\t\t\t\tconst filterType = singleFilter?.[\"@type\"];\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"catalogQuery\",\n\t\t\t\t\tdata: { filterType: filterType ?? \"\", cursor: cursor ?? \"\", limit: limit ?? \"\" }\n\t\t\t\t});\n\n\t\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(filterType), filterType);\n\n\t\t\t\tconst selectedFilter = FederatedCatalogueFilterFactory.get(filterType);\n\n\t\t\t\tObjectHelper.propertyDelete(filter, \"@type\");\n\t\t\t\tconst result = await selectedFilter.query(filter, cursor, limit);\n\n\t\t\t\tdatasets = result.datasets.map(d => datasetEntityToModel(d));\n\t\t\t\tresultCursor = result.cursor;\n\t\t\t}\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"catalogQueryComplete\",\n\t\t\t\tdata: { resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) }\n\t\t\t});\n\n\t\t\t// Return CatalogError 404 when no datasets exist\n\t\t\tif (datasets.length === 0) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"noDatasetsFound\");\n\t\t\t}\n\n\t\t\t// Get requesting participant from context (organizationId maps to participantId)\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tlet requestingParticipantId = contextIds?.[ContextIdKeys.Organization];\n\n\t\t\t// Group datasets by dcterms:publisher (participantId)\n\t\t\tconst datasetsByParticipant = new Map<string, IDcatDataset[]>();\n\t\t\tfor (const dataset of datasets) {\n\t\t\t\tconst publisher = this.extractPublisher(dataset);\n\t\t\t\tconst participantId = publisher ?? \"unknown\";\n\t\t\t\tconst existing = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\texisting.push(dataset);\n\t\t\t\tdatasetsByParticipant.set(participantId, existing);\n\t\t\t}\n\n\t\t\tconst participantIds = [...datasetsByParticipant.keys()];\n\n\t\t\t// For anonymous requests (no context), use first publisher as fallback\n\t\t\tif (!Is.stringValue(requestingParticipantId)) {\n\t\t\t\trequestingParticipantId = participantIds[0] ?? \"unknown\";\n\t\t\t}\n\n\t\t\t// Separate own datasets from other participants' datasets\n\t\t\tconst ownDatasets = datasetsByParticipant.get(requestingParticipantId) ?? [];\n\t\t\tconst otherParticipantIds = participantIds.filter(id => id !== requestingParticipantId);\n\n\t\t\tlet catalog: IDataspaceProtocolCatalog;\n\n\t\t\tif (otherParticipantIds.length === 0) {\n\t\t\t\t// Only own datasets (or all datasets belong to requesting participant)\n\t\t\t\tconst catalogId = this.generateCatalogId(ownDatasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": catalogId,\n\t\t\t\t\t\"@type\": \"Catalog\",\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Mixed: own datasets at root level, others in nested catalogs\n\t\t\t\tconst nestedCatalogs: IDataspaceProtocolCatalog[] = [];\n\n\t\t\t\tfor (const participantId of otherParticipantIds) {\n\t\t\t\t\tconst participantDatasets = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\t\tconst subCatalogId = this.generateCatalogId(participantDatasets, participantId);\n\n\t\t\t\t\tconst subCatalog: IDataspaceProtocolCatalog = {\n\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\"@id\": subCatalogId,\n\t\t\t\t\t\t\"@type\": \"Catalog\",\n\t\t\t\t\t\tparticipantId,\n\t\t\t\t\t\tdataset: participantDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t\t};\n\n\t\t\t\t\tnestedCatalogs.push(subCatalog);\n\t\t\t\t}\n\n\t\t\t\t// Root catalog contains own datasets and nested catalogs for others\n\t\t\t\tconst rootCatalogId = this.generateCatalogId(datasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": rootCatalogId,\n\t\t\t\t\t\"@type\": \"Catalog\",\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"],\n\t\t\t\t\tcatalog: nestedCatalogs\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedCatalog = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(catalog)\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tresult: JsonLdHelper.toStructuredObject(normalizedCatalog),\n\t\t\t\tcursor: resultCursor\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tresult: transformToCatalogError(error)\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Remove a dataset from the catalogue by its unique identifier.\n\t * Indexes are automatically removed as they are stored with the dataset.\n\t * @param dataSetId The unique identifier of the dataset to remove.\n\t */\n\tpublic async remove(dataSetId: string): Promise<void> {\n\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(dataSetId), dataSetId);\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"datasetRemove\",\n\t\t\tdata: { dataSetId }\n\t\t});\n\n\t\tawait this._datasetStorage.remove(dataSetId);\n\t}\n\n\t/**\n\t * Extract publisher from dataset.\n\t * Publisher can be a string or an IFoafAgent object with @id.\n\t * @param dataset The dataset to extract publisher from.\n\t * @returns The publisher string or undefined if not found.\n\t */\n\tprivate extractPublisher(dataset: IDcatDataset): string | undefined {\n\t\tconst publisher = dataset[\"dcterms:publisher\"];\n\n\t\t// Handle case where publisher is an object with @id (IFoafAgent)\n\t\tif (publisher && typeof publisher === \"object\") {\n\t\t\tconst publisherId = (publisher as { \"@id\"?: string })[\"@id\"];\n\t\t\treturn Is.stringValue(publisherId) ? publisherId : undefined;\n\t\t}\n\n\t\treturn Is.stringValue(publisher) ? publisher : undefined;\n\t}\n\n\t/**\n\t * Generate a deterministic catalog ID using canonical hash.\n\t * @param datasets The datasets to include in the hash.\n\t * @param participantId The participant ID to include in the hash.\n\t * @returns A URN-formatted catalog ID.\n\t */\n\tprivate generateCatalogId(datasets: IDcatDataset[], participantId: string): string {\n\t\tconst datasetIds = datasets\n\t\t\t.map(d => d[\"@id\"])\n\t\t\t.filter(id => Is.stringValue(id))\n\t\t\t.sort();\n\n\t\tconst canonicalContent = JsonHelper.canonicalize({\n\t\t\t\"@type\": \"Catalog\",\n\t\t\tparticipantId,\n\t\t\tdatasets: datasetIds\n\t\t});\n\n\t\tconst canonicalBytes = Converter.utf8ToBytes(canonicalContent);\n\t\tconst catalogHash = Converter.bytesToHex(Blake2b.sum256(canonicalBytes));\n\t\treturn `urn:x-catalog:${catalogHash}`;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"federatedCatalogueService.js","sourceRoot":"","sources":["../../../src/services/federatedCatalogueService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IUrlTransformerComponent } from \"@twin.org/api-models\";\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tArrayHelper,\n\tBaseError,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tJsonHelper,\n\tNotFoundError,\n\tObjectHelper,\n\tUrl,\n\tUrn,\n\tValidation\n} from \"@twin.org/core\";\nimport { Blake2b } from \"@twin.org/crypto\";\nimport { JsonLdHelper, JsonLdProcessor } from \"@twin.org/data-json-ld\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport type { IFederatedCatalogueComponent } from \"@twin.org/federated-catalogue-models\";\nimport { FederatedCatalogueFilterFactory } from \"@twin.org/federated-catalogue-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts,\n\tDataspaceProtocolDataTypes,\n\tDataspaceProtocolHelper,\n\ttype IDataspaceProtocolCatalog,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DublinCoreContexts, DublinCoreDataTypes } from \"@twin.org/standards-dublin-core\";\nimport { FoafDataTypes } from \"@twin.org/standards-foaf\";\nimport {\n\tDcatContexts,\n\tDcatDataTypes,\n\ttype DcatContextType,\n\ttype IDcatDataset\n} from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlContexts } from \"@twin.org/standards-w3c-odrl\";\nimport type { Dataset } from \"../entities/dataset.js\";\nimport type { IFederatedCatalogueServiceConstructorOptions } from \"../models/IFederatedCatalogueServiceConstructorOptions.js\";\nimport { transformToCatalogError } from \"../utils/catalogErrorUtils.js\";\nimport { datasetEntityToModel, datasetModelToEntity } from \"../utils/datasetConverters.js\";\n\n/**\n * Service for managing federated catalogue operations.\n * Provides Dataspace Protocol-compliant catalog endpoints for dataset registry and query.\n */\nexport class FederatedCatalogueService implements IFederatedCatalogueComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<FederatedCatalogueService>();\n\n\t/**\n\t * The logging component for the federated catalogue service.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The entity storage connector for datasets.\n\t * @internal\n\t */\n\tprivate readonly _datasetStorage: IEntityStorageConnector<Dataset>;\n\n\t/**\n\t * The URL transformer component for encrypting tenant routing tokens into distribution URLs.\n\t * @internal\n\t */\n\tprivate readonly _urlTransformerComponent: IUrlTransformerComponent;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of FederatedCatalogueService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IFederatedCatalogueServiceConstructorOptions) {\n\t\tthis._logging = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\toptions?.loggingComponentType ?? \"logging\"\n\t\t);\n\n\t\tthis._datasetStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.datasetEntityStorageType ?? \"dataset\"\n\t\t);\n\n\t\tthis._urlTransformerComponent = ComponentFactory.get<IUrlTransformerComponent>(\n\t\t\toptions?.urlTransformerComponentType ?? \"url-transformer\"\n\t\t);\n\n\t\t// Register JSON-LD redirects for offline processing\n\t\tDcatDataTypes.registerRedirects();\n\t\tDublinCoreDataTypes.registerRedirects();\n\t\tFoafDataTypes.registerRedirects();\n\t\tDataspaceProtocolDataTypes.registerRedirects();\n\n\t\t// Register DS Protocol data types for conformance checking\n\t\tDataspaceProtocolDataTypes.registerTypes();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn FederatedCatalogueService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the federated catalogue service.\n\t * @returns Nothing.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\t}\n\n\t/**\n\t * Retrieve a dataset by its unique identifier.\n\t * @param dataSetId The unique identifier of the dataset.\n\t * @returns The dataset if found, or a CatalogError if not found or an error occurs.\n\t */\n\tpublic async get(dataSetId: string): Promise<IDcatDataset | IDataspaceProtocolCatalogError> {\n\t\ttry {\n\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(dataSetId), dataSetId);\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"datasetRetrieve\",\n\t\t\t\tdata: { dataSetId }\n\t\t\t});\n\n\t\t\tconst datasetEntity = await this._datasetStorage.get(dataSetId);\n\n\t\t\tif (!datasetEntity) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"datasetNotFound\", dataSetId);\n\t\t\t}\n\n\t\t\tconst dataset = datasetEntityToModel(datasetEntity);\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedDataset = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(dataset)\n\t\t\t);\n\n\t\t\tconst structured = JsonLdHelper.toStructuredObject<IDcatDataset>(normalizedDataset);\n\t\t\treturn structured;\n\t\t} catch (error) {\n\t\t\treturn transformToCatalogError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Insert or update a dataset in the catalogue.\n\t * This method is internal and should not be exposed via REST endpoints.\n\t * @param dataSet The dataset to store.\n\t */\n\tpublic async set(dataSet: IDcatDataset): Promise<void> {\n\t\tGuards.object(FederatedCatalogueService.CLASS_NAME, nameof(dataSet), dataSet);\n\n\t\t// Normalize @id from dcterms:identifier if provided\n\t\tconst dataSetId = dataSet[\"@id\"] ?? dataSet[\"dcterms:identifier\"];\n\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(dataSetId), dataSetId);\n\n\t\t// Set @id if it was derived from dcterms:identifier\n\t\tif (Is.empty(dataSet[\"@id\"]) && !Is.empty(dataSet[\"dcterms:identifier\"])) {\n\t\t\tdataSet[\"@id\"] = dataSetId;\n\t\t}\n\n\t\t// Validate @id is a valid URI (URN or URL) per DS Protocol\n\t\tconst isValidUrn = !Is.empty(Urn.tryParseExact(dataSetId));\n\t\tconst isValidUrl = !Is.empty(Url.tryParseExact(dataSetId));\n\t\tif (!isValidUrn && !isValidUrl) {\n\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetIdInvalidUri\", {\n\t\t\t\tdataSetId\n\t\t\t});\n\t\t}\n\n\t\t// Validate @type exists\n\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, \"@type\", dataSet[\"@type\"]);\n\n\t\t// Validate dcterms:publisher exists (required for multi-participant catalog)\n\t\t// The publisher is used to derive participantId when returning catalog query results\n\t\tconst publisher = dataSet[\"dcterms:publisher\"];\n\t\tif (Is.empty(publisher)) {\n\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetMissingPublisher\", {\n\t\t\t\tdataSetId\n\t\t\t});\n\t\t}\n\n\t\t// DS Protocol compliance validation\n\t\tconst validationFailures = await DataspaceProtocolHelper.validate(\n\t\t\tJsonLdHelper.toNodeObject(dataSet)\n\t\t);\n\n\t\tValidation.asValidationError(\n\t\t\tFederatedCatalogueService.CLASS_NAME,\n\t\t\t\"dataSet\",\n\t\t\tvalidationFailures\n\t\t);\n\n\t\t// Normalize dataset for storage using JSON-LD compaction\n\t\t// This ensures the dataset uses prefixed properties that entity storage expects\n\t\t// Entity storage schema uses DCAT-prefixed properties (dcat:distribution, not distribution)\n\t\t// Use a standard context with prefixes to ensure proper normalization\n\t\tconst storageContext: DcatContextType = {\n\t\t\tdcat: DcatContexts.Namespace,\n\t\t\tdcterms: DublinCoreContexts.NamespaceTerms,\n\t\t\todrl: OdrlContexts.Namespace\n\t\t};\n\t\tconst normalizedDataset = await JsonLdProcessor.compact(dataSet, storageContext);\n\n\t\tconst datasetEntity = datasetModelToEntity(\n\t\t\tnormalizedDataset,\n\t\t\tthis._nodeId ?? \"\",\n\t\t\tnew Date().toISOString()\n\t\t);\n\n\t\t// Capture the publishing tenant from the current request context.\n\t\tconst setContextIds = await ContextIdStore.getContextIds();\n\t\tconst setTenantId = setContextIds?.[ContextIdKeys.Tenant];\n\t\tif (Is.stringValue(setTenantId)) {\n\t\t\tdatasetEntity.tenantId = setTenantId;\n\n\t\t\t// Bake the publishing tenant token into distribution accessService URLs\n\t\t\tawait this.bakeTenantTokenIntoDistributions(datasetEntity, setTenantId);\n\t\t}\n\n\t\t// Skip update if entity content hasn't changed to avoid unnecessary sync\n\t\tconst existingEntity = await this._datasetStorage.get(dataSetId);\n\t\tif (existingEntity) {\n\t\t\tconst existingContent = ObjectHelper.omit(existingEntity, [\"nodeIdentity\", \"dateModified\"]);\n\t\t\tconst newContent = ObjectHelper.omit(datasetEntity, [\"nodeIdentity\", \"dateModified\"]);\n\n\t\t\tif (ObjectHelper.equal(existingContent, newContent, false)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"datasetSet\",\n\t\t\tdata: { dataSetId }\n\t\t});\n\n\t\tconst allIndexes: { [key: string]: unknown } = {};\n\t\tconst filterNames = FederatedCatalogueFilterFactory.names();\n\t\tfor (const filterType of filterNames) {\n\t\t\ttry {\n\t\t\t\tconst filter = FederatedCatalogueFilterFactory.get(filterType);\n\t\t\t\tconst filterIndexes = await filter.createIndex(dataSet);\n\n\t\t\t\tallIndexes[filterType] = filterIndexes;\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"filterIndexPersisted\",\n\t\t\t\t\tdata: { dataSetId, filterType, indexCount: Object.keys(filterIndexes).length }\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"filterIndexCreationFailed\",\n\t\t\t\t\tdata: { dataSetId, filterType },\n\t\t\t\t\terror: BaseError.fromError(error)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait this._datasetStorage.set(datasetEntity);\n\t}\n\n\t/**\n\t * Execute a query against the catalogue using registered filter plugins.\n\t * Returns a DS Protocol compliant Catalog object with participantId.\n\t *\n\t * The root catalog's participantId is the requesting participant (from context).\n\t * Own datasets (matching requestingParticipantId) go directly in root dataset[].\n\t * Other participants' datasets are grouped in nested catalog[] entries.\n\t *\n\t * For anonymous requests (no context), uses the first publisher found as fallback.\n\t * Returns CatalogError 404 when no datasets exist, CatalogError 400 for invalid requests.\n\t *\n\t * @param filter The filter criteria containing @type, optional cursor and limit properties.\n\t * @param cursor Optional cursor for pagination.\n\t * @param limit Optional limit for pagination.\n\t * @returns Complete IDataspaceProtocolCatalog with @context, @id, @type, participantId, dataset/catalog,\n\t * or CatalogError if validation fails or an error occurs.\n\t */\n\tpublic async query(\n\t\tfilter?: unknown[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\tresult: IDataspaceProtocolCatalog | IDataspaceProtocolCatalogError;\n\t\tcursor?: string;\n\t}> {\n\t\ttry {\n\t\t\tGuards.array(FederatedCatalogueService.CLASS_NAME, nameof(filter), filter);\n\n\t\t\tlet datasets: IDcatDataset[];\n\t\t\tlet resultCursor: string | undefined;\n\n\t\t\tif (!Is.empty(filter) && !Is.array(filter)) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"filterMustBeArray\");\n\t\t\t}\n\n\t\t\tif (!Is.arrayValue(filter)) {\n\t\t\t\tconst result = await this._datasetStorage.query();\n\t\t\t\tdatasets = result.entities.map(entity => datasetEntityToModel(entity));\n\t\t\t} else if (filter.length > 1) {\n\t\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"multipleFiltersNotSupported\");\n\t\t\t} else {\n\t\t\t\tconst singleFilter = filter[0] as { \"@type\"?: string } | undefined;\n\n\t\t\t\tconst filterType = singleFilter?.[\"@type\"];\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"catalogQuery\",\n\t\t\t\t\tdata: { filterType: filterType ?? \"\", cursor: cursor ?? \"\", limit: limit ?? \"\" }\n\t\t\t\t});\n\n\t\t\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(filterType), filterType);\n\n\t\t\t\tconst selectedFilter = FederatedCatalogueFilterFactory.get(filterType);\n\n\t\t\t\tObjectHelper.propertyDelete(filter, \"@type\");\n\t\t\t\tconst result = await selectedFilter.query(filter, cursor, limit);\n\n\t\t\t\tdatasets = result.datasets.map(d => datasetEntityToModel(d));\n\t\t\t\tresultCursor = result.cursor;\n\t\t\t}\n\n\t\t\tawait this._logging?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"catalogQueryComplete\",\n\t\t\t\tdata: { resultCount: datasets.length, hasMore: Is.stringValue(resultCursor) }\n\t\t\t});\n\n\t\t\t// Return CatalogError 404 when no datasets exist\n\t\t\tif (!Is.arrayValue(datasets)) {\n\t\t\t\tthrow new NotFoundError(FederatedCatalogueService.CLASS_NAME, \"noDatasetsFound\");\n\t\t\t}\n\n\t\t\t// Get requesting participant from context (organizationId maps to participantId)\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tlet requestingParticipantId = contextIds?.[ContextIdKeys.Organization];\n\n\t\t\t// Group datasets by dcterms:publisher (participantId)\n\t\t\tconst datasetsByParticipant = new Map<string, IDcatDataset[]>();\n\t\t\tfor (const dataset of datasets) {\n\t\t\t\tconst publisher = this.extractPublisher(dataset);\n\t\t\t\tconst participantId = publisher ?? \"unknown\";\n\t\t\t\tconst existing = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\texisting.push(dataset);\n\t\t\t\tdatasetsByParticipant.set(participantId, existing);\n\t\t\t}\n\n\t\t\tconst participantIds = [...datasetsByParticipant.keys()];\n\n\t\t\t// For anonymous requests (no context), use first publisher as fallback\n\t\t\tif (!Is.stringValue(requestingParticipantId)) {\n\t\t\t\trequestingParticipantId = participantIds[0] ?? \"unknown\";\n\t\t\t}\n\n\t\t\t// Separate own datasets from other participants' datasets\n\t\t\tconst ownDatasets = datasetsByParticipant.get(requestingParticipantId) ?? [];\n\t\t\tconst otherParticipantIds = participantIds.filter(id => id !== requestingParticipantId);\n\n\t\t\tlet catalog: IDataspaceProtocolCatalog;\n\n\t\t\tif (!Is.arrayValue(otherParticipantIds)) {\n\t\t\t\t// Only own datasets (or all datasets belong to requesting participant)\n\t\t\t\tconst catalogId = this.generateCatalogId(ownDatasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": catalogId,\n\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Mixed: own datasets at root level, others in nested catalogs\n\t\t\t\tconst nestedCatalogs: IDataspaceProtocolCatalog[] = [];\n\n\t\t\t\tfor (const participantId of otherParticipantIds) {\n\t\t\t\t\tconst participantDatasets = datasetsByParticipant.get(participantId) ?? [];\n\t\t\t\t\tconst subCatalogId = this.generateCatalogId(participantDatasets, participantId);\n\n\t\t\t\t\tconst subCatalog: IDataspaceProtocolCatalog = {\n\t\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\t\"@id\": subCatalogId,\n\t\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\t\tparticipantId,\n\t\t\t\t\t\tdataset: participantDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"]\n\t\t\t\t\t};\n\n\t\t\t\t\tnestedCatalogs.push(subCatalog);\n\t\t\t\t}\n\n\t\t\t\t// Root catalog contains own datasets and nested catalogs for others\n\t\t\t\tconst rootCatalogId = this.generateCatalogId(datasets, requestingParticipantId);\n\n\t\t\t\tcatalog = {\n\t\t\t\t\t\"@context\": [DataspaceProtocolContexts.Context],\n\t\t\t\t\t\"@id\": rootCatalogId,\n\t\t\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\t\t\tparticipantId: requestingParticipantId,\n\t\t\t\t\tdataset: ownDatasets as unknown as IDataspaceProtocolCatalog[\"dataset\"],\n\t\t\t\t\tcatalog: nestedCatalogs\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Normalize to DS Protocol compliant format\n\t\t\t// This ensures the payload matches exactly what the DS Protocol mandates\n\t\t\tconst normalizedCatalog = await DataspaceProtocolHelper.normalize(\n\t\t\t\tJsonLdHelper.toNodeObject(catalog)\n\t\t\t);\n\n\t\t\tconst structuredResult: IDataspaceProtocolCatalog =\n\t\t\t\tJsonLdHelper.toStructuredObject(normalizedCatalog);\n\n\t\t\treturn {\n\t\t\t\tresult: structuredResult,\n\t\t\t\tcursor: resultCursor\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tresult: transformToCatalogError(error)\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Remove a dataset from the catalogue by its unique identifier.\n\t * Indexes are automatically removed as they are stored with the dataset.\n\t * @param dataSetId The unique identifier of the dataset to remove.\n\t */\n\tpublic async remove(dataSetId: string): Promise<void> {\n\t\tGuards.stringValue(FederatedCatalogueService.CLASS_NAME, nameof(dataSetId), dataSetId);\n\n\t\tawait this._logging?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: FederatedCatalogueService.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"datasetRemove\",\n\t\t\tdata: { dataSetId }\n\t\t});\n\n\t\tawait this._datasetStorage.remove(dataSetId);\n\t}\n\n\t/**\n\t * Extract publisher from dataset.\n\t * Publisher can be a string or an IFoafAgent object with @id.\n\t * @param dataset The dataset to extract publisher from.\n\t * @returns The publisher string or undefined if not found.\n\t */\n\tprivate extractPublisher(dataset: IDcatDataset): string | undefined {\n\t\tconst publisher = dataset[\"dcterms:publisher\"];\n\n\t\t// Handle case where publisher is an object with @id (IFoafAgent)\n\t\tif (Is.object<{ \"@id\"?: string }>(publisher)) {\n\t\t\tconst publisherId = publisher[\"@id\"];\n\t\t\treturn Is.stringValue(publisherId) ? publisherId : undefined;\n\t\t}\n\n\t\treturn Is.stringValue(publisher) ? publisher : undefined;\n\t}\n\n\t/**\n\t * Bake the publishing tenant token into each distribution's accessService URL.\n\t * @param entity The dataset entity to modify in place.\n\t * @param tenantId The publishing tenant id to bake.\n\t * @internal\n\t */\n\tprivate async bakeTenantTokenIntoDistributions(entity: Dataset, tenantId: string): Promise<void> {\n\t\t// Storage uses the prefixed JSON-LD key \"dcat:accessService\" (not the unprefixed\n\t\t// \"accessService\" that appears in compacted query responses).\n\t\tconst distributions = ArrayHelper.fromObjectOrArray(entity[\"dcat:distribution\"]) ?? [];\n\t\tfor (const dist of distributions) {\n\t\t\tif (Is.stringValue(dist?.[\"dcat:accessService\"])) {\n\t\t\t\tdist[\"dcat:accessService\"] =\n\t\t\t\t\tawait this._urlTransformerComponent.addEncryptedQueryParamToUrl(\n\t\t\t\t\t\tdist[\"dcat:accessService\"],\n\t\t\t\t\t\t\"tenant\",\n\t\t\t\t\t\ttenantId\n\t\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Generate a deterministic catalog ID using canonical hash.\n\t * @param datasets The datasets to include in the hash.\n\t * @param participantId The participant ID to include in the hash.\n\t * @returns A URN-formatted catalog ID.\n\t * @internal\n\t */\n\tprivate generateCatalogId(datasets: IDcatDataset[], participantId: string): string {\n\t\tconst datasetIds = datasets\n\t\t\t.map(d => d[\"@id\"])\n\t\t\t.filter(id => Is.stringValue(id))\n\t\t\t.sort();\n\n\t\tconst canonicalContent = JsonHelper.canonicalize({\n\t\t\t\"@type\": DataspaceProtocolCatalogTypes.Catalog,\n\t\t\tparticipantId,\n\t\t\tdatasets: datasetIds\n\t\t});\n\n\t\tconst canonicalBytes = Converter.utf8ToBytes(canonicalContent);\n\t\tconst catalogHash = Converter.bytesToHex(Blake2b.sum256(canonicalBytes));\n\t\treturn `urn:x-catalog:${catalogHash}`;\n\t}\n}\n"]}
|
|
@@ -17,6 +17,10 @@ export declare class Dataset {
|
|
|
17
17
|
* The date the entity was last modified (required for sync).
|
|
18
18
|
*/
|
|
19
19
|
dateModified: string;
|
|
20
|
+
/**
|
|
21
|
+
* The tenant id of the publisher captured at write time.
|
|
22
|
+
*/
|
|
23
|
+
tenantId?: string;
|
|
20
24
|
/**
|
|
21
25
|
* The JSON-LD context for the dataset.
|
|
22
26
|
*/
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./entities/dataset.js";
|
|
2
2
|
export * from "./federatedCatalogueRoutes.js";
|
|
3
|
+
export * from "./models/IFederatedCatalogueServiceConfig.js";
|
|
3
4
|
export * from "./models/IFederatedCatalogueServiceConstructorOptions.js";
|
|
4
5
|
export * from "./restEntryPoints.js";
|
|
5
6
|
export * from "./schema.js";
|