@uns-kit/api 2.0.45 → 2.0.46

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.
@@ -1 +1,185 @@
1
- export type { IApiProxyOptions, IGetEndpointOptions, QueryParamDef } from "@uns-kit/core/uns/uns-interfaces.js";
1
+ export type { IApiProxyOptions, IGetEndpointOptions, IPostEndpointOptions, QueryParamDef, } from "@uns-kit/core/uns/uns-interfaces.js";
2
+ import type { IGetEndpointOptions, IPostEndpointOptions } from "@uns-kit/core/uns/uns-interfaces.js";
3
+ export type ApiInteractionMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
4
+ export type DataCatalogParameterRegistration = {
5
+ name: string;
6
+ in: "path" | "query" | "header" | "cookie";
7
+ required?: boolean;
8
+ description?: string | null;
9
+ type?: string | null;
10
+ format?: string | null;
11
+ nullable?: boolean;
12
+ example?: unknown;
13
+ enumValues?: string[];
14
+ schema?: unknown;
15
+ };
16
+ export type DataCatalogSchemaFieldRegistration = {
17
+ name: string;
18
+ path?: string;
19
+ sourceKey?: string | null;
20
+ type?: string | null;
21
+ format?: string | null;
22
+ nullable?: boolean;
23
+ required?: boolean;
24
+ description?: string | null;
25
+ enumValues?: string[];
26
+ example?: unknown;
27
+ };
28
+ export type DataCatalogSchemaRegistration = {
29
+ id: string;
30
+ title: string;
31
+ kind?: string | null;
32
+ source?: string | null;
33
+ contentType?: string | null;
34
+ rootType?: string | null;
35
+ fieldPathPrefix?: string | null;
36
+ nullable?: boolean;
37
+ description?: string | null;
38
+ fields?: DataCatalogSchemaFieldRegistration[];
39
+ examplePayloads?: unknown[];
40
+ };
41
+ export type DataCatalogRequestBodyRegistration = {
42
+ required?: boolean;
43
+ description?: string | null;
44
+ contentType?: string | null;
45
+ schemas?: DataCatalogSchemaRegistration[];
46
+ schemaIds?: string[];
47
+ };
48
+ export type DataCatalogResponseRegistration = {
49
+ statusCode: string;
50
+ description?: string | null;
51
+ contentType?: string | null;
52
+ schemas?: DataCatalogSchemaRegistration[];
53
+ schemaIds?: string[];
54
+ examplePayloads?: unknown[];
55
+ headers?: DataCatalogParameterRegistration[];
56
+ };
57
+ export type DataCatalogOperationRegistration = {
58
+ id?: string;
59
+ method: ApiInteractionMethod | "HEAD" | "OPTIONS";
60
+ path: string;
61
+ summary?: string | null;
62
+ description?: string | null;
63
+ tags?: string[];
64
+ deprecated?: boolean;
65
+ parameters?: DataCatalogParameterRegistration[];
66
+ headers?: DataCatalogParameterRegistration[];
67
+ requestBody?: DataCatalogRequestBodyRegistration | null;
68
+ responses?: DataCatalogResponseRegistration[];
69
+ };
70
+ export type DataCatalogOfferRegistration = {
71
+ offerId: string;
72
+ displayName: string;
73
+ description?: string | null;
74
+ owner?: string | null;
75
+ status?: string | null;
76
+ tags?: string[];
77
+ categories?: string[];
78
+ microserviceName?: string | null;
79
+ version?: string | null;
80
+ basePaths?: string[];
81
+ operations: DataCatalogOperationRegistration[];
82
+ schemas?: DataCatalogSchemaRegistration[];
83
+ swaggerPath?: string | null;
84
+ metadata?: Record<string, unknown> | null;
85
+ };
86
+ type ApiRouteIdentity<Handler = unknown> = {
87
+ topic: string;
88
+ asset: string;
89
+ objectType: string;
90
+ objectId: string;
91
+ attribute: string;
92
+ handler: Handler;
93
+ };
94
+ export type DataCatalogOfferSourceRegistration<Handler = unknown> = ApiRouteIdentity<Handler> & {
95
+ offerId: string;
96
+ displayName: string;
97
+ description: string;
98
+ owner?: string;
99
+ status?: string;
100
+ tags?: string[];
101
+ categories?: string[];
102
+ method?: ApiInteractionMethod;
103
+ queryParams?: DataCatalogParameterRegistration[];
104
+ headers?: DataCatalogParameterRegistration[];
105
+ requestBody?: DataCatalogRequestBodyRegistration | null;
106
+ response?: {
107
+ statusCode?: string;
108
+ description?: string | null;
109
+ contentType?: string | null;
110
+ headers?: DataCatalogParameterRegistration[];
111
+ examplePayloads?: unknown[];
112
+ };
113
+ schema: DataCatalogSchemaRegistration;
114
+ apiDescription?: string;
115
+ };
116
+ export type ServiceApiRegistration<Handler = unknown> = {
117
+ attribute: string;
118
+ handler: Handler;
119
+ method?: ApiInteractionMethod;
120
+ description: string;
121
+ publishInUnsTree?: boolean;
122
+ tags?: string[];
123
+ queryParams?: DataCatalogParameterRegistration[];
124
+ requestBody?: DataCatalogRequestBodyRegistration | null;
125
+ response?: DataCatalogResponseRegistration;
126
+ schema?: DataCatalogSchemaRegistration;
127
+ topic?: string;
128
+ asset?: string;
129
+ objectType?: string;
130
+ objectId?: string;
131
+ };
132
+ export type ApiInteractionDefinition<Handler = unknown> = {
133
+ id: string;
134
+ topic: string;
135
+ asset: string;
136
+ objectType: string;
137
+ objectId: string;
138
+ attribute: string;
139
+ method: ApiInteractionMethod;
140
+ routeOnly?: boolean;
141
+ registryTopic?: "api-endpoints" | "service-endpoints" | "data-offer-endpoints";
142
+ options: IGetEndpointOptions | IPostEndpointOptions;
143
+ handler: Handler;
144
+ };
145
+ export declare function defineApiInteraction<Handler>(input: ApiInteractionDefinition<Handler>): ApiInteractionDefinition<Handler>;
146
+ export declare function defineServiceApi<Handler>(input: ServiceApiRegistration<Handler>): ServiceApiRegistration<Handler>;
147
+ export declare function buildApiInteractionPath(route: Pick<ApiInteractionDefinition, "topic" | "asset" | "objectType" | "objectId" | "attribute">, apiBasePrefix?: string): string;
148
+ export declare function buildApiInteractionTopicPath(route: Pick<ApiInteractionDefinition, "topic" | "asset" | "objectType" | "objectId" | "attribute">): string;
149
+ export declare function defineDataCatalogQueryParam(name: string, description: string, options?: {
150
+ required?: boolean;
151
+ type?: string;
152
+ format?: string;
153
+ example?: unknown;
154
+ }): DataCatalogParameterRegistration;
155
+ export declare function defineDataCatalogField(name: string, type: string, description: string, options?: {
156
+ path?: string;
157
+ sourceKey?: string;
158
+ required?: boolean;
159
+ format?: string;
160
+ nullable?: boolean;
161
+ example?: unknown;
162
+ enumValues?: string[];
163
+ }): DataCatalogSchemaFieldRegistration;
164
+ export declare function defineDataCatalogSchema(input: {
165
+ id: string;
166
+ title: string;
167
+ contentType: string;
168
+ description?: string;
169
+ kind?: string;
170
+ source?: string;
171
+ rootType?: string;
172
+ fieldPathPrefix?: string;
173
+ fields: DataCatalogSchemaFieldRegistration[];
174
+ examplePayloads?: unknown[];
175
+ }): DataCatalogSchemaRegistration;
176
+ export declare function buildDataCatalogOfferOperation(route: Pick<ApiInteractionDefinition, "topic" | "asset" | "objectType" | "objectId" | "attribute">, operation: Omit<DataCatalogOperationRegistration, "path">, apiBasePrefix?: string): DataCatalogOperationRegistration;
177
+ export declare function defineDataCatalogOfferSource<Handler>(input: DataCatalogOfferSourceRegistration<Handler>): DataCatalogOfferSourceRegistration<Handler>;
178
+ export declare function buildServiceApiInteractions<Handler, T extends Record<string, ServiceApiRegistration<Handler>>>(processName: string, definitions: T): Record<keyof T, ApiInteractionDefinition<Handler>>;
179
+ export declare function buildApiInteractionsFromDataOfferSources<Handler, T extends Record<string, DataCatalogOfferSourceRegistration<Handler>>>(sources: T): Record<keyof T, ApiInteractionDefinition<Handler>>;
180
+ export declare function buildDataCatalogOffersFromSources<T extends Record<string, DataCatalogOfferSourceRegistration<any>>>(sources: T): DataCatalogOfferRegistration[];
181
+ export declare function projectRowsForDataCatalogSchema(rows: Array<Record<string, unknown>>, schema: DataCatalogSchemaRegistration): Array<Record<string, unknown>>;
182
+ export declare function buildSchemaFieldPathIndex(schema: DataCatalogSchemaRegistration): Array<{
183
+ path: string;
184
+ name: string;
185
+ }>;
@@ -1 +1,311 @@
1
- export {};
1
+ import { buildUnsRoutePath } from "@uns-kit/core/uns/uns-path.js";
2
+ export function defineApiInteraction(input) {
3
+ return input;
4
+ }
5
+ export function defineServiceApi(input) {
6
+ return input;
7
+ }
8
+ export function buildApiInteractionPath(route, apiBasePrefix = "/api") {
9
+ return `${apiBasePrefix}${buildUnsRoutePath(route.topic, route.asset, route.objectType, route.objectId, route.attribute)}`.replace(/\/{2,}/g, "/");
10
+ }
11
+ export function buildApiInteractionTopicPath(route) {
12
+ return buildUnsRoutePath(route.topic, route.asset, route.objectType, route.objectId, route.attribute).slice(1);
13
+ }
14
+ export function defineDataCatalogQueryParam(name, description, options = {}) {
15
+ return {
16
+ name,
17
+ in: "query",
18
+ type: options.type ?? "string",
19
+ required: options.required === true,
20
+ description,
21
+ ...(options.format ? { format: options.format } : {}),
22
+ ...(options.example !== undefined ? { example: options.example } : {}),
23
+ };
24
+ }
25
+ export function defineDataCatalogField(name, type, description, options = {}) {
26
+ return {
27
+ name,
28
+ path: options.path ?? name,
29
+ type,
30
+ description,
31
+ required: options.required === true,
32
+ nullable: options.nullable === true,
33
+ ...(options.format ? { format: options.format } : {}),
34
+ ...(options.example !== undefined ? { example: options.example } : {}),
35
+ ...(Array.isArray(options.enumValues) ? { enumValues: options.enumValues } : {}),
36
+ ...(options.sourceKey ? { sourceKey: options.sourceKey } : {}),
37
+ };
38
+ }
39
+ export function defineDataCatalogSchema(input) {
40
+ const fieldPathPrefix = input.fieldPathPrefix?.trim();
41
+ const fields = input.fields.map((field) => ({
42
+ ...field,
43
+ path: field.path ?? buildSchemaFieldPath(field.name, field.type ?? "string", fieldPathPrefix),
44
+ }));
45
+ const shouldAutogenerateExamples = !isBinaryCatalogContentType(input.contentType);
46
+ const examplePayloads = Array.isArray(input.examplePayloads) && input.examplePayloads.length
47
+ ? input.examplePayloads
48
+ : shouldAutogenerateExamples
49
+ ? buildExamplePayloadsFromFields(fields)
50
+ : [];
51
+ return {
52
+ id: input.id,
53
+ title: input.title,
54
+ kind: input.kind ?? "response",
55
+ source: input.source ?? "registered",
56
+ contentType: input.contentType,
57
+ rootType: input.rootType ?? inferSchemaRootType(input.contentType, input.fields),
58
+ ...(fieldPathPrefix ? { fieldPathPrefix } : {}),
59
+ ...(input.description ? { description: input.description } : {}),
60
+ fields,
61
+ ...(examplePayloads?.length ? { examplePayloads } : {}),
62
+ };
63
+ }
64
+ function isBinaryCatalogContentType(contentType) {
65
+ const normalized = String(contentType ?? "").toLowerCase();
66
+ return normalized.includes("parquet") || normalized.includes("octet-stream");
67
+ }
68
+ export function buildDataCatalogOfferOperation(route, operation, apiBasePrefix = "/api") {
69
+ return {
70
+ ...operation,
71
+ path: buildApiInteractionPath(route, apiBasePrefix),
72
+ };
73
+ }
74
+ export function defineDataCatalogOfferSource(input) {
75
+ return input;
76
+ }
77
+ export function buildServiceApiInteractions(processName, definitions) {
78
+ return Object.fromEntries(Object.entries(definitions).map(([key, definition]) => [
79
+ key,
80
+ defineApiInteraction({
81
+ id: String(key),
82
+ topic: definition.topic ?? "system",
83
+ asset: definition.asset ?? "service",
84
+ objectType: definition.objectType ?? "runtime",
85
+ objectId: definition.objectId ?? processName,
86
+ attribute: definition.attribute,
87
+ method: definition.method ?? "GET",
88
+ routeOnly: definition.publishInUnsTree !== true,
89
+ registryTopic: "service-endpoints",
90
+ options: buildApiInteractionOptions(definition.method ?? "GET", definition.description, definition.tags, definition.queryParams, definition.requestBody, definition.publishInUnsTree !== true, "service-endpoints", buildServiceApiPayload(String(key), definition)),
91
+ handler: definition.handler,
92
+ }),
93
+ ]));
94
+ }
95
+ export function buildApiInteractionsFromDataOfferSources(sources) {
96
+ return Object.fromEntries(Object.entries(sources).map(([key, source]) => [
97
+ key,
98
+ defineApiInteraction({
99
+ id: source.offerId,
100
+ topic: source.topic,
101
+ asset: source.asset,
102
+ objectType: source.objectType,
103
+ objectId: source.objectId,
104
+ attribute: source.attribute,
105
+ method: source.method ?? "GET",
106
+ routeOnly: true,
107
+ registryTopic: "data-offer-endpoints",
108
+ options: buildApiInteractionOptions(source.method ?? "GET", source.apiDescription ?? source.description, source.tags, source.queryParams, source.requestBody, true, "data-offer-endpoints"),
109
+ handler: source.handler,
110
+ }),
111
+ ]));
112
+ }
113
+ export function buildDataCatalogOffersFromSources(sources) {
114
+ return Object.values(sources).map((source) => ({
115
+ offerId: source.offerId,
116
+ displayName: source.displayName,
117
+ description: source.description,
118
+ ...(source.owner ? { owner: source.owner } : {}),
119
+ status: source.status ?? "available",
120
+ ...(source.tags ? { tags: source.tags } : {}),
121
+ ...(source.categories ? { categories: source.categories } : {}),
122
+ schemas: [source.schema],
123
+ operations: [
124
+ buildDataCatalogOfferOperation(source, {
125
+ id: `${source.offerId}-${(source.method ?? "GET").toLowerCase()}`,
126
+ method: source.method ?? "GET",
127
+ summary: source.displayName,
128
+ description: source.description,
129
+ ...(source.tags ? { tags: source.tags } : {}),
130
+ ...(source.queryParams ? { parameters: source.queryParams } : {}),
131
+ ...(source.headers ? { headers: source.headers } : {}),
132
+ ...(source.requestBody !== undefined ? { requestBody: source.requestBody } : {}),
133
+ responses: [
134
+ {
135
+ statusCode: source.response?.statusCode ?? "200",
136
+ ...(source.response?.description ? { description: source.response.description } : { description: source.description }),
137
+ contentType: source.response?.contentType ?? source.schema.contentType ?? null,
138
+ schemas: [source.schema],
139
+ ...(source.response?.examplePayloads ? { examplePayloads: source.response.examplePayloads } : {}),
140
+ ...(source.response?.headers ? { headers: source.response.headers } : { headers: [] }),
141
+ },
142
+ ],
143
+ }),
144
+ ],
145
+ }));
146
+ }
147
+ export function projectRowsForDataCatalogSchema(rows, schema) {
148
+ const leafFields = (schema.fields ?? []).filter((field) => field.type !== "array");
149
+ return rows.map((row) => {
150
+ const projected = {};
151
+ for (const field of leafFields) {
152
+ const outputKey = field.name;
153
+ const sourceKey = field.sourceKey?.trim() || field.name;
154
+ projected[outputKey] = getRowValue(row, sourceKey);
155
+ }
156
+ return projected;
157
+ });
158
+ }
159
+ export function buildSchemaFieldPathIndex(schema) {
160
+ return (schema.fields ?? []).map((field) => ({
161
+ path: field.path ?? field.name,
162
+ name: field.name,
163
+ }));
164
+ }
165
+ function inferSchemaRootType(contentType, fields) {
166
+ if (contentType.includes("parquet") || contentType.includes("octet-stream")) {
167
+ return "table";
168
+ }
169
+ if (fields.some((field) => (field.path ?? field.name).includes("[]"))) {
170
+ return "object";
171
+ }
172
+ return "object";
173
+ }
174
+ function buildSchemaFieldPath(name, type, fieldPathPrefix) {
175
+ if (fieldPathPrefix && type !== "array") {
176
+ return `${fieldPathPrefix}.${name}`;
177
+ }
178
+ return name;
179
+ }
180
+ function buildApiInteractionOptions(method, description, tags, queryParams, requestBody, routeOnly = true, registryTopic = "api-endpoints", serviceApi) {
181
+ if (method === "GET") {
182
+ return {
183
+ apiDescription: description,
184
+ ...(routeOnly ? { routeOnly: true } : {}),
185
+ ...(registryTopic ? { registryTopic } : {}),
186
+ ...(serviceApi ? { serviceApi } : {}),
187
+ ...(tags ? { tags } : {}),
188
+ ...(queryParams
189
+ ? {
190
+ queryParams: queryParams
191
+ .filter((parameter) => parameter.in === "query")
192
+ .map((parameter) => ({
193
+ name: parameter.name,
194
+ type: parameter.type === "number" || parameter.type === "boolean"
195
+ ? parameter.type
196
+ : "string",
197
+ required: parameter.required,
198
+ ...(parameter.description ? { description: parameter.description } : {}),
199
+ })),
200
+ }
201
+ : {}),
202
+ };
203
+ }
204
+ const requestSchema = requestBody?.schemas?.[0]
205
+ ? buildJsonSchemaFromCatalogSchema(requestBody.schemas[0])
206
+ : null;
207
+ return {
208
+ apiDescription: description,
209
+ ...(routeOnly ? { routeOnly: true } : {}),
210
+ ...(registryTopic ? { registryTopic } : {}),
211
+ ...(serviceApi ? { serviceApi } : {}),
212
+ ...(tags ? { tags } : {}),
213
+ ...(requestBody
214
+ ? {
215
+ requestBody: {
216
+ required: requestBody.required ?? true,
217
+ ...(requestBody.description ? { description: requestBody.description } : {}),
218
+ ...(requestSchema && typeof requestSchema === "object" ? { schema: requestSchema } : {}),
219
+ },
220
+ }
221
+ : {}),
222
+ };
223
+ }
224
+ function buildServiceApiPayload(id, definition) {
225
+ const schemas = definition.schema ? [definition.schema] : [];
226
+ const response = definition.response ?? {
227
+ statusCode: "200",
228
+ description: definition.description,
229
+ ...(definition.schema?.contentType ? { contentType: definition.schema.contentType } : {}),
230
+ ...(definition.schema ? { schemas: [definition.schema] } : {}),
231
+ };
232
+ return {
233
+ id,
234
+ summary: definition.description,
235
+ description: definition.description,
236
+ tags: definition.tags ?? [],
237
+ parameters: definition.queryParams ?? [],
238
+ headers: [],
239
+ requestBody: definition.requestBody ?? null,
240
+ responses: [response],
241
+ schemas,
242
+ };
243
+ }
244
+ function getRowValue(row, key) {
245
+ if (Object.prototype.hasOwnProperty.call(row, key)) {
246
+ return row[key];
247
+ }
248
+ const normalizedKey = key.toLowerCase();
249
+ const match = Object.keys(row).find((candidate) => candidate.toLowerCase() === normalizedKey);
250
+ return match ? row[match] : null;
251
+ }
252
+ function buildJsonSchemaFromCatalogSchema(schema) {
253
+ const leafFields = (schema.fields ?? []).filter((field) => field.type !== "array");
254
+ const properties = Object.fromEntries(leafFields.map((field) => [
255
+ field.name,
256
+ {
257
+ type: toJsonSchemaType(field.type ?? "string"),
258
+ ...(field.description ? { description: field.description } : {}),
259
+ ...(field.format ? { format: field.format } : {}),
260
+ ...(field.example !== undefined ? { example: field.example } : {}),
261
+ ...(Array.isArray(field.enumValues) && field.enumValues.length ? { enum: field.enumValues } : {}),
262
+ },
263
+ ]));
264
+ const required = leafFields.filter((field) => field.required === true).map((field) => field.name);
265
+ return {
266
+ type: "object",
267
+ ...(required.length ? { required } : {}),
268
+ properties,
269
+ };
270
+ }
271
+ function toJsonSchemaType(type) {
272
+ switch (type) {
273
+ case "number":
274
+ case "integer":
275
+ case "boolean":
276
+ case "array":
277
+ case "object":
278
+ return type;
279
+ default:
280
+ return "string";
281
+ }
282
+ }
283
+ function buildExamplePayloadsFromFields(fields) {
284
+ const arrayFields = fields.filter((field) => field.type === "array");
285
+ const scalarFields = fields.filter((field) => field.type !== "array");
286
+ if (!scalarFields.length) {
287
+ return undefined;
288
+ }
289
+ if (arrayFields.length === 1 && isGenericListField(arrayFields[0].name)) {
290
+ const listField = arrayFields[0];
291
+ const rowFields = scalarFields.filter((field) => field.name !== "count");
292
+ if (!rowFields.length || rowFields.some((field) => field.example === undefined)) {
293
+ return undefined;
294
+ }
295
+ const payload = {
296
+ [listField.name]: [Object.fromEntries(rowFields.map((field) => [field.name, field.example]))],
297
+ };
298
+ const countField = scalarFields.find((field) => field.name === "count");
299
+ if (countField) {
300
+ payload[countField.name] = countField.example ?? 1;
301
+ }
302
+ return [payload];
303
+ }
304
+ if (scalarFields.some((field) => field.example === undefined)) {
305
+ return undefined;
306
+ }
307
+ return [Object.fromEntries(scalarFields.map((field) => [field.name, field.example]))];
308
+ }
309
+ function isGenericListField(name) {
310
+ return name === "data" || name === "rows";
311
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { default } from "./uns-api-plugin.js";
2
2
  export { UnsApiProxy, type UnsProxyProcessWithApi } from "./uns-api-plugin.js";
3
3
  export * from "./api-interfaces.js";
4
+ export * from "./api-event-routing.js";
5
+ export * from "./parquet.js";
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export { default } from "./uns-api-plugin.js";
2
2
  export { UnsApiProxy } from "./uns-api-plugin.js";
3
3
  export * from "./api-interfaces.js";
4
+ export * from "./api-event-routing.js";
5
+ export * from "./parquet.js";
@@ -0,0 +1,8 @@
1
+ import type { DataCatalogSchemaRegistration } from "./api-interfaces.js";
2
+ export declare function writeSchemaRowsToParquet(input: {
3
+ rows: Array<Record<string, unknown>>;
4
+ schema: DataCatalogSchemaRegistration;
5
+ outputDir?: string;
6
+ fileName?: string;
7
+ }): Promise<string | null>;
8
+ export declare function buildParquetSchemaFromCatalogSchema(schema: DataCatalogSchemaRegistration): any;
@@ -0,0 +1,61 @@
1
+ import { randomUUID } from "crypto";
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import { ParquetSchema, ParquetWriter } from "parquets";
6
+ import logger from "@uns-kit/core/logger.js";
7
+ const PARQUET_TYPE_MAP = {
8
+ string: "UTF8",
9
+ number: "DOUBLE",
10
+ integer: "INT64",
11
+ boolean: "BOOLEAN",
12
+ date: "TIMESTAMP_MILLIS",
13
+ "date-time": "TIMESTAMP_MILLIS",
14
+ };
15
+ export async function writeSchemaRowsToParquet(input) {
16
+ try {
17
+ const outputDir = input.outputDir ?? path.join(os.tmpdir(), "uns-data-offers");
18
+ const fileName = input.fileName ?? `${randomUUID()}.parquet`;
19
+ const filePath = path.join(outputDir, fileName);
20
+ if (!fs.existsSync(outputDir)) {
21
+ fs.mkdirSync(outputDir, { recursive: true });
22
+ }
23
+ const writer = await ParquetWriter.openFile(buildParquetSchemaFromCatalogSchema(input.schema), filePath);
24
+ for (const row of input.rows) {
25
+ await writer.appendRow(normalizeParquetRow(row));
26
+ }
27
+ await writer.close();
28
+ return filePath;
29
+ }
30
+ catch (error) {
31
+ logger.error("Failed to write schema-driven parquet:", error);
32
+ return null;
33
+ }
34
+ }
35
+ export function buildParquetSchemaFromCatalogSchema(schema) {
36
+ const schemaDef = {};
37
+ for (const field of schema.fields ?? []) {
38
+ const parquetType = toParquetType(field.type ?? "string", field.format ?? null);
39
+ if (!parquetType) {
40
+ continue;
41
+ }
42
+ schemaDef[field.name] = {
43
+ type: parquetType,
44
+ optional: field.required !== true,
45
+ };
46
+ }
47
+ return new ParquetSchema(schemaDef);
48
+ }
49
+ function toParquetType(type, format) {
50
+ if (format && PARQUET_TYPE_MAP[format]) {
51
+ return PARQUET_TYPE_MAP[format];
52
+ }
53
+ return PARQUET_TYPE_MAP[type] ?? null;
54
+ }
55
+ function normalizeParquetRow(row) {
56
+ const normalized = {};
57
+ for (const [key, value] of Object.entries(row)) {
58
+ normalized[key] = value instanceof Date ? new Date(value) : value;
59
+ }
60
+ return normalized;
61
+ }
@@ -22,9 +22,18 @@ const unsApiPlugin = ({ define }) => {
22
22
  unsApiProxy.event.on("unsProxyProducedApiEndpoints", (event) => {
23
23
  internals.processMqttProxy.publish(event.statusTopic, JSON.stringify(event.producedApiEndpoints));
24
24
  });
25
+ unsApiProxy.event.on("unsProxyProducedServiceEndpoints", (event) => {
26
+ internals.processMqttProxy.publish(event.statusTopic, JSON.stringify(event.producedServiceEndpoints));
27
+ });
28
+ unsApiProxy.event.on("unsProxyProducedDataOfferEndpoints", (event) => {
29
+ internals.processMqttProxy.publish(event.statusTopic, JSON.stringify(event.producedDataOfferEndpoints));
30
+ });
25
31
  unsApiProxy.event.on("unsProxyProducedApiCatchAll", (event) => {
26
32
  internals.processMqttProxy.publish(event.statusTopic, JSON.stringify(event.producedCatchall));
27
33
  });
34
+ unsApiProxy.event.on("unsProxyProducedDataCatalogOffers", (event) => {
35
+ internals.processMqttProxy.publish(event.statusTopic, JSON.stringify(event.producedDataCatalogOffers));
36
+ });
28
37
  unsApiProxy.event.on("mqttProxyStatus", (event) => {
29
38
  const time = UnsPacket.formatToISO8601(new Date());
30
39
  const unsMessage = { data: { time, value: event.value, uom: event.uom } };
@@ -4,6 +4,7 @@ import { UnsTopics } from "@uns-kit/core/uns/uns-topics.js";
4
4
  import { IApiProxyOptions, IGetEndpointOptions, IPostEndpointOptions } from "@uns-kit/core/uns/uns-interfaces.js";
5
5
  import { UnsAsset } from "@uns-kit/core/uns/uns-asset.js";
6
6
  import { UnsObjectType, UnsObjectId } from "@uns-kit/core/uns/uns-object.js";
7
+ import type { DataCatalogOfferRegistration } from "./api-interfaces.js";
7
8
  export default class UnsApiProxy extends UnsProxy {
8
9
  instanceName: string;
9
10
  private topicBuilder;
@@ -18,7 +19,9 @@ export default class UnsApiProxy extends UnsProxy {
18
19
  private startedAt;
19
20
  private statusInterval;
20
21
  private readonly statusIntervalMs;
22
+ private readonly dataCatalogOffers;
21
23
  constructor(processName: string, instanceName: string, options: IApiProxyOptions);
24
+ getProcessName(): string;
22
25
  /**
23
26
  * Unregister endpoint
24
27
  * @param topic - The API topic
@@ -51,20 +54,27 @@ export default class UnsApiProxy extends UnsProxy {
51
54
  tags?: string[];
52
55
  queryParams?: IGetEndpointOptions["queryParams"];
53
56
  }): Promise<void>;
54
- /**
55
- * Register a POST endpoint.
56
- * @param topic - The API topic
57
- * @param attribute - The attribute for the topic.
58
- * @param options.apiDescription - Optional description.
59
- * @param options.tags - Optional tags.
60
- * @param options.requestBody - Optional request body schema for Swagger.
61
- */
62
57
  post(topic: UnsTopics, asset: UnsAsset, objectType: UnsObjectType, objectId: UnsObjectId, attribute: UnsAttribute, options?: IPostEndpointOptions): Promise<void>;
58
+ put(topic: UnsTopics, asset: UnsAsset, objectType: UnsObjectType, objectId: UnsObjectId, attribute: UnsAttribute, options?: IPostEndpointOptions): Promise<void>;
59
+ patch(topic: UnsTopics, asset: UnsAsset, objectType: UnsObjectType, objectId: UnsObjectId, attribute: UnsAttribute, options?: IPostEndpointOptions): Promise<void>;
60
+ delete(topic: UnsTopics, asset: UnsAsset, objectType: UnsObjectType, objectId: UnsObjectId, attribute: UnsAttribute, options?: IPostEndpointOptions): Promise<void>;
61
+ private registerMutationEndpoint;
62
+ registerDataOffer(input: DataCatalogOfferRegistration): void;
63
63
  private emitStatusMetrics;
64
64
  private registerHealthEndpoint;
65
+ private getApiEventName;
65
66
  private extractBearerToken;
66
67
  private getPublicKeyFromJwks;
67
68
  private fetchJwksKeys;
68
69
  private certFromX5c;
69
70
  stop(): Promise<void>;
71
+ private publishDataCatalogOffer;
72
+ private emitDataCatalogOffers;
73
+ private normalizeDataOfferOperation;
74
+ private normalizeDataOfferParameter;
75
+ private normalizeDataOfferRequestBody;
76
+ private normalizeDataOfferResponse;
77
+ private normalizeDataOfferSchema;
78
+ private normalizeDataOfferSchemaField;
79
+ private resolveOfferSchemas;
70
80
  }