@twin.org/federated-catalogue-service 0.0.3-next.5 → 0.0.3-next.7
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/federatedCatalogueRoutes.js +47 -39
- 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/services/federatedCatalogueService.js +25 -46
- package/dist/es/services/federatedCatalogueService.js.map +1 -1
- package/dist/es/utils/catalogErrorUtils.js +49 -0
- package/dist/es/utils/catalogErrorUtils.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/services/federatedCatalogueService.d.ts +1 -8
- package/dist/types/utils/catalogErrorUtils.d.ts +15 -0
- package/docs/changelog.md +28 -0
- package/docs/reference/classes/FederatedCatalogueService.md +4 -4
- package/docs/reference/functions/transformErrorToStatusCode.md +19 -0
- package/docs/reference/functions/transformToCatalogError.md +20 -0
- package/docs/reference/index.md +2 -0
- package/locales/en.json +1 -0
- package/package.json +2 -2
|
@@ -2,6 +2,7 @@ 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
4
|
import { HeaderHelper, HeaderTypes, HttpStatusCode } from "@twin.org/web";
|
|
5
|
+
import { transformErrorToStatusCode, transformToCatalogError } from "./utils/catalogErrorUtils.js";
|
|
5
6
|
/**
|
|
6
7
|
* The source used when communicating about these routes.
|
|
7
8
|
*/
|
|
@@ -36,7 +37,7 @@ export function generateRestRoutesFederatedCatalogue(baseRouteName, componentNam
|
|
|
36
37
|
id: "catalogRequestExample",
|
|
37
38
|
request: {
|
|
38
39
|
body: {
|
|
39
|
-
"@context": [DataspaceProtocolContexts.
|
|
40
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
40
41
|
"@type": DataspaceProtocolCatalogTypes.CatalogRequestMessage,
|
|
41
42
|
filter: [
|
|
42
43
|
{
|
|
@@ -50,7 +51,7 @@ export function generateRestRoutesFederatedCatalogue(baseRouteName, componentNam
|
|
|
50
51
|
id: "catalogRequestNoFilterExample",
|
|
51
52
|
request: {
|
|
52
53
|
body: {
|
|
53
|
-
"@context": [DataspaceProtocolContexts.
|
|
54
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
54
55
|
"@type": DataspaceProtocolCatalogTypes.CatalogRequestMessage
|
|
55
56
|
}
|
|
56
57
|
}
|
|
@@ -65,13 +66,13 @@ export function generateRestRoutesFederatedCatalogue(baseRouteName, componentNam
|
|
|
65
66
|
id: "catalogRequestResponseExample",
|
|
66
67
|
response: {
|
|
67
68
|
body: {
|
|
68
|
-
"@context": [DataspaceProtocolContexts.
|
|
69
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
69
70
|
"@id": "urn:x-catalog:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2",
|
|
70
71
|
"@type": "Catalog",
|
|
71
72
|
participantId: "did:example:node-identity-123",
|
|
72
73
|
dataset: [
|
|
73
74
|
{
|
|
74
|
-
"@context": [DataspaceProtocolContexts.
|
|
75
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
75
76
|
"@id": "urn:uuid:dataset-123",
|
|
76
77
|
"@type": "Dataset",
|
|
77
78
|
title: "Energy Consumption Data",
|
|
@@ -113,7 +114,7 @@ export function generateRestRoutesFederatedCatalogue(baseRouteName, componentNam
|
|
|
113
114
|
id: "getDatasetResponseExample",
|
|
114
115
|
response: {
|
|
115
116
|
body: {
|
|
116
|
-
"@context": DataspaceProtocolContexts.
|
|
117
|
+
"@context": DataspaceProtocolContexts.Context,
|
|
117
118
|
"@id": "urn:uuid:dataset-123",
|
|
118
119
|
"@type": DcatClasses.Dataset,
|
|
119
120
|
"dcterms:title": "Energy Consumption Data",
|
|
@@ -127,17 +128,6 @@ export function generateRestRoutesFederatedCatalogue(baseRouteName, componentNam
|
|
|
127
128
|
};
|
|
128
129
|
return [catalogRequestRoute, getDatasetRoute];
|
|
129
130
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Map the DS Protocol result to an HTTP status code.
|
|
132
|
-
* @param result The result to map.
|
|
133
|
-
* @returns The mapped status code or undefined if no mapping was found or not an error.
|
|
134
|
-
*/
|
|
135
|
-
function mapCatalogError(result) {
|
|
136
|
-
if (result?.["@type"] === DataspaceProtocolCatalogTypes.CatalogError && Is.objectValue(result)) {
|
|
137
|
-
return Coerce.integer(result.code) ?? HttpStatusCode.badRequest;
|
|
138
|
-
}
|
|
139
|
-
return undefined;
|
|
140
|
-
}
|
|
141
131
|
/**
|
|
142
132
|
* Handle the catalog request operation.
|
|
143
133
|
* @param httpRequestContext The request context for the operation.
|
|
@@ -146,20 +136,29 @@ function mapCatalogError(result) {
|
|
|
146
136
|
* @returns The response.
|
|
147
137
|
*/
|
|
148
138
|
async function catalogRequest(httpRequestContext, componentName, request) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
139
|
+
try {
|
|
140
|
+
Guards.object(ROUTES_SOURCE, "request", request);
|
|
141
|
+
Guards.object(ROUTES_SOURCE, "request.body", request.body);
|
|
142
|
+
Guards.stringValue(ROUTES_SOURCE, "@type", request.body["@type"]);
|
|
143
|
+
const component = ComponentFactory.get(componentName);
|
|
144
|
+
const queryResult = await component.query(request.body.filter, request.query?.cursor, Coerce.integer(request.query?.limit));
|
|
145
|
+
return {
|
|
146
|
+
statusCode: transformErrorToStatusCode(queryResult.result),
|
|
147
|
+
body: queryResult.result,
|
|
148
|
+
headers: Is.stringValue(queryResult.cursor) && Is.stringValue(httpRequestContext.serverRequest?.url)
|
|
149
|
+
? {
|
|
150
|
+
[HeaderTypes.Link]: HeaderHelper.createLinkHeader(httpRequestContext.serverRequest.url, { cursor: queryResult.cursor }, "next")
|
|
151
|
+
}
|
|
152
|
+
: undefined
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const catalogError = transformToCatalogError(error);
|
|
157
|
+
return {
|
|
158
|
+
statusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,
|
|
159
|
+
body: catalogError
|
|
160
|
+
};
|
|
161
|
+
}
|
|
163
162
|
}
|
|
164
163
|
/**
|
|
165
164
|
* Handle the get dataset operation.
|
|
@@ -169,14 +168,23 @@ async function catalogRequest(httpRequestContext, componentName, request) {
|
|
|
169
168
|
* @returns The response.
|
|
170
169
|
*/
|
|
171
170
|
async function getDataset(httpRequestContext, componentName, request) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
171
|
+
try {
|
|
172
|
+
Guards.object(ROUTES_SOURCE, "request", request);
|
|
173
|
+
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
174
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.datasetId", request.pathParams.datasetId);
|
|
175
|
+
const component = ComponentFactory.get(componentName);
|
|
176
|
+
const result = await component.get(request.pathParams.datasetId);
|
|
177
|
+
return {
|
|
178
|
+
statusCode: transformErrorToStatusCode(result),
|
|
179
|
+
body: result
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
const catalogError = transformToCatalogError(error);
|
|
184
|
+
return {
|
|
185
|
+
statusCode: transformErrorToStatusCode(catalogError) ?? HttpStatusCode.badRequest,
|
|
186
|
+
body: catalogError
|
|
187
|
+
};
|
|
188
|
+
}
|
|
181
189
|
}
|
|
182
190
|
//# sourceMappingURL=federatedCatalogueRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"federatedCatalogueRoutes.js","sourceRoot":"","sources":["../../src/federatedCatalogueRoutes.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAStE,OAAO,EACN,6BAA6B,EAC7B,yBAAyB,EAGzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,WAAW,EAA2C,MAAM,8BAA8B,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE1E;;GAEG;AACH,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAW;IAC7C;QACC,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EACV,qGAAqG;KACtG;CACD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,oCAAoC,CACnD,aAAqB,EACrB,aAAqB;IAErB,MAAM,mBAAmB,GAAgE;QACxF,WAAW,EAAE,gBAAgB;QAC7B,OAAO,EAAE,4CAA4C;QACrD,GAAG,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC,IAAI;QACnC,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,GAAG,aAAa,UAAU;QAChC,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,EAAE,CAC9C,cAAc,CAAC,kBAAkB,EAAE,aAAa,EAAE,OAAO,CAAC;QAC3D,WAAW,EAAE;YACZ,IAAI,0BAAkC;YACtC,QAAQ,EAAE;gBACT;oBACC,EAAE,EAAE,uBAAuB;oBAC3B,OAAO,EAAE;wBACR,IAAI,EAAE;4BACL,UAAU,EAAE,CAAC,yBAAyB,CAAC,aAAa,CAAC;4BACrD,OAAO,EAAE,6BAA6B,CAAC,qBAAqB;4BAC5D,MAAM,EAAE;gCACP;oCACC,eAAe,EAAE,yBAAyB;iCAC1C;6BACD;yBACD;qBACD;iBACD;gBACD;oBACC,EAAE,EAAE,+BAA+B;oBACnC,OAAO,EAAE;wBACR,IAAI,EAAE;4BACL,UAAU,EAAE,CAAC,yBAAyB,CAAC,aAAa,CAAC;4BACrD,OAAO,EAAE,6BAA6B,CAAC,qBAAqB;yBAC5D;qBACD;iBACD;aACD;SACD;QACD,YAAY,EAAE;YACb;gBACC,IAAI,2BAAmC;gBACvC,QAAQ,EAAE;oBACT;wBACC,EAAE,EAAE,+BAA+B;wBACnC,QAAQ,EAAE;4BACT,IAAI,EAAE;gCACL,UAAU,EAAE,CAAC,yBAAyB,CAAC,aAAa,CAAC;gCACrD,KAAK,EACJ,gFAAgF;gCACjF,OAAO,EAAE,SAAS;gCAClB,aAAa,EAAE,+BAA+B;gCAC9C,OAAO,EAAE;oCACR;wCACC,UAAU,EAAE,CAAC,yBAAyB,CAAC,aAAa,CAAC;wCACrD,KAAK,EAAE,sBAAsB;wCAC7B,OAAO,EAAE,SAAS;wCAClB,KAAK,EAAE,yBAAyB;wCAChC,WAAW,EAAE,oCAAoC;qCACjD;iCACD;6BACD;yBACqC;qBACvC;iBACD;aACD;SACD;KACD,CAAC;IAEF,MAAM,eAAe,GAAwD;QAC5E,WAAW,EAAE,YAAY;QACzB,OAAO,EAAE,mCAAmC;QAC5C,GAAG,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC,IAAI;QACnC,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,GAAG,aAAa,sBAAsB;QAC5C,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,EAAE,CAC9C,UAAU,CAAC,kBAAkB,EAAE,aAAa,EAAE,OAAO,CAAC;QACvD,WAAW,EAAE;YACZ,IAAI,sBAA8B;YAClC,QAAQ,EAAE;gBACT;oBACC,EAAE,EAAE,0BAA0B;oBAC9B,OAAO,EAAE;wBACR,UAAU,EAAE;4BACX,SAAS,EAAE,sBAAsB;yBACjC;qBACD;iBACD;aACD;SACD;QACD,YAAY,EAAE;YACb;gBACC,IAAI,uBAA+B;gBACnC,QAAQ,EAAE;oBACT;wBACC,EAAE,EAAE,2BAA2B;wBAC/B,QAAQ,EAAE;4BACT,IAAI,EAAE;gCACL,UAAU,EAAE,yBAAyB,CAAC,aAA2C;gCACjF,KAAK,EAAE,sBAAsB;gCAC7B,OAAO,EAAE,WAAW,CAAC,OAAO;gCAC5B,eAAe,EAAE,yBAAyB;gCAC1C,qBAAqB,EAAE,oCAAoC;6BAC3D;yBACD;qBACD;iBACD;aACD;SACD;KACD,CAAC;IAEF,OAAO,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CACvB,MAA6F;IAE7F,IAAI,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,6BAA6B,CAAC,YAAY,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QAChG,OAAQ,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAoB,IAAI,cAAc,CAAC,UAAU,CAAC;IACrF,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,cAAc,CAC5B,kBAAuC,EACvC,aAAqB,EACrB,OAA+B;IAE/B,MAAM,CAAC,MAAM,CAAyB,aAAa,aAAmB,OAAO,CAAC,CAAC;IAC/E,MAAM,CAAC,MAAM,CAAC,aAAa,kBAAwB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAElE,MAAM,SAAS,GAAiC,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAEpF,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CACnC,OAAO,CAAC,IAAI,CAAC,MAAmB,EAChC,OAAO,CAAC,KAAK,EAAE,MAAM,EACrB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CACpC,CAAC;IAEF,OAAO;QACN,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC;QAC3C,IAAI,EAAE,MAAM,CAAC,OAAO;QACpB,OAAO,EACN,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,kBAAkB,CAAC,aAAa,EAAE,GAAG,CAAC;YACrF,CAAC,CAAC;gBACA,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,gBAAgB,CAChD,kBAAkB,CAAC,aAAa,CAAC,GAAG,EACpC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EACzB,MAAM,CACN;aACD;YACF,CAAC,CAAC,SAAS;KACb,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,UAAU,CACxB,kBAAuC,EACvC,aAAqB,EACrB,OAA2B;IAE3B,MAAM,CAAC,MAAM,CAAqB,aAAa,aAAmB,OAAO,CAAC,CAAC;IAC3E,MAAM,CAAC,MAAM,CAAC,aAAa,wBAA8B,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7E,MAAM,CAAC,WAAW,CACjB,aAAa,kCAEb,OAAO,CAAC,UAAU,CAAC,SAAS,CAC5B,CAAC;IAEF,MAAM,SAAS,GAAiC,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAEpF,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAEjE,OAAO;QACN,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC;QACnC,IAAI,EAAE,MAAM;KACZ,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IHttpRequestContext, IRestRoute, ITag } 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\ttype IDataspaceProtocolCatalog,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { DcatClasses, type DcatContextType, type IDcatDataset } from \"@twin.org/standards-w3c-dcat\";\nimport { HeaderHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\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.JsonLdContext],\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.JsonLdContext],\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.JsonLdContext],\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\"@context\": [DataspaceProtocolContexts.JsonLdContext],\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\ttitle: \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"Historical energy consumption data\"\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} as unknown as ICatalogRequestResponse\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.JsonLdContext 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 * Map the DS Protocol result to an HTTP status code.\n * @param result The result to map.\n * @returns The mapped status code or undefined if no mapping was found or not an error.\n */\nfunction mapCatalogError(\n\tresult: IDataspaceProtocolCatalog | IDcatDataset | IDataspaceProtocolCatalogError | undefined\n): HttpStatusCode | undefined {\n\tif (result?.[\"@type\"] === DataspaceProtocolCatalogTypes.CatalogError && Is.objectValue(result)) {\n\t\treturn (Coerce.integer(result.code) as HttpStatusCode) ?? HttpStatusCode.badRequest;\n\t}\n\n\treturn undefined;\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\tGuards.object<ICatalogRequestRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object(ROUTES_SOURCE, nameof(request.body), request.body);\n\tGuards.stringValue(ROUTES_SOURCE, \"@type\", request.body[\"@type\"]);\n\n\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\tconst result = await component.query(\n\t\trequest.body.filter as unknown[],\n\t\trequest.query?.cursor,\n\t\tCoerce.integer(request.query?.limit)\n\t);\n\n\treturn {\n\t\tstatusCode: mapCatalogError(result.catalog),\n\t\tbody: result.catalog,\n\t\theaders:\n\t\t\tIs.stringValue(result.cursor) && Is.stringValue(httpRequestContext.serverRequest?.url)\n\t\t\t\t? {\n\t\t\t\t\t\t[HeaderTypes.Link]: HeaderHelper.createLinkHeader(\n\t\t\t\t\t\t\thttpRequestContext.serverRequest.url,\n\t\t\t\t\t\t\t{ cursor: result.cursor },\n\t\t\t\t\t\t\t\"next\"\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t: undefined\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\tGuards.object<IGetDatasetRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object(ROUTES_SOURCE, nameof(request.pathParams), request.pathParams);\n\tGuards.stringValue(\n\t\tROUTES_SOURCE,\n\t\tnameof(request.pathParams.datasetId),\n\t\trequest.pathParams.datasetId\n\t);\n\n\tconst component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\tconst result = await component.get(request.pathParams.datasetId);\n\n\treturn {\n\t\tstatusCode: mapCatalogError(result),\n\t\tbody: result\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 { IHttpRequestContext, IRestRoute, ITag } 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 { 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\"@context\": [DataspaceProtocolContexts.Context],\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\ttitle: \"Energy Consumption Data\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"Historical energy consumption data\"\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} as unknown as ICatalogRequestResponse\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 component: IFederatedCatalogueComponent = ComponentFactory.get(componentName);\n\n\t\tconst queryResult = await component.query(\n\t\t\trequest.body.filter as unknown[],\n\t\t\trequest.query?.cursor,\n\t\t\tCoerce.integer(request.query?.limit)\n\t\t);\n\n\t\treturn {\n\t\t\tstatusCode: transformErrorToStatusCode(queryResult.result),\n\t\t\tbody: queryResult.result,\n\t\t\theaders:\n\t\t\t\tIs.stringValue(queryResult.cursor) && Is.stringValue(httpRequestContext.serverRequest?.url)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t[HeaderTypes.Link]: HeaderHelper.createLinkHeader(\n\t\t\t\t\t\t\t\thttpRequestContext.serverRequest.url,\n\t\t\t\t\t\t\t\t{ cursor: queryResult.cursor },\n\t\t\t\t\t\t\t\t\"next\"\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined\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
|
@@ -6,5 +6,6 @@ export * from "./models/IFederatedCatalogueServiceConstructorOptions.js";
|
|
|
6
6
|
export * from "./restEntryPoints.js";
|
|
7
7
|
export * from "./schema.js";
|
|
8
8
|
export * from "./services/federatedCatalogueService.js";
|
|
9
|
+
export * from "./utils/catalogErrorUtils.js";
|
|
9
10
|
export * from "./utils/datasetConverters.js";
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
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","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/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,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"]}
|
|
@@ -6,12 +6,12 @@ import { Blake2b } from "@twin.org/crypto";
|
|
|
6
6
|
import { JsonLdProcessor } from "@twin.org/data-json-ld";
|
|
7
7
|
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
8
8
|
import { FederatedCatalogueFilterFactory } from "@twin.org/federated-catalogue-models";
|
|
9
|
-
import {
|
|
9
|
+
import { DataspaceProtocolContexts, DataspaceProtocolDataTypes, DataspaceProtocolHelper } from "@twin.org/standards-dataspace-protocol";
|
|
10
10
|
import { DublinCoreContexts, DublinCoreDataTypes } from "@twin.org/standards-dublin-core";
|
|
11
11
|
import { FoafDataTypes } from "@twin.org/standards-foaf";
|
|
12
12
|
import { DcatContexts, DcatDataTypes } from "@twin.org/standards-w3c-dcat";
|
|
13
13
|
import { OdrlContexts } from "@twin.org/standards-w3c-odrl";
|
|
14
|
-
import {
|
|
14
|
+
import { transformToCatalogError } from "../utils/catalogErrorUtils.js";
|
|
15
15
|
import { datasetEntityToModel, datasetModelToEntity } from "../utils/datasetConverters.js";
|
|
16
16
|
/**
|
|
17
17
|
* Service for managing federated catalogue operations.
|
|
@@ -60,18 +60,18 @@ export class FederatedCatalogueService {
|
|
|
60
60
|
* @returns The dataset if found, or a CatalogError if not found or an error occurs.
|
|
61
61
|
*/
|
|
62
62
|
async get(dataSetId) {
|
|
63
|
-
Guards.stringValue(FederatedCatalogueService.CLASS_NAME, "dataSetId", dataSetId);
|
|
64
|
-
await this._logging?.log({
|
|
65
|
-
level: "info",
|
|
66
|
-
source: FederatedCatalogueService.CLASS_NAME,
|
|
67
|
-
ts: Date.now(),
|
|
68
|
-
message: "datasetRetrieve",
|
|
69
|
-
data: { dataSetId }
|
|
70
|
-
});
|
|
71
63
|
try {
|
|
64
|
+
Guards.stringValue(FederatedCatalogueService.CLASS_NAME, "dataSetId", dataSetId);
|
|
65
|
+
await this._logging?.log({
|
|
66
|
+
level: "info",
|
|
67
|
+
source: FederatedCatalogueService.CLASS_NAME,
|
|
68
|
+
ts: Date.now(),
|
|
69
|
+
message: "datasetRetrieve",
|
|
70
|
+
data: { dataSetId }
|
|
71
|
+
});
|
|
72
72
|
const datasetEntity = await this._datasetStorage.get(dataSetId);
|
|
73
73
|
if (!datasetEntity) {
|
|
74
|
-
|
|
74
|
+
throw new NotFoundError(FederatedCatalogueService.CLASS_NAME, "datasetNotFound", dataSetId);
|
|
75
75
|
}
|
|
76
76
|
const dataset = datasetEntityToModel(datasetEntity);
|
|
77
77
|
// Normalize to DS Protocol compliant format
|
|
@@ -80,7 +80,7 @@ export class FederatedCatalogueService {
|
|
|
80
80
|
return normalizedDataset;
|
|
81
81
|
}
|
|
82
82
|
catch (error) {
|
|
83
|
-
return
|
|
83
|
+
return transformToCatalogError(error);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
@@ -189,17 +189,18 @@ export class FederatedCatalogueService {
|
|
|
189
189
|
*/
|
|
190
190
|
async query(filter, cursor, limit) {
|
|
191
191
|
try {
|
|
192
|
+
Guards.array(FederatedCatalogueService.CLASS_NAME, "filter", filter);
|
|
192
193
|
let datasets;
|
|
193
194
|
let resultCursor;
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
if (!Is.empty(filter) && !Is.array(filter)) {
|
|
196
|
+
throw new GeneralError(FederatedCatalogueService.CLASS_NAME, "filterMustBeArray");
|
|
197
|
+
}
|
|
198
|
+
if (!filter || filter.length === 0) {
|
|
196
199
|
const result = await this._datasetStorage.query();
|
|
197
200
|
datasets = result.entities.map(entity => datasetEntityToModel(entity));
|
|
198
201
|
}
|
|
199
|
-
else if (
|
|
200
|
-
|
|
201
|
-
catalog: this.transformToCatalogError(new GeneralError(FederatedCatalogueService.CLASS_NAME, "multipleFiltersNotSupported"), HttpStatusCode.badRequest)
|
|
202
|
-
};
|
|
202
|
+
else if (filter.length > 1) {
|
|
203
|
+
throw new GeneralError(FederatedCatalogueService.CLASS_NAME, "multipleFiltersNotSupported");
|
|
203
204
|
}
|
|
204
205
|
else {
|
|
205
206
|
const singleFilter = filter[0];
|
|
@@ -227,9 +228,7 @@ export class FederatedCatalogueService {
|
|
|
227
228
|
});
|
|
228
229
|
// Return CatalogError 404 when no datasets exist
|
|
229
230
|
if (datasets.length === 0) {
|
|
230
|
-
|
|
231
|
-
catalog: this.transformToCatalogError(new NotFoundError(FederatedCatalogueService.CLASS_NAME, "noDatasetsFound"), HttpStatusCode.notFound)
|
|
232
|
-
};
|
|
231
|
+
throw new NotFoundError(FederatedCatalogueService.CLASS_NAME, "noDatasetsFound");
|
|
233
232
|
}
|
|
234
233
|
// Get requesting participant from context (organizationId maps to participantId)
|
|
235
234
|
const contextIds = await ContextIdStore.getContextIds();
|
|
@@ -256,7 +255,7 @@ export class FederatedCatalogueService {
|
|
|
256
255
|
// Only own datasets (or all datasets belong to requesting participant)
|
|
257
256
|
const catalogId = this.generateCatalogId(ownDatasets, requestingParticipantId);
|
|
258
257
|
catalog = {
|
|
259
|
-
"@context": [DataspaceProtocolContexts.
|
|
258
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
260
259
|
"@id": catalogId,
|
|
261
260
|
"@type": "Catalog",
|
|
262
261
|
participantId: requestingParticipantId,
|
|
@@ -270,7 +269,7 @@ export class FederatedCatalogueService {
|
|
|
270
269
|
const participantDatasets = datasetsByParticipant.get(participantId) ?? [];
|
|
271
270
|
const subCatalogId = this.generateCatalogId(participantDatasets, participantId);
|
|
272
271
|
const subCatalog = {
|
|
273
|
-
"@context": [DataspaceProtocolContexts.
|
|
272
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
274
273
|
"@id": subCatalogId,
|
|
275
274
|
"@type": "Catalog",
|
|
276
275
|
participantId,
|
|
@@ -281,7 +280,7 @@ export class FederatedCatalogueService {
|
|
|
281
280
|
// Root catalog contains own datasets and nested catalogs for others
|
|
282
281
|
const rootCatalogId = this.generateCatalogId(datasets, requestingParticipantId);
|
|
283
282
|
catalog = {
|
|
284
|
-
"@context": [DataspaceProtocolContexts.
|
|
283
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
285
284
|
"@id": rootCatalogId,
|
|
286
285
|
"@type": "Catalog",
|
|
287
286
|
participantId: requestingParticipantId,
|
|
@@ -293,13 +292,13 @@ export class FederatedCatalogueService {
|
|
|
293
292
|
// This ensures the payload matches exactly what the DS Protocol mandates
|
|
294
293
|
const normalizedCatalog = await DataspaceProtocolHelper.normalize(catalog);
|
|
295
294
|
return {
|
|
296
|
-
|
|
295
|
+
result: normalizedCatalog,
|
|
297
296
|
cursor: resultCursor
|
|
298
297
|
};
|
|
299
298
|
}
|
|
300
299
|
catch (error) {
|
|
301
300
|
return {
|
|
302
|
-
|
|
301
|
+
result: transformToCatalogError(error)
|
|
303
302
|
};
|
|
304
303
|
}
|
|
305
304
|
}
|
|
@@ -354,25 +353,5 @@ export class FederatedCatalogueService {
|
|
|
354
353
|
const catalogHash = Converter.bytesToHex(Blake2b.sum256(canonicalBytes));
|
|
355
354
|
return `urn:x-catalog:${catalogHash}`;
|
|
356
355
|
}
|
|
357
|
-
/**
|
|
358
|
-
* Transform a TWIN Platform error to DS Protocol CatalogError format.
|
|
359
|
-
* @param error The error to transform.
|
|
360
|
-
* @param statusCode The HTTP status code.
|
|
361
|
-
* @returns The CatalogError.
|
|
362
|
-
*/
|
|
363
|
-
transformToCatalogError(error, statusCode) {
|
|
364
|
-
const baseError = BaseError.fromError(error);
|
|
365
|
-
const reason = [baseError.message];
|
|
366
|
-
// Include properties for debugging if present
|
|
367
|
-
if (baseError.properties && Object.keys(baseError.properties).length > 0) {
|
|
368
|
-
reason.push(JSON.stringify(baseError.properties));
|
|
369
|
-
}
|
|
370
|
-
return {
|
|
371
|
-
"@context": DataspaceProtocolContexts.JsonLdContext,
|
|
372
|
-
"@type": DataspaceProtocolCatalogTypes.CatalogError,
|
|
373
|
-
code: statusCode.toString(),
|
|
374
|
-
reason
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
356
|
}
|
|
378
357
|
//# sourceMappingURL=federatedCatalogueService.js.map
|
|
@@ -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 { 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\ttype IValidationFailure\n} from \"@twin.org/core\";\nimport { Blake2b } from \"@twin.org/crypto\";\nimport { 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\ttype DcatContextType,\n\tDcatDataTypes,\n\ttype IDcatDataset\n} from \"@twin.org/standards-w3c-dcat\";\nimport { OdrlContexts } from \"@twin.org/standards-w3c-odrl\";\nimport { HttpStatusCode } from \"@twin.org/web\";\nimport type { Dataset } from \"../entities/dataset.js\";\nimport type { IFederatedCatalogueServiceConstructorOptions } from \"../models/IFederatedCatalogueServiceConstructorOptions.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 * 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?.datasetStorageConnectorType ?? \"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 * 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\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: \"datasetRetrieve\",\n\t\t\tdata: { dataSetId }\n\t\t});\n\n\t\ttry {\n\t\t\tconst datasetEntity = await this._datasetStorage.get(dataSetId);\n\n\t\t\tif (!datasetEntity) {\n\t\t\t\treturn this.transformToCatalogError(\n\t\t\t\t\tnew NotFoundError(FederatedCatalogueService.CLASS_NAME, \"datasetNotFound\", dataSetId),\n\t\t\t\t\tHttpStatusCode.notFound\n\t\t\t\t);\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(dataset);\n\n\t\t\treturn normalizedDataset as IDcatDataset;\n\t\t} catch (error) {\n\t\t\treturn this.transformToCatalogError(error, HttpStatusCode.internalServerError);\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: IValidationFailure[] = [];\n\t\tconst isConformant = await DataspaceProtocolHelper.checkConformance(\n\t\t\tdataSet,\n\t\t\tvalidationFailures\n\t\t);\n\n\t\tif (!isConformant) {\n\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetNotConformant\", {\n\t\t\t\tdataSetId,\n\t\t\t\tvalidationFailures\n\t\t\t});\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\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 datasetEntity = datasetModelToEntity(normalizedDataset);\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\tcatalog: IDataspaceProtocolCatalog | IDataspaceProtocolCatalogError;\n\t\tcursor?: string;\n\t}> {\n\t\ttry {\n\t\t\tlet datasets: IDcatDataset[];\n\t\t\tlet resultCursor: string | undefined;\n\n\t\t\tconst isArray = Is.array(filter);\n\t\t\tif (!filter || (isArray && 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 (isArray && filter.length > 1) {\n\t\t\t\treturn {\n\t\t\t\t\tcatalog: this.transformToCatalogError(\n\t\t\t\t\t\tnew GeneralError(FederatedCatalogueService.CLASS_NAME, \"multipleFiltersNotSupported\"),\n\t\t\t\t\t\tHttpStatusCode.badRequest\n\t\t\t\t\t)\n\t\t\t\t};\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;\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\treturn {\n\t\t\t\t\tcatalog: this.transformToCatalogError(\n\t\t\t\t\t\tnew NotFoundError(FederatedCatalogueService.CLASS_NAME, \"noDatasetsFound\"),\n\t\t\t\t\t\tHttpStatusCode.notFound\n\t\t\t\t\t)\n\t\t\t\t};\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.JsonLdContext],\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.JsonLdContext],\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.JsonLdContext],\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(catalog);\n\n\t\t\treturn {\n\t\t\t\tcatalog: normalizedCatalog as IDataspaceProtocolCatalog,\n\t\t\t\tcursor: resultCursor\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tcatalog: this.transformToCatalogError(error, HttpStatusCode.badRequest)\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\t/**\n\t * Transform a TWIN Platform error to DS Protocol CatalogError format.\n\t * @param error The error to transform.\n\t * @param statusCode The HTTP status code.\n\t * @returns The CatalogError.\n\t */\n\tprivate transformToCatalogError(\n\t\terror: unknown,\n\t\tstatusCode: HttpStatusCode\n\t): IDataspaceProtocolCatalogError {\n\t\tconst baseError = BaseError.fromError(error);\n\n\t\tconst reason: string[] = [baseError.message];\n\n\t\t// Include properties for debugging if present\n\t\tif (baseError.properties && Object.keys(baseError.properties).length > 0) {\n\t\t\treason.push(JSON.stringify(baseError.properties));\n\t\t}\n\n\t\treturn {\n\t\t\t\"@context\": DataspaceProtocolContexts.JsonLdContext,\n\t\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogError,\n\t\t\tcode: statusCode.toString(),\n\t\t\treason\n\t\t} as unknown as IDataspaceProtocolCatalogError;\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 { 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\ttype IValidationFailure\n} from \"@twin.org/core\";\nimport { Blake2b } from \"@twin.org/crypto\";\nimport { 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\ttype DcatContextType,\n\tDcatDataTypes,\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 * 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?.datasetStorageConnectorType ?? \"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 * 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(dataset);\n\n\t\t\treturn normalizedDataset as IDcatDataset;\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: IValidationFailure[] = [];\n\t\tconst isConformant = await DataspaceProtocolHelper.checkConformance(\n\t\t\tdataSet,\n\t\t\tvalidationFailures\n\t\t);\n\n\t\tif (!isConformant) {\n\t\t\tthrow new GeneralError(FederatedCatalogueService.CLASS_NAME, \"datasetNotConformant\", {\n\t\t\t\tdataSetId,\n\t\t\t\tvalidationFailures\n\t\t\t});\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\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 datasetEntity = datasetModelToEntity(normalizedDataset);\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;\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(catalog);\n\n\t\t\treturn {\n\t\t\t\tresult: normalizedCatalog as IDataspaceProtocolCatalog,\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"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright 2025 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { HttpErrorHelper } from "@twin.org/api-models";
|
|
4
|
+
import { BaseError, Is } from "@twin.org/core";
|
|
5
|
+
import { DataspaceProtocolCatalogTypes, DataspaceProtocolContexts } from "@twin.org/standards-dataspace-protocol";
|
|
6
|
+
import { HttpStatusCode } from "@twin.org/web";
|
|
7
|
+
/**
|
|
8
|
+
* Transform an error to DS Protocol CatalogError format.
|
|
9
|
+
* Used by both service and route layers to ensure consistent error responses.
|
|
10
|
+
* @param error The error to transform.
|
|
11
|
+
* @returns The CatalogError.
|
|
12
|
+
*/
|
|
13
|
+
export function transformToCatalogError(error) {
|
|
14
|
+
const flattened = BaseError.flatten(error);
|
|
15
|
+
// We maintain the reason as the flattened array of errors for more context
|
|
16
|
+
// this also helps preserve the original error messages and types
|
|
17
|
+
// The code property can be any machine-readable string, we use the top-level error name
|
|
18
|
+
// The schema allows for an array of any objects for the reason property
|
|
19
|
+
// and is not limited to just strings or specific error formats
|
|
20
|
+
// https://github.com/eclipse-dataspace-protocol-base/DataspaceProtocol/blob/main/artifacts/src/main/resources/catalog/catalog-error-schema.json
|
|
21
|
+
return {
|
|
22
|
+
"@context": DataspaceProtocolContexts.Context,
|
|
23
|
+
"@type": DataspaceProtocolCatalogTypes.CatalogError,
|
|
24
|
+
code: `${flattened[0].name}:${flattened[0].message}`,
|
|
25
|
+
reason: flattened
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Transform the DS Protocol result to an HTTP status code.
|
|
30
|
+
* @param result The result to transform.
|
|
31
|
+
* @returns The transformed status code or undefined if no transformation was found or not an error.
|
|
32
|
+
*/
|
|
33
|
+
export function transformErrorToStatusCode(result) {
|
|
34
|
+
// Is this an catalog error?
|
|
35
|
+
if (Is.object(result) &&
|
|
36
|
+
result["@type"] === DataspaceProtocolCatalogTypes.CatalogError) {
|
|
37
|
+
// As per the method above the result.code is the IError name property
|
|
38
|
+
// so we use that to map to status codes
|
|
39
|
+
const codePart = result.code.split(":")[0];
|
|
40
|
+
return HttpErrorHelper.ERROR_TYPE_MAP[codePart] ?? HttpStatusCode.badRequest;
|
|
41
|
+
}
|
|
42
|
+
// Or a regular error
|
|
43
|
+
if (Is.object(result) && !BaseError.isEmpty(result)) {
|
|
44
|
+
// If this is a regular error, we can use the name property to map to status codes
|
|
45
|
+
return HttpErrorHelper.ERROR_TYPE_MAP[result.name] ?? HttpStatusCode.badRequest;
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=catalogErrorUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogErrorUtils.js","sourceRoot":"","sources":["../../../src/utils/catalogErrorUtils.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAe,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EACN,6BAA6B,EAC7B,yBAAyB,EAEzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE3C,2EAA2E;IAC3E,iEAAiE;IACjE,wFAAwF;IACxF,wEAAwE;IACxE,+DAA+D;IAC/D,gJAAgJ;IAChJ,OAAO;QACN,UAAU,EAAE,yBAAyB,CAAC,OAAO;QAC7C,OAAO,EAAE,6BAA6B,CAAC,YAAY;QACnD,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE;QACpD,MAAM,EAAE,SAAS;KAC4B,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAe;IACzD,4BAA4B;IAC5B,IACC,EAAE,CAAC,MAAM,CAAiC,MAAM,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,KAAK,6BAA6B,CAAC,YAAY,EAC7D,CAAC;QACF,sEAAsE;QACtE,wCAAwC;QACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,eAAe,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC;IAC9E,CAAC;IAED,qBAAqB;IACrB,IAAI,EAAE,CAAC,MAAM,CAAS,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,kFAAkF;QAClF,OAAO,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC;IACjF,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { HttpErrorHelper } from \"@twin.org/api-models\";\nimport { BaseError, type IError, Is } from \"@twin.org/core\";\nimport {\n\tDataspaceProtocolCatalogTypes,\n\tDataspaceProtocolContexts,\n\ttype IDataspaceProtocolCatalogError\n} from \"@twin.org/standards-dataspace-protocol\";\nimport { HttpStatusCode } from \"@twin.org/web\";\n\n/**\n * Transform an error to DS Protocol CatalogError format.\n * Used by both service and route layers to ensure consistent error responses.\n * @param error The error to transform.\n * @returns The CatalogError.\n */\nexport function transformToCatalogError(error: unknown): IDataspaceProtocolCatalogError {\n\tconst flattened = BaseError.flatten(error);\n\n\t// We maintain the reason as the flattened array of errors for more context\n\t// this also helps preserve the original error messages and types\n\t// The code property can be any machine-readable string, we use the top-level error name\n\t// The schema allows for an array of any objects for the reason property\n\t// and is not limited to just strings or specific error formats\n\t// https://github.com/eclipse-dataspace-protocol-base/DataspaceProtocol/blob/main/artifacts/src/main/resources/catalog/catalog-error-schema.json\n\treturn {\n\t\t\"@context\": DataspaceProtocolContexts.Context,\n\t\t\"@type\": DataspaceProtocolCatalogTypes.CatalogError,\n\t\tcode: `${flattened[0].name}:${flattened[0].message}`,\n\t\treason: flattened\n\t} as unknown as IDataspaceProtocolCatalogError;\n}\n\n/**\n * Transform the DS Protocol result to an HTTP status code.\n * @param result The result to transform.\n * @returns The transformed status code or undefined if no transformation was found or not an error.\n */\nexport function transformErrorToStatusCode(result: unknown): HttpStatusCode | undefined {\n\t// Is this an catalog error?\n\tif (\n\t\tIs.object<IDataspaceProtocolCatalogError>(result) &&\n\t\tresult[\"@type\"] === DataspaceProtocolCatalogTypes.CatalogError\n\t) {\n\t\t// As per the method above the result.code is the IError name property\n\t\t// so we use that to map to status codes\n\t\tconst codePart = result.code.split(\":\")[0];\n\t\treturn HttpErrorHelper.ERROR_TYPE_MAP[codePart] ?? HttpStatusCode.badRequest;\n\t}\n\n\t// Or a regular error\n\tif (Is.object<IError>(result) && !BaseError.isEmpty(result)) {\n\t\t// If this is a regular error, we can use the name property to map to status codes\n\t\treturn HttpErrorHelper.ERROR_TYPE_MAP[result.name] ?? HttpStatusCode.badRequest;\n\t}\n\n\treturn undefined;\n}\n"]}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ export * from "./models/IFederatedCatalogueServiceConstructorOptions.js";
|
|
|
4
4
|
export * from "./restEntryPoints.js";
|
|
5
5
|
export * from "./schema.js";
|
|
6
6
|
export * from "./services/federatedCatalogueService.js";
|
|
7
|
+
export * from "./utils/catalogErrorUtils.js";
|
|
7
8
|
export * from "./utils/datasetConverters.js";
|
|
@@ -51,7 +51,7 @@ export declare class FederatedCatalogueService implements IFederatedCatalogueCom
|
|
|
51
51
|
* or CatalogError if validation fails or an error occurs.
|
|
52
52
|
*/
|
|
53
53
|
query(filter?: unknown[], cursor?: string, limit?: number): Promise<{
|
|
54
|
-
|
|
54
|
+
result: IDataspaceProtocolCatalog | IDataspaceProtocolCatalogError;
|
|
55
55
|
cursor?: string;
|
|
56
56
|
}>;
|
|
57
57
|
/**
|
|
@@ -74,11 +74,4 @@ export declare class FederatedCatalogueService implements IFederatedCatalogueCom
|
|
|
74
74
|
* @returns A URN-formatted catalog ID.
|
|
75
75
|
*/
|
|
76
76
|
private generateCatalogId;
|
|
77
|
-
/**
|
|
78
|
-
* Transform a TWIN Platform error to DS Protocol CatalogError format.
|
|
79
|
-
* @param error The error to transform.
|
|
80
|
-
* @param statusCode The HTTP status code.
|
|
81
|
-
* @returns The CatalogError.
|
|
82
|
-
*/
|
|
83
|
-
private transformToCatalogError;
|
|
84
77
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type IDataspaceProtocolCatalogError } from "@twin.org/standards-dataspace-protocol";
|
|
2
|
+
import { HttpStatusCode } from "@twin.org/web";
|
|
3
|
+
/**
|
|
4
|
+
* Transform an error to DS Protocol CatalogError format.
|
|
5
|
+
* Used by both service and route layers to ensure consistent error responses.
|
|
6
|
+
* @param error The error to transform.
|
|
7
|
+
* @returns The CatalogError.
|
|
8
|
+
*/
|
|
9
|
+
export declare function transformToCatalogError(error: unknown): IDataspaceProtocolCatalogError;
|
|
10
|
+
/**
|
|
11
|
+
* Transform the DS Protocol result to an HTTP status code.
|
|
12
|
+
* @param result The result to transform.
|
|
13
|
+
* @returns The transformed status code or undefined if no transformation was found or not an error.
|
|
14
|
+
*/
|
|
15
|
+
export declare function transformErrorToStatusCode(result: unknown): HttpStatusCode | undefined;
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @twin.org/federated-catalogue-service - Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.3-next.7](https://github.com/twinfoundation/federated-catalogue/compare/federated-catalogue-service-v0.0.3-next.6...federated-catalogue-service-v0.0.3-next.7) (2026-01-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* context usage ([cd51790](https://github.com/twinfoundation/federated-catalogue/commit/cd51790ef97e14d21e89ef668b98477c163856bb))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @twin.org/federated-catalogue-models bumped from 0.0.3-next.6 to 0.0.3-next.7
|
|
16
|
+
|
|
17
|
+
## [0.0.3-next.6](https://github.com/twinfoundation/federated-catalogue/compare/federated-catalogue-service-v0.0.3-next.5...federated-catalogue-service-v0.0.3-next.6) (2026-01-20)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* transform GuardError to CatalogError for DS Protocol compliance ([#49](https://github.com/twinfoundation/federated-catalogue/issues/49)) ([d0f1090](https://github.com/twinfoundation/federated-catalogue/commit/d0f10900c251b9abc18e58c90562c393c3265727))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Dependencies
|
|
26
|
+
|
|
27
|
+
* The following workspace dependencies were updated
|
|
28
|
+
* dependencies
|
|
29
|
+
* @twin.org/federated-catalogue-models bumped from 0.0.3-next.5 to 0.0.3-next.6
|
|
30
|
+
|
|
3
31
|
## [0.0.3-next.5](https://github.com/twinfoundation/federated-catalogue/compare/federated-catalogue-service-v0.0.3-next.4...federated-catalogue-service-v0.0.3-next.5) (2026-01-15)
|
|
4
32
|
|
|
5
33
|
|
|
@@ -57,7 +57,7 @@ The class name of the component.
|
|
|
57
57
|
|
|
58
58
|
### get()
|
|
59
59
|
|
|
60
|
-
> **get**(`dataSetId`): `Promise`\<`
|
|
60
|
+
> **get**(`dataSetId`): `Promise`\<`IDataspaceProtocolCatalogError` \| `IDcatDataset`\>
|
|
61
61
|
|
|
62
62
|
Retrieve a dataset by its unique identifier.
|
|
63
63
|
|
|
@@ -71,7 +71,7 @@ The unique identifier of the dataset.
|
|
|
71
71
|
|
|
72
72
|
#### Returns
|
|
73
73
|
|
|
74
|
-
`Promise`\<`
|
|
74
|
+
`Promise`\<`IDataspaceProtocolCatalogError` \| `IDcatDataset`\>
|
|
75
75
|
|
|
76
76
|
The dataset if found, or a CatalogError if not found or an error occurs.
|
|
77
77
|
|
|
@@ -108,7 +108,7 @@ The dataset to store.
|
|
|
108
108
|
|
|
109
109
|
### query()
|
|
110
110
|
|
|
111
|
-
> **query**(`filter?`, `cursor?`, `limit?`): `Promise`\<\{ `
|
|
111
|
+
> **query**(`filter?`, `cursor?`, `limit?`): `Promise`\<\{ `result`: `IDataspaceProtocolCatalogError` \| `IDataspaceProtocolCatalog`; `cursor?`: `string`; \}\>
|
|
112
112
|
|
|
113
113
|
Execute a query against the catalogue using registered filter plugins.
|
|
114
114
|
Returns a DS Protocol compliant Catalog object with participantId.
|
|
@@ -142,7 +142,7 @@ Optional limit for pagination.
|
|
|
142
142
|
|
|
143
143
|
#### Returns
|
|
144
144
|
|
|
145
|
-
`Promise`\<\{ `
|
|
145
|
+
`Promise`\<\{ `result`: `IDataspaceProtocolCatalogError` \| `IDataspaceProtocolCatalog`; `cursor?`: `string`; \}\>
|
|
146
146
|
|
|
147
147
|
Complete IDataspaceProtocolCatalog with @context, @id, @type, participantId, dataset/catalog,
|
|
148
148
|
or CatalogError if validation fails or an error occurs.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Function: transformErrorToStatusCode()
|
|
2
|
+
|
|
3
|
+
> **transformErrorToStatusCode**(`result`): `HttpStatusCode` \| `undefined`
|
|
4
|
+
|
|
5
|
+
Transform the DS Protocol result to an HTTP status code.
|
|
6
|
+
|
|
7
|
+
## Parameters
|
|
8
|
+
|
|
9
|
+
### result
|
|
10
|
+
|
|
11
|
+
`unknown`
|
|
12
|
+
|
|
13
|
+
The result to transform.
|
|
14
|
+
|
|
15
|
+
## Returns
|
|
16
|
+
|
|
17
|
+
`HttpStatusCode` \| `undefined`
|
|
18
|
+
|
|
19
|
+
The transformed status code or undefined if no transformation was found or not an error.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Function: transformToCatalogError()
|
|
2
|
+
|
|
3
|
+
> **transformToCatalogError**(`error`): `IDataspaceProtocolCatalogError`
|
|
4
|
+
|
|
5
|
+
Transform an error to DS Protocol CatalogError format.
|
|
6
|
+
Used by both service and route layers to ensure consistent error responses.
|
|
7
|
+
|
|
8
|
+
## Parameters
|
|
9
|
+
|
|
10
|
+
### error
|
|
11
|
+
|
|
12
|
+
`unknown`
|
|
13
|
+
|
|
14
|
+
The error to transform.
|
|
15
|
+
|
|
16
|
+
## Returns
|
|
17
|
+
|
|
18
|
+
`IDataspaceProtocolCatalogError`
|
|
19
|
+
|
|
20
|
+
The CatalogError.
|
package/docs/reference/index.md
CHANGED
|
@@ -18,5 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
- [generateRestRoutesFederatedCatalogue](functions/generateRestRoutesFederatedCatalogue.md)
|
|
20
20
|
- [initSchema](functions/initSchema.md)
|
|
21
|
+
- [transformToCatalogError](functions/transformToCatalogError.md)
|
|
22
|
+
- [transformErrorToStatusCode](functions/transformErrorToStatusCode.md)
|
|
21
23
|
- [datasetEntityToModel](functions/datasetEntityToModel.md)
|
|
22
24
|
- [datasetModelToEntity](functions/datasetModelToEntity.md)
|
package/locales/en.json
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"datasetMissingPublisher": "Dataset \"{dataSetId}\" is missing required dcterms:publisher property (needed for participant identification)",
|
|
8
8
|
"filterIndexCreationFailed": "Failed to create filter index for dataset \"{dataSetId}\" using filter \"{filterType}\"",
|
|
9
9
|
"multipleFiltersNotSupported": "Multiple filters are not supported in a single query",
|
|
10
|
+
"filterMustBeArray": "Filter must be an array per DS Protocol specification",
|
|
10
11
|
"noDatasetsFound": "No datasets found in the catalogue"
|
|
11
12
|
}
|
|
12
13
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/federated-catalogue-service",
|
|
3
|
-
"version": "0.0.3-next.
|
|
3
|
+
"version": "0.0.3-next.7",
|
|
4
4
|
"description": "Federated Catalogue contract implementation and REST endpoint definitions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@twin.org/data-json-ld": "next",
|
|
21
21
|
"@twin.org/entity": "next",
|
|
22
22
|
"@twin.org/entity-storage-models": "next",
|
|
23
|
-
"@twin.org/federated-catalogue-models": "0.0.3-next.
|
|
23
|
+
"@twin.org/federated-catalogue-models": "0.0.3-next.7",
|
|
24
24
|
"@twin.org/logging-models": "next",
|
|
25
25
|
"@twin.org/nameof": "next",
|
|
26
26
|
"@twin.org/standards-dataspace-protocol": "next",
|