@microtronics/studio-cli 0.60.0 → 0.62.0

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,688 +0,0 @@
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 manifest_1 = require("../../manifest");
34
- const contextIdRef = { $ref: '#/components/parameters/contextId' };
35
- const addonIdRef = { $ref: '#/components/parameters/addonId' };
36
- const appIdRef = { $ref: '#/components/parameters/appId' };
37
- const histdataResponseRef = { $ref: '#/components/schemas/histdataResponse' };
38
- const histdataRequestRef = { $ref: '#/components/schemas/histdataRequest' };
39
- const rapidM2MStampRef = { $ref: '#/components/schemas/rapidM2MStamp' };
40
- const timeseriesRequestBaseRef = { $ref: '#/components/schemas/timeseriesRequestBase' };
41
- const RAPIDM2M_STAMP = 'rapidM2M Stamp';
42
- const successfulOperationResponse = {
43
- description: 'Successful operation'
44
- };
45
- // default path context parameter
46
- const defaultComponents = {
47
- parameters: {
48
- contextId: {
49
- name: 'siteUid',
50
- in: 'path',
51
- description: 'The site uid',
52
- schema: {
53
- type: 'string'
54
- },
55
- required: true
56
- },
57
- appId: {
58
- name: 'appUid',
59
- in: 'path',
60
- description: 'The app uid',
61
- schema: {
62
- type: 'string'
63
- },
64
- required: true
65
- }
66
- },
67
- schemas: {
68
- rapidM2MStamp: {
69
- type: 'string',
70
- format: 'YYYYMMDDhhmmssSSS',
71
- description: 'Special format of an UTC timestamp. Important: The trailing 0 are truncated. eg.: "2024-02-10" -> "2024021"'
72
- }
73
- }
74
- };
75
- const defaultAppComponents = {
76
- ...structuredClone(defaultComponents)
77
- };
78
- Object.assign(defaultAppComponents.schemas, {
79
- histdataResponse: {
80
- type: 'array',
81
- description: 'Array of histdata records. Array starts with the youngest record',
82
- items: {
83
- type: 'array',
84
- description: 'First item is always the rapidM2M timestamp of the record. All following values are in the order of the "select" array.',
85
- items: {
86
- anyOf: [rapidM2MStampRef, { type: 'string' }, { type: 'number' }]
87
- }
88
- }
89
- },
90
- histdataRequest: {
91
- type: 'object',
92
- additionalProperties: false,
93
- required: ['from', 'until'],
94
- properties: {
95
- from: {
96
- type: 'string',
97
- format: RAPIDM2M_STAMP,
98
- description: 'items to include must be older or equal, UTC w/o time saving.'
99
- },
100
- until: {
101
- type: 'string',
102
- format: RAPIDM2M_STAMP,
103
- description: 'items to include must be younger, UTC w/o time saving. Reply set is limited like that: from <= item.stamp < until.'
104
- },
105
- count: {
106
- type: 'integer',
107
- description: 'limits the number of returned records, negative values will count the records from the end of the result set.'
108
- },
109
- offset: {
110
- type: 'integer',
111
- description: 'skips the given number of records in the result set.'
112
- }
113
- }
114
- },
115
- timeseriesRequestBase: {
116
- type: 'object',
117
- additionalProperties: false,
118
- properties: {
119
- count: {
120
- type: 'integer',
121
- description: 'limits the number of returned records, negative values will count the records from the end of the result set.',
122
- minimum: -1000,
123
- maximum: 1000,
124
- default: 1000
125
- },
126
- from: {
127
- type: 'string',
128
- format: RAPIDM2M_STAMP,
129
- description: 'items to include must be older or equal, UTC w/o time saving'
130
- },
131
- until: {
132
- type: 'string',
133
- format: RAPIDM2M_STAMP,
134
- description: 'items to include must be younger, UTC w/o time saving. Reply set is limited like that: from <= item.stamp < until'
135
- },
136
- offset: {
137
- type: 'integer',
138
- description: 'Skips the given number of records in the result set',
139
- minimum: 0,
140
- default: 0
141
- }
142
- }
143
- }
144
- });
145
- const defaultErrorResponse = {
146
- content: {
147
- 'application/json': {
148
- schema: {
149
- type: 'object',
150
- additionalProperties: false,
151
- properties: {
152
- err: {
153
- type: 'string',
154
- description: 'Information what went wrong'
155
- }
156
- }
157
- }
158
- }
159
- }
160
- };
161
- const defaultResponseCodes = {
162
- 401: {
163
- description: 'Permission denied',
164
- ...defaultErrorResponse
165
- },
166
- 403: {
167
- description: 'Forbidden',
168
- ...defaultErrorResponse
169
- },
170
- 404: {
171
- description: 'Resource not found',
172
- ...defaultErrorResponse
173
- },
174
- 500: {
175
- description: 'Internal error',
176
- ...defaultErrorResponse
177
- }
178
- };
179
- function generateAPIFromMap(ddeMap, manifest, context = null) {
180
- const apiVersion = semver.coerce(manifest.version)?.version;
181
- const apiDescription = {
182
- openapi: '3.0.3',
183
- info: { description: manifest.description, title: manifest.name, version: apiVersion || '' },
184
- tags: [],
185
- paths: {},
186
- components: { parameters: {}, schemas: {} }
187
- };
188
- if (context) {
189
- Object.assign(apiDescription.components, structuredClone(defaultComponents));
190
- }
191
- else {
192
- Object.assign(apiDescription.components, structuredClone(defaultAppComponents));
193
- }
194
- const isAddon = manifest_1.Manifest.isAddon(manifest);
195
- const isAnalytics = manifest_1.Manifest.isAnalytics(manifest);
196
- if (isAddon) {
197
- // add addonUid parameter if this is an addon project
198
- apiDescription.components.parameters['addonId'] = {
199
- name: 'addonUid',
200
- in: 'path',
201
- description: 'The addon uid',
202
- schema: {
203
- type: 'string'
204
- },
205
- required: true
206
- };
207
- }
208
- for (const containerDefinition of Object.values(ddeMap.containers)) {
209
- if ((context &&
210
- (!containerDefinition.libraries.includes(context) || containerDefinition.containerType === ContainerType.hx)) ||
211
- (!context && !coreDefinitions_1.DDE_Core.hasContainerAppFields(containerDefinition)) ||
212
- containerDefinition.containerType === ContainerType.aloha) {
213
- continue;
214
- }
215
- generateTagDescriptionFromContainer(apiDescription, containerDefinition);
216
- generateSchemasFromContainer(apiDescription, containerDefinition, context);
217
- generatePathInformationFromContainer(apiDescription, containerDefinition, isAddon, isAnalytics);
218
- // legacy routes
219
- if (!isAddon && !isAnalytics) {
220
- generateLegacySchemasFromContainer(apiDescription, containerDefinition, context);
221
- generateLegacyPathInformationFromContainer(apiDescription, containerDefinition);
222
- }
223
- }
224
- apiDescription.tags.push({
225
- name: 'app-center',
226
- description: 'App Center related routes'
227
- });
228
- return (0, yaml_1.stringify)(apiDescription);
229
- }
230
- function generateTagDescriptionFromContainer(apiDescription, containerDefinition) {
231
- apiDescription.tags.push({
232
- name: tagNameFromContainerTitle(containerDefinition.title),
233
- description: `Transfer direction ${containerDefinition.transferDirection}`
234
- });
235
- }
236
- function tagNameFromContainerTitle(title) {
237
- //todo maybe change to camelCase?
238
- return title.split('_').join(' ');
239
- }
240
- function generateLegacySchemasFromContainer(apiDescription, containerDefinition, context) {
241
- let schemaName = `${containerDefinition.name}`;
242
- const schema = {
243
- type: 'object',
244
- description: `Container: "${containerDefinition.title}"`,
245
- additionalProperties: false,
246
- properties: {}
247
- };
248
- const requiredProperies = [];
249
- // handle legacy histdata
250
- if (containerDefinition.containerType === ContainerType.hx) {
251
- schemaName += `Select`;
252
- requiredProperies.push('select');
253
- schema.properties['select'] = {
254
- type: 'array',
255
- items: {
256
- type: 'string',
257
- enum: []
258
- }
259
- };
260
- }
261
- for (const field of containerDefinition.fields) {
262
- if (field.library !== context || field.type === 'shadow') {
263
- continue;
264
- }
265
- if (containerDefinition.containerType === ContainerType.hx) {
266
- schema.properties.select.items.enum.push(field.id);
267
- }
268
- else {
269
- schema.properties[field.id] = getApiDefinitionForField(field);
270
- // validate for read or write permission
271
- if (!field.readPermission && !field.writePermission) {
272
- requiredProperies.push(field.id);
273
- }
274
- }
275
- }
276
- schema.required = requiredProperies.length ? requiredProperies : undefined;
277
- apiDescription.components.schemas[schemaName] = schema;
278
- }
279
- function generateSchemasFromContainer(apiDescription, containerDefinition, context = null) {
280
- const schemaName = `${containerDefinition.title}`;
281
- const schema = {
282
- type: 'object',
283
- description: `Container: "${containerDefinition.title}" with device transfer direction "${containerDefinition.transferDirection}"`,
284
- additionalProperties: false,
285
- properties: {}
286
- };
287
- const requiredProperties = [];
288
- const isTimeseriesContainer = containerDefinition.containerType === ContainerType.hx;
289
- for (const field of containerDefinition.fields) {
290
- if (field.library !== context || field.type === 'shadow') {
291
- continue;
292
- }
293
- schema.properties[field.name] = getApiDefinitionForField(field);
294
- // validate for read or write permission
295
- if (!field.readPermission && !field.writePermission) {
296
- requiredProperties.push(field.name);
297
- }
298
- }
299
- schema.required = requiredProperties.length ? requiredProperties : undefined;
300
- apiDescription.components.schemas[schemaName] = schema;
301
- const originalProperties = schema.properties;
302
- if (isTimeseriesContainer) {
303
- schema.required = schema.required?.map(name => name.toLowerCase());
304
- const lowerCaseProperties = {};
305
- for (const [key, value] of Object.entries(schema.properties)) {
306
- lowerCaseProperties[key.toLowerCase()] = value;
307
- }
308
- schema.properties = lowerCaseProperties;
309
- }
310
- if (isTimeseriesContainer) {
311
- const timerSeriesSelect = {
312
- type: 'object',
313
- description: `Request schema of "${containerDefinition.title}"`,
314
- additionalProperties: false,
315
- properties: {
316
- select: {
317
- type: 'array',
318
- description: 'Optional selection of specific fields of the given container',
319
- items: {
320
- type: 'string',
321
- enum: Object.keys(originalProperties)
322
- }
323
- }
324
- }
325
- };
326
- apiDescription.components.schemas[`${schemaName}RequestSingle`] = timerSeriesSelect;
327
- apiDescription.components.schemas[`${schemaName}Request`] = {
328
- allOf: [timeseriesRequestBaseRef, timerSeriesSelect]
329
- };
330
- // stamp parameter is always returned if it is a timeseries container
331
- schema.properties['stamp'] = rapidM2MStampRef;
332
- requiredProperties.push('stamp');
333
- }
334
- }
335
- function getApiDefinitionForField(field) {
336
- const typeDefinition = fieldTypeDefinitions[field.type];
337
- const schemaDefinition = { ...typeDefinition.openApi };
338
- const defaultValue = parseDefaultValueFromField(field);
339
- schemaDefinition.default = defaultValue;
340
- schemaDefinition.example = defaultValue;
341
- schemaDefinition.description = field.description;
342
- return schemaDefinition;
343
- }
344
- function parseDefaultValueFromField(field) {
345
- if (field.defaultValue === undefined) {
346
- return field.defaultValue;
347
- }
348
- if (field.type === 'binary') {
349
- return `${field.defaultValue.substring(2)}`;
350
- }
351
- return field.defaultValue;
352
- }
353
- function generateLegacyPathInformationFromContainer(apiDescription, containerDefinition) {
354
- let apiPath = `/api/1/sites/{siteUid}/${containerDefinition.name}`;
355
- const apiPathMethods = {
356
- get: undefined,
357
- put: undefined
358
- };
359
- if ([ContainerType.cx, ContainerType.cb].includes(containerDefinition.containerType)) {
360
- // add get methods
361
- apiPathMethods.get = {
362
- deprecated: true,
363
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
364
- summary: `Retrieve the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
365
- parameters: [contextIdRef],
366
- responses: {
367
- '200': {
368
- ...successfulOperationResponse,
369
- content: {
370
- 'application/json': {
371
- schema: {
372
- allOf: [
373
- {
374
- type: 'object',
375
- required: ['stamp'],
376
- properties: {
377
- stamp: rapidM2MStampRef
378
- }
379
- },
380
- {
381
- $ref: `#/components/schemas/${containerDefinition.name}`
382
- }
383
- ]
384
- }
385
- }
386
- }
387
- },
388
- ...defaultResponseCodes
389
- }
390
- };
391
- if ([TransferDirection.down, TransferDirection.backendOnly].includes(containerDefinition.transferDirection)) {
392
- apiPathMethods.put = {
393
- deprecated: true,
394
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
395
- summary: `Modify the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
396
- parameters: [contextIdRef],
397
- requestBody: {
398
- content: {
399
- 'application/json': {
400
- schema: {
401
- $ref: `#/components/schemas/${containerDefinition.name}`
402
- }
403
- }
404
- }
405
- },
406
- responses: {
407
- 201: {
408
- ...successfulOperationResponse
409
- },
410
- 204: {
411
- ...successfulOperationResponse
412
- },
413
- ...defaultResponseCodes
414
- }
415
- };
416
- }
417
- }
418
- else if (containerDefinition.containerType === ContainerType.hx) {
419
- const timeSeriesPath = `${apiPath}`;
420
- // youngest histdata record:
421
- apiPath += `/youngest`;
422
- apiPathMethods.get = {
423
- deprecated: true,
424
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
425
- summary: `Retrieve the youngest data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
426
- parameters: [
427
- contextIdRef,
428
- {
429
- name: 'json',
430
- in: 'query',
431
- required: true,
432
- schema: {
433
- $ref: `#/components/schemas/${containerDefinition.name}Select`
434
- }
435
- }
436
- ],
437
- responses: {
438
- '200': {
439
- ...successfulOperationResponse,
440
- content: {
441
- 'application/json': {
442
- schema: histdataResponseRef
443
- }
444
- }
445
- },
446
- ...defaultResponseCodes
447
- }
448
- };
449
- apiDescription.paths[timeSeriesPath] = {
450
- get: {
451
- deprecated: true,
452
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
453
- summary: `Get the historical data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container.`,
454
- parameters: [
455
- contextIdRef,
456
- {
457
- name: 'json',
458
- in: 'query',
459
- required: true,
460
- schema: {
461
- allOf: [{ $ref: `#/components/schemas/${containerDefinition.name}Select` }, histdataRequestRef]
462
- }
463
- }
464
- ],
465
- responses: {
466
- '200': {
467
- ...successfulOperationResponse,
468
- content: {
469
- 'application/json': {
470
- schema: histdataResponseRef
471
- }
472
- }
473
- },
474
- ...defaultResponseCodes
475
- }
476
- }
477
- };
478
- }
479
- apiDescription.paths[apiPath] = apiPathMethods;
480
- }
481
- function generatePathInformationFromContainer(apiDescription, containerDefinition, isAddonProject = false, isAnalyticsProject = false) {
482
- const isTimeseriesContainer = containerDefinition.containerType === ContainerType.hx;
483
- const containerType = isTimeseriesContainer ? 'timeseries' : 'containers';
484
- let apiPathOptions = [];
485
- const containerRouteTagName = tagNameFromContainerTitle(containerDefinition.title);
486
- // analytic projects do not have any site
487
- if (!isAnalyticsProject) {
488
- apiPathOptions.push({
489
- path: `/api/1/sites/{siteUid}/${containerType}/${containerDefinition.title}`,
490
- params: [contextIdRef],
491
- tags: [containerRouteTagName]
492
- });
493
- }
494
- if (isAddonProject) {
495
- apiPathOptions = [
496
- {
497
- path: `/api/1/sites/{siteUid}/addons/{addonUid}/${containerType}/${containerDefinition.title}`,
498
- params: [contextIdRef, addonIdRef],
499
- tags: [containerRouteTagName]
500
- }
501
- ];
502
- }
503
- const appCenterPathOptions = {
504
- path: `/api/1/app-center/apps/{appUid}/${containerType}/${containerDefinition.title}`,
505
- params: [appIdRef],
506
- tags: ['app-center']
507
- };
508
- if (isAnalyticsProject) {
509
- appCenterPathOptions.tags.push(containerRouteTagName);
510
- }
511
- apiPathOptions.push(appCenterPathOptions);
512
- const apiPathMethods = {
513
- get: undefined,
514
- put: undefined,
515
- post: undefined
516
- };
517
- const getMethod = {
518
- tags: [],
519
- summary: `Retrieve the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
520
- parameters: [],
521
- responses: {
522
- '200': {
523
- ...successfulOperationResponse,
524
- content: {
525
- 'application/json': {
526
- schema: {
527
- allOf: [
528
- {
529
- type: 'object',
530
- required: ['stamp'],
531
- properties: {
532
- stamp: rapidM2MStampRef
533
- }
534
- },
535
- {
536
- $ref: `#/components/schemas/${containerDefinition.title}`
537
- }
538
- ]
539
- }
540
- }
541
- }
542
- },
543
- ...defaultResponseCodes
544
- }
545
- };
546
- if ([ContainerType.cx, ContainerType.cb].includes(containerDefinition.containerType)) {
547
- // add get methods
548
- apiPathMethods.get = getMethod;
549
- if ([TransferDirection.down, TransferDirection.backendOnly].includes(containerDefinition.transferDirection)) {
550
- apiPathMethods.put = {
551
- tags: [],
552
- summary: `Modify the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
553
- parameters: [],
554
- requestBody: {
555
- content: {
556
- 'application/json': {
557
- schema: {
558
- $ref: `#/components/schemas/${containerDefinition.title}`
559
- }
560
- }
561
- }
562
- },
563
- responses: {
564
- 201: {
565
- ...successfulOperationResponse
566
- },
567
- 204: {
568
- ...successfulOperationResponse
569
- },
570
- ...defaultResponseCodes
571
- }
572
- };
573
- }
574
- apiPathOptions.forEach(path => {
575
- const methods = structuredClone(apiPathMethods);
576
- Object.values(methods).forEach(value => {
577
- value?.parameters?.unshift(...path.params);
578
- value?.tags?.unshift(...path.tags);
579
- });
580
- apiDescription.paths[path.path] = methods;
581
- });
582
- }
583
- if (containerDefinition.containerType === ContainerType.hx) {
584
- apiPathMethods.get = {
585
- tags: [],
586
- summary: `Get the historical data of "${containerDefinition.title}"`,
587
- parameters: [
588
- {
589
- name: 'json',
590
- in: 'query',
591
- required: false,
592
- schema: {
593
- $ref: `#/components/schemas/${containerDefinition.title}Request`
594
- }
595
- }
596
- ],
597
- responses: {
598
- '200': {
599
- ...successfulOperationResponse,
600
- content: {
601
- 'application/json': {
602
- schema: {
603
- type: 'array',
604
- items: {
605
- $ref: `#/components/schemas/${containerDefinition.title}`
606
- }
607
- }
608
- }
609
- }
610
- },
611
- ...defaultResponseCodes
612
- }
613
- };
614
- if (isAddonProject) {
615
- apiPathMethods.post = {
616
- tags: [],
617
- summary: `Insert data into the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
618
- parameters: [
619
- {
620
- name: 'allow_future_date',
621
- in: 'query',
622
- description: 'Allow inserting records with a stamp value in the future',
623
- schema: {
624
- type: 'boolean'
625
- }
626
- }
627
- ],
628
- requestBody: {
629
- content: {
630
- 'application/json': {
631
- schema: {
632
- $ref: `#/components/schemas/${containerDefinition.title}`
633
- }
634
- }
635
- }
636
- },
637
- responses: {
638
- 201: {
639
- ...successfulOperationResponse
640
- },
641
- 400: {
642
- description: 'Bad request',
643
- ...defaultErrorResponse
644
- },
645
- ...defaultResponseCodes
646
- }
647
- };
648
- }
649
- // only site and addons support timeseries
650
- const pathOptions = apiPathOptions[0];
651
- const methods = structuredClone(apiPathMethods);
652
- Object.values(methods).forEach(value => {
653
- value?.parameters?.unshift(...pathOptions.params);
654
- value?.tags?.unshift(...pathOptions.tags);
655
- });
656
- apiDescription.paths[pathOptions.path] = methods;
657
- const histdataSingleResponse = {
658
- ...successfulOperationResponse,
659
- content: {
660
- 'application/json': {
661
- schema: {
662
- $ref: `#/components/schemas/${containerDefinition.title}`
663
- }
664
- }
665
- }
666
- };
667
- const youngestPath = `${pathOptions.path}/youngest`;
668
- const youngestMethod = {
669
- get: structuredClone(methods.get)
670
- };
671
- youngestMethod.get.summary = `Get the youngest record of "${containerDefinition.title}" container`;
672
- youngestMethod.get.responses['200'] = histdataSingleResponse;
673
- const parameterIdx = isAddonProject ? 2 : 1;
674
- youngestMethod.get.parameters[parameterIdx].schema.$ref =
675
- `#/components/schemas/${containerDefinition.title}RequestSingle`;
676
- apiDescription.paths[youngestPath] = youngestMethod;
677
- const oldestPath = `${pathOptions.path}/oldest`;
678
- const oldestMethod = {
679
- get: structuredClone(methods.get)
680
- };
681
- oldestMethod.get.summary = `Get the oldest record of "${containerDefinition.title}" container`;
682
- oldestMethod.get.responses['200'] = histdataSingleResponse;
683
- oldestMethod.get.parameters[parameterIdx].schema.$ref =
684
- `#/components/schemas/${containerDefinition.title}RequestSingle`;
685
- apiDescription.paths[oldestPath] = oldestMethod;
686
- }
687
- }
688
- //# sourceMappingURL=api.js.map