@microtronics/studio-cli 0.4.1 → 0.5.1

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.
@@ -0,0 +1,551 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.generateAPIFromMap = generateAPIFromMap;
27
+ const semver = __importStar(require("semver"));
28
+ const yaml_1 = require("yaml");
29
+ const coreDefinitions_1 = require("../coreDefinitions");
30
+ var ContainerType = coreDefinitions_1.DDE_Core.ContainerType;
31
+ var TransferDirection = coreDefinitions_1.DDE_Core.TransferDirection;
32
+ var fieldTypeDefinitions = coreDefinitions_1.DDE_Core.fieldTypeDefinitions;
33
+ const contextIdRef = { $ref: '#/components/parameters/contextId' };
34
+ const histdataResponseRef = { $ref: '#/components/schemas/histdataResponse' };
35
+ const histdataRequestRef = { $ref: '#/components/schemas/histdataRequest' };
36
+ const rapidM2MStampRef = { $ref: '#/components/schemas/rapidM2MStamp' };
37
+ const timeseriesRequestBaseRef = { $ref: '#/components/schemas/timeseriesRequestBase' };
38
+ const RAPIDM2M_STAMP = 'rapidM2M Stamp';
39
+ const successfulOperationResponse = {
40
+ description: 'Successful operation'
41
+ };
42
+ // default path context parameter
43
+ const defaultComponents = {
44
+ parameters: {
45
+ contextId: {
46
+ name: 'siteUid',
47
+ in: 'path',
48
+ description: 'The site uid',
49
+ schema: {
50
+ type: 'string'
51
+ },
52
+ required: true
53
+ }
54
+ },
55
+ schemas: {
56
+ rapidM2MStamp: {
57
+ type: 'string',
58
+ format: 'YYYYMMDDhhmmssSSS',
59
+ description: 'Special format of an UTC timestamp. Important: The trailing 0 are truncated. eg.: "2024-02-10" -> "2024021"'
60
+ }
61
+ }
62
+ };
63
+ const defaultAppComponents = {
64
+ ...structuredClone(defaultComponents)
65
+ };
66
+ Object.assign(defaultAppComponents.schemas, {
67
+ histdataResponse: {
68
+ type: 'array',
69
+ description: 'Array of histdata records. Array starts with the youngest record',
70
+ items: {
71
+ type: 'array',
72
+ description: 'First item is always the rapidM2M timestamp of the record. All following values are in the order of the "select" array.',
73
+ items: {
74
+ anyOf: [rapidM2MStampRef, { type: 'string' }, { type: 'number' }]
75
+ }
76
+ }
77
+ },
78
+ histdataRequest: {
79
+ type: 'object',
80
+ additionalProperties: false,
81
+ required: ['from', 'until'],
82
+ properties: {
83
+ from: {
84
+ type: 'string',
85
+ format: RAPIDM2M_STAMP,
86
+ description: 'items to include must be older or equal, UTC w/o time saving.'
87
+ },
88
+ until: {
89
+ type: 'string',
90
+ format: RAPIDM2M_STAMP,
91
+ description: 'items to include must be younger, UTC w/o time saving. Reply set is limited like that: from <= item.stamp < until.'
92
+ },
93
+ count: {
94
+ type: 'integer',
95
+ description: 'limits the number of returned records, negative values will count the records from the end of the result set.'
96
+ },
97
+ offset: {
98
+ type: 'integer',
99
+ description: 'skips the given number of records in the result set.'
100
+ }
101
+ }
102
+ },
103
+ timeseriesRequestBase: {
104
+ type: 'object',
105
+ additionalProperties: false,
106
+ properties: {
107
+ count: {
108
+ type: 'integer',
109
+ description: 'limits the number of returned records, negative values will count the records from the end of the result set.',
110
+ minimum: -1000,
111
+ maximum: 1000,
112
+ default: 1000
113
+ },
114
+ from: {
115
+ type: 'string',
116
+ format: RAPIDM2M_STAMP,
117
+ description: 'items to include must be older or equal, UTC w/o time saving'
118
+ },
119
+ until: {
120
+ type: 'string',
121
+ format: RAPIDM2M_STAMP,
122
+ description: 'items to include must be younger, UTC w/o time saving. Reply set is limited like that: from <= item.stamp < until'
123
+ },
124
+ offset: {
125
+ type: 'integer',
126
+ description: 'Skips the given number of records in the result set',
127
+ minimum: 0,
128
+ default: 0
129
+ }
130
+ }
131
+ }
132
+ });
133
+ const defaultErrorResponse = {
134
+ content: {
135
+ 'application/json': {
136
+ schema: {
137
+ type: 'object',
138
+ additionalProperties: false,
139
+ properties: {
140
+ err: {
141
+ type: 'string',
142
+ description: 'Information what went wrong'
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ };
149
+ const defaultResponseCodes = {
150
+ 401: {
151
+ description: 'Permission denied',
152
+ ...defaultErrorResponse
153
+ },
154
+ 403: {
155
+ description: 'Forbidden',
156
+ ...defaultErrorResponse
157
+ },
158
+ 404: {
159
+ description: 'Resource not found',
160
+ ...defaultErrorResponse
161
+ },
162
+ 500: {
163
+ description: 'Internal error',
164
+ ...defaultErrorResponse
165
+ }
166
+ };
167
+ function generateAPIFromMap(ddeMap, manifest, context = null) {
168
+ const apiVersion = semver.coerce(manifest.version)?.version;
169
+ const apiDescription = {
170
+ openapi: '3.0.3',
171
+ info: { description: manifest.description, title: manifest.name, version: apiVersion || '' },
172
+ tags: [],
173
+ paths: {},
174
+ components: { parameters: {}, schemas: {} }
175
+ };
176
+ if (context) {
177
+ Object.assign(apiDescription.components, structuredClone(defaultComponents));
178
+ }
179
+ else {
180
+ Object.assign(apiDescription.components, structuredClone(defaultAppComponents));
181
+ }
182
+ for (const containerDefinition of Object.values(ddeMap.containers)) {
183
+ if ((context &&
184
+ (!containerDefinition.libraries.includes(context) || containerDefinition.containerType === ContainerType.hx)) ||
185
+ (!context && !coreDefinitions_1.DDE_Core.hasContainerAppFields(containerDefinition)) ||
186
+ containerDefinition.containerType === ContainerType.aloha) {
187
+ continue;
188
+ }
189
+ generateTagDescriptionFromContainer(apiDescription, containerDefinition);
190
+ generateSchemasFromContainer(apiDescription, containerDefinition, context);
191
+ generatePathInformationFromContainer(apiDescription, containerDefinition);
192
+ // legacy routes
193
+ generateLegacySchemasFromContainer(apiDescription, containerDefinition, context);
194
+ generateLegacyPathInformationFromContainer(apiDescription, containerDefinition);
195
+ }
196
+ return (0, yaml_1.stringify)(apiDescription);
197
+ }
198
+ function generateTagDescriptionFromContainer(apiDescription, containerDefinition) {
199
+ apiDescription.tags.push({
200
+ name: tagNameFromContainerTitle(containerDefinition.title),
201
+ description: `Transfer direction ${containerDefinition.transferDirection}`
202
+ });
203
+ }
204
+ function tagNameFromContainerTitle(title) {
205
+ //todo maybe change to camelCase?
206
+ return title.split('_').join(' ');
207
+ }
208
+ function generateLegacySchemasFromContainer(apiDescription, containerDefinition, context) {
209
+ let schemaName = `${containerDefinition.name}`;
210
+ const schema = {
211
+ type: 'object',
212
+ description: `Container: "${containerDefinition.title}"`,
213
+ additionalProperties: false,
214
+ properties: {}
215
+ };
216
+ const requiredProperies = [];
217
+ // handle legacy histdata
218
+ if (containerDefinition.containerType === ContainerType.hx) {
219
+ schemaName += `Select`;
220
+ requiredProperies.push('select');
221
+ schema.properties['select'] = {
222
+ type: 'array',
223
+ items: {
224
+ type: 'string',
225
+ enum: []
226
+ }
227
+ };
228
+ }
229
+ for (const field of containerDefinition.fields) {
230
+ if (field.library !== context || field.type === 'shadow') {
231
+ continue;
232
+ }
233
+ if (containerDefinition.containerType === ContainerType.hx) {
234
+ schema.properties.select.items.enum.push(field.id);
235
+ }
236
+ else {
237
+ schema.properties[field.id] = getApiDefinitionForField(field);
238
+ // validate for read or write permission
239
+ if (!field.readPermission && !field.writePermission) {
240
+ requiredProperies.push(field.id);
241
+ }
242
+ }
243
+ }
244
+ schema.required = requiredProperies.length ? requiredProperies : undefined;
245
+ apiDescription.components.schemas[schemaName] = schema;
246
+ }
247
+ function generateSchemasFromContainer(apiDescription, containerDefinition, context = null) {
248
+ const schemaName = `${containerDefinition.title}`;
249
+ const schema = {
250
+ type: 'object',
251
+ description: `Container: "${containerDefinition.title}" with device transfer direction "${containerDefinition.transferDirection}"`,
252
+ additionalProperties: false,
253
+ properties: {}
254
+ };
255
+ const requiredProperties = [];
256
+ for (const field of containerDefinition.fields) {
257
+ if (field.library !== context || field.type === 'shadow') {
258
+ continue;
259
+ }
260
+ schema.properties[field.name] = getApiDefinitionForField(field);
261
+ // validate for read or write permission
262
+ if (!field.readPermission && !field.writePermission) {
263
+ requiredProperties.push(field.name);
264
+ }
265
+ }
266
+ schema.required = requiredProperties.length ? requiredProperties : undefined;
267
+ apiDescription.components.schemas[schemaName] = schema;
268
+ if (containerDefinition.containerType === ContainerType.hx) {
269
+ apiDescription.components.schemas[`${schemaName}Request`] = {
270
+ allOf: [
271
+ timeseriesRequestBaseRef,
272
+ {
273
+ type: 'object',
274
+ description: `Request schema of "${containerDefinition.title}"`,
275
+ additionalProperties: false,
276
+ properties: {
277
+ select: {
278
+ type: 'array',
279
+ description: 'Optional selection of specific fields of the given container',
280
+ items: {
281
+ type: 'string',
282
+ enum: Object.keys(schema.properties)
283
+ }
284
+ }
285
+ }
286
+ }
287
+ ]
288
+ };
289
+ }
290
+ }
291
+ function getApiDefinitionForField(field) {
292
+ const typeDefinition = fieldTypeDefinitions[field.type];
293
+ const schemaDefinition = { ...typeDefinition.openApi };
294
+ const defaultValue = parseDefaultValueFromField(field);
295
+ schemaDefinition.default = defaultValue;
296
+ schemaDefinition.example = defaultValue;
297
+ schemaDefinition.description = field.description;
298
+ return schemaDefinition;
299
+ }
300
+ function parseDefaultValueFromField(field) {
301
+ if (field.defaultValue === undefined) {
302
+ return field.defaultValue;
303
+ }
304
+ if (field.type === 'binary') {
305
+ return `${field.defaultValue.substring(2)}`;
306
+ }
307
+ return field.defaultValue;
308
+ }
309
+ function generateLegacyPathInformationFromContainer(apiDescription, containerDefinition) {
310
+ let apiPath = `/api/1/sites/{siteUid}/${containerDefinition.name}`;
311
+ const apiPathMethods = {
312
+ get: undefined,
313
+ put: undefined
314
+ };
315
+ if ([ContainerType.cx, ContainerType.cb].includes(containerDefinition.containerType)) {
316
+ // add get methods
317
+ apiPathMethods.get = {
318
+ deprecated: true,
319
+ tags: [tagNameFromContainerTitle(containerDefinition.title)],
320
+ summary: `Retrieve the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
321
+ parameters: [contextIdRef],
322
+ responses: {
323
+ '200': {
324
+ ...successfulOperationResponse,
325
+ content: {
326
+ 'application/json': {
327
+ schema: {
328
+ allOf: [
329
+ {
330
+ type: 'object',
331
+ required: ['stamp'],
332
+ properties: {
333
+ stamp: rapidM2MStampRef
334
+ }
335
+ },
336
+ {
337
+ $ref: `#/components/schemas/${containerDefinition.name}`
338
+ }
339
+ ]
340
+ }
341
+ }
342
+ }
343
+ },
344
+ ...defaultResponseCodes
345
+ }
346
+ };
347
+ if ([TransferDirection.down, TransferDirection.backendOnly].includes(containerDefinition.transferDirection)) {
348
+ apiPathMethods.put = {
349
+ deprecated: true,
350
+ tags: [tagNameFromContainerTitle(containerDefinition.title)],
351
+ summary: `Modify the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
352
+ parameters: [contextIdRef],
353
+ requestBody: {
354
+ content: {
355
+ 'application/json': {
356
+ schema: {
357
+ $ref: `#/components/schemas/${containerDefinition.name}`
358
+ }
359
+ }
360
+ }
361
+ },
362
+ responses: {
363
+ 201: {
364
+ ...successfulOperationResponse
365
+ },
366
+ 204: {
367
+ ...successfulOperationResponse
368
+ },
369
+ ...defaultResponseCodes
370
+ }
371
+ };
372
+ }
373
+ }
374
+ else if (containerDefinition.containerType === ContainerType.hx) {
375
+ const timeSeriesPath = `${apiPath}`;
376
+ // youngest histdata record:
377
+ apiPath += `/youngest`;
378
+ apiPathMethods.get = {
379
+ deprecated: true,
380
+ tags: [tagNameFromContainerTitle(containerDefinition.title)],
381
+ summary: `Retrieve the youngest data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
382
+ parameters: [
383
+ contextIdRef,
384
+ {
385
+ name: 'json',
386
+ in: 'query',
387
+ required: true,
388
+ schema: {
389
+ $ref: `#/components/schemas/${containerDefinition.name}Select`
390
+ }
391
+ }
392
+ ],
393
+ responses: {
394
+ '200': {
395
+ ...successfulOperationResponse,
396
+ content: {
397
+ 'application/json': {
398
+ schema: histdataResponseRef
399
+ }
400
+ }
401
+ },
402
+ ...defaultResponseCodes
403
+ }
404
+ };
405
+ apiDescription.paths[timeSeriesPath] = {
406
+ get: {
407
+ deprecated: true,
408
+ tags: [tagNameFromContainerTitle(containerDefinition.title)],
409
+ summary: `Get the historical data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container.`,
410
+ parameters: [
411
+ contextIdRef,
412
+ {
413
+ name: 'json',
414
+ in: 'query',
415
+ required: true,
416
+ schema: {
417
+ allOf: [{ $ref: `#/components/schemas/${containerDefinition.name}Select` }, histdataRequestRef]
418
+ }
419
+ }
420
+ ],
421
+ responses: {
422
+ '200': {
423
+ ...successfulOperationResponse,
424
+ content: {
425
+ 'application/json': {
426
+ schema: histdataResponseRef
427
+ }
428
+ }
429
+ },
430
+ ...defaultResponseCodes
431
+ }
432
+ }
433
+ };
434
+ }
435
+ apiDescription.paths[apiPath] = apiPathMethods;
436
+ }
437
+ function generatePathInformationFromContainer(apiDescription, containerDefinition) {
438
+ const isTimeseriesContainer = containerDefinition.containerType === ContainerType.hx;
439
+ const containerType = isTimeseriesContainer ? 'timeseries' : 'containers';
440
+ const apiPath = `/api/1/sites/{siteUid}/${containerType}/${containerDefinition.title}`;
441
+ const apiPathMethods = {
442
+ get: undefined,
443
+ put: undefined
444
+ };
445
+ const getMethod = {
446
+ tags: [tagNameFromContainerTitle(containerDefinition.title)],
447
+ summary: `Retrieve the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
448
+ parameters: [contextIdRef],
449
+ responses: {
450
+ '200': {
451
+ ...successfulOperationResponse,
452
+ content: {
453
+ 'application/json': {
454
+ schema: {
455
+ allOf: [
456
+ {
457
+ type: 'object',
458
+ required: ['stamp'],
459
+ properties: {
460
+ stamp: rapidM2MStampRef
461
+ }
462
+ },
463
+ {
464
+ $ref: `#/components/schemas/${containerDefinition.title}`
465
+ }
466
+ ]
467
+ }
468
+ }
469
+ }
470
+ },
471
+ ...defaultResponseCodes
472
+ }
473
+ };
474
+ if ([ContainerType.cx, ContainerType.cb].includes(containerDefinition.containerType)) {
475
+ // add get methods
476
+ apiPathMethods.get = getMethod;
477
+ if ([TransferDirection.down, TransferDirection.backendOnly].includes(containerDefinition.transferDirection)) {
478
+ apiPathMethods.put = {
479
+ tags: [tagNameFromContainerTitle(containerDefinition.title)],
480
+ summary: `Modify the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
481
+ parameters: [contextIdRef],
482
+ requestBody: {
483
+ content: {
484
+ 'application/json': {
485
+ schema: {
486
+ $ref: `#/components/schemas/${containerDefinition.title}`
487
+ }
488
+ }
489
+ }
490
+ },
491
+ responses: {
492
+ 201: {
493
+ ...successfulOperationResponse
494
+ },
495
+ 204: {
496
+ ...successfulOperationResponse
497
+ },
498
+ ...defaultResponseCodes
499
+ }
500
+ };
501
+ }
502
+ apiDescription.paths[apiPath] = apiPathMethods;
503
+ }
504
+ if (containerDefinition.containerType === ContainerType.hx) {
505
+ apiPathMethods.get = {
506
+ tags: [tagNameFromContainerTitle(containerDefinition.title)],
507
+ summary: `Get the historical data of "${containerDefinition.title}"`,
508
+ parameters: [
509
+ contextIdRef,
510
+ {
511
+ name: 'json',
512
+ in: 'query',
513
+ required: false,
514
+ schema: {
515
+ $ref: `#/components/schemas/${containerDefinition.title}Request`
516
+ }
517
+ }
518
+ ],
519
+ responses: {
520
+ '200': {
521
+ ...successfulOperationResponse,
522
+ content: {
523
+ 'application/json': {
524
+ schema: {
525
+ type: 'array',
526
+ items: {
527
+ $ref: `#/components/schemas/${containerDefinition.title}`
528
+ }
529
+ }
530
+ }
531
+ }
532
+ },
533
+ ...defaultResponseCodes
534
+ }
535
+ };
536
+ apiDescription.paths[apiPath] = apiPathMethods;
537
+ const youngestPath = `${apiPath}/youngest`;
538
+ const youngestMethod = {
539
+ get: getMethod
540
+ };
541
+ youngestMethod.get.summary = `Get the youngest record of "${containerDefinition.title}" container`;
542
+ apiDescription.paths[youngestPath] = youngestMethod;
543
+ const oldestPath = `${apiPath}/oldest`;
544
+ const oldestMethod = {
545
+ get: getMethod
546
+ };
547
+ oldestMethod.get.summary = `Get the oldest record of "${containerDefinition.title}" container`;
548
+ apiDescription.paths[oldestPath] = oldestMethod;
549
+ }
550
+ }
551
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dlo.d.ts","sourceRoot":"","sources":["../../../src/dde/fileGenerators/dlo.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;AAUlC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAqBxC,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,UA+LlH;AAED,qBAAa,gBAAgB;IAWhB,OAAO,CAAC,MAAM;IAAW,OAAO,CAAC,OAAO;IAVpD,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,UAAU,CAA+F;gBAC7F,MAAM,EAAE,OAAO,EAAU,OAAO,EAAE,IAAI,GAAG,MAAM;IAGnE,OAAO,CAAC,IAAI;IAiBL,QAAQ;;;;IAoCf,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,8BAA8B;IAoEtC,OAAO,CAAC,uBAAuB;IAY/B,OAAO,CAAC,yBAAyB;IAgBjC,OAAO,CAAC,uBAAuB;IA8D/B,OAAO,CAAC,6BAA6B;IASrC,OAAO,CAAC,2BAA2B;IAmBnC;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,0BAA0B;IA8BlC,OAAO,CAAC,mBAAmB;IAiD3B;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IAIpC,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,sBAAsB;IAmC9B,OAAO,CAAC,iBAAiB;IAoDzB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;CA6B7B;AAGD,wBAAgB,4BAA4B,CAC3C,MAAM,EAAE,OAAO;AACf;;GAEG;AACH,gBAAgB,EAAE,MAAM,EAAE,UAsI1B;AAID,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,UAqExD"}