@microtronics/studio-cli 0.40.0 → 0.42.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.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,28 @@
1
1
  # Changelog
2
2
 
3
- ## 0.40.0 (2025-11-11)
3
+ ## 0.42.0 (2026-01-13)
4
4
 
5
5
  ### Features
6
6
 
7
+ * add isIotApp helper function to check IoT app project type ([98ce8a1](https://bitbucket.org/microtronics-core/vscode-studio/commits/98ce8a1358bdd4d2ba1635cd4ef1a3f6e8ec0004))
8
+ * add POST endpoint for addon timeseries data insertion ([ace6880](https://bitbucket.org/microtronics-core/vscode-studio/commits/ace6880f801faefc303a8020c7acf87b06db5709))
9
+
10
+ ### Bug Fixes
11
+
12
+ * restrict write access rejection to IoT apps only ([13aa98f](https://bitbucket.org/microtronics-core/vscode-studio/commits/13aa98f261632b576681a894ecb375b7ab5a4b99))
13
+
14
+ ## 0.41.0 (2026-01-12)
15
+
16
+ ### Features
17
+
18
+ * add validation for maxInstancesPerSite manifest property ([065dc98](https://bitbucket.org/microtronics-core/vscode-studio/commits/065dc9809e8dccb75b74c5b52a58ce4ec15b5860))
19
+ * use manifest maxInstancesPerSite for addon package header ([0350d2a](https://bitbucket.org/microtronics-core/vscode-studio/commits/0350d2af8ee3aae0ddbb00d7af5cd0b1c2c4cdd5))
20
+ * validate manifest before building package ([e70fc0e](https://bitbucket.org/microtronics-core/vscode-studio/commits/e70fc0ef9c89849ce2c19ec0a9bb575a764a3b07))
21
+
22
+ ## 0.40.0 (2025-11-11)
23
+
24
+ ### Features
25
+
7
26
  * add lowercase property mapping for timeseries containers ([24f575e](https://bitbucket.org/microtronics-core/vscode-studio/commits/24f575efa418417c5fd612e55bd17f5fb80972cf))
8
27
 
9
28
  ## 0.39.0 (2025-11-10)
@@ -2183,6 +2183,7 @@ export declare namespace Manifest {
2183
2183
  description: string;
2184
2184
  type: ProjectTypes;
2185
2185
  legacyProject?: boolean;
2186
+ maxInstancesPerSite?: number;
2186
2187
  icon?: string;
2187
2188
  dpid?: DeviceProfiles;
2188
2189
  engines?: {
@@ -2273,6 +2274,11 @@ export declare namespace Manifest {
2273
2274
  * @param manifest
2274
2275
  */
2275
2276
  export function isApp(manifest: Manifest.Manifest): boolean;
2277
+ /**
2278
+ * Check if the current project is an iot app project
2279
+ * @param manifest
2280
+ */
2281
+ export function isIotApp(manifest: Manifest.Manifest): boolean;
2276
2282
  /**
2277
2283
  * Check if the current project is an analytics project
2278
2284
  * @param manifest
package/out/api.js CHANGED
@@ -50,19 +50,14 @@ Object.defineProperty(exports, "Log", { enumerable: true, get: function () { ret
50
50
  * @return {Promise<void>} A promise that resolves when the installation process completes successfully or logs an error if it fails.
51
51
  */
52
52
  async function install(cwd, fs, env, apiToken, dependencies) {
53
- try {
54
- if (dependencies.length > 0) {
55
- for (const dependency of dependencies) {
56
- const [libraryId, version] = dependency.split('@');
57
- await dependencies_1.Dependencies.installDependency(cwd, fs, env, apiToken, libraryId, version || null);
58
- }
59
- }
60
- else {
61
- return await dependencies_1.Dependencies.installDependencies(cwd, fs, env, apiToken);
53
+ if (dependencies.length > 0) {
54
+ for (const dependency of dependencies) {
55
+ const [libraryId, version] = dependency.split('@');
56
+ await dependencies_1.Dependencies.installDependency(cwd, fs, env, apiToken, libraryId, version || null);
62
57
  }
63
58
  }
64
- catch (e) {
65
- console.error(e);
59
+ else {
60
+ return await dependencies_1.Dependencies.installDependencies(cwd, fs, env, apiToken);
66
61
  }
67
62
  }
68
63
  /**
@@ -78,6 +73,7 @@ async function install(cwd, fs, env, apiToken, dependencies) {
78
73
  * @return {Promise<Object>} A promise resolved with an object containing diagnostics information for the specified project parts: `dde`, `dlo`, and `dfiles`.
79
74
  */
80
75
  async function build(cwd, fs, logger, env, apiToken = null, selectedParts = true, errorWithDiagnostics) {
76
+ await manifest_1.Manifest.validateBasicManifest(cwd, fs, null);
81
77
  return package_1.Package.buildAll(cwd, fs, logger, env, apiToken, selectedParts, errorWithDiagnostics);
82
78
  }
83
79
  /**
@@ -495,7 +495,7 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
495
495
  apiPathOptions = [
496
496
  {
497
497
  path: `/api/1/sites/{siteUid}/addons/{addonUid}/${containerType}/${containerDefinition.title}`,
498
- params: [addonIdRef],
498
+ params: [contextIdRef, addonIdRef],
499
499
  tags: [containerRouteTagName]
500
500
  }
501
501
  ];
@@ -511,7 +511,8 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
511
511
  apiPathOptions.push(appCenterPathOptions);
512
512
  const apiPathMethods = {
513
513
  get: undefined,
514
- put: undefined
514
+ put: undefined,
515
+ post: undefined
515
516
  };
516
517
  const getMethod = {
517
518
  tags: [],
@@ -610,6 +611,41 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
610
611
  ...defaultResponseCodes
611
612
  }
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
+ }
613
649
  // only site and addons support timeseries
614
650
  const pathOptions = apiPathOptions[0];
615
651
  const methods = structuredClone(apiPathMethods);
@@ -634,7 +670,9 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
634
670
  };
635
671
  youngestMethod.get.summary = `Get the youngest record of "${containerDefinition.title}" container`;
636
672
  youngestMethod.get.responses['200'] = histdataSingleResponse;
637
- youngestMethod.get.parameters[1].schema.$ref = `#/components/schemas/${containerDefinition.title}RequestSingle`;
673
+ const parameterIdx = isAddonProject ? 2 : 1;
674
+ youngestMethod.get.parameters[parameterIdx].schema.$ref =
675
+ `#/components/schemas/${containerDefinition.title}RequestSingle`;
638
676
  apiDescription.paths[youngestPath] = youngestMethod;
639
677
  const oldestPath = `${pathOptions.path}/oldest`;
640
678
  const oldestMethod = {
@@ -642,7 +680,8 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
642
680
  };
643
681
  oldestMethod.get.summary = `Get the oldest record of "${containerDefinition.title}" container`;
644
682
  oldestMethod.get.responses['200'] = histdataSingleResponse;
645
- oldestMethod.get.parameters[1].schema.$ref = `#/components/schemas/${containerDefinition.title}RequestSingle`;
683
+ oldestMethod.get.parameters[parameterIdx].schema.$ref =
684
+ `#/components/schemas/${containerDefinition.title}RequestSingle`;
646
685
  apiDescription.paths[oldestPath] = oldestMethod;
647
686
  }
648
687
  }
@@ -68,7 +68,8 @@ function generateXMLFromMap(manifest, ddeMap) {
68
68
  xmlAttr('download', '0');
69
69
  }
70
70
  // set edit=99 on upward containers to reject write access via API
71
- if ((manifest_1.Manifest.isApp(manifest) || manifest_1.Manifest.isLibrary(manifest)) &&
71
+ // only for IoT apps and libraries. For addons or analytics this is not needed.
72
+ if ((manifest_1.Manifest.isIotApp(manifest) || manifest_1.Manifest.isLibrary(manifest)) &&
72
73
  [TransferDirection.up, TransferDirection.upPlus].includes(containerDefinition.transferDirection)) {
73
74
  xmlAttr('edit', '99');
74
75
  }
package/out/manifest.js CHANGED
@@ -161,6 +161,17 @@ var Manifest;
161
161
  return manifest.type === ProjectTypes.app;
162
162
  }
163
163
  Manifest.isApp = isApp;
164
+ /**
165
+ * Check if the current project is an iot app project
166
+ * @param manifest
167
+ */
168
+ function isIotApp(manifest) {
169
+ if (manifest.type === undefined) {
170
+ return true;
171
+ }
172
+ return manifest.type === ProjectTypes.app;
173
+ }
174
+ Manifest.isIotApp = isIotApp;
164
175
  /**
165
176
  * Check if the current project is an analytics project
166
177
  * @param manifest
@@ -183,6 +194,7 @@ var Manifest;
183
194
  validateProjectVersion(manifest);
184
195
  validatePOVSettings(manifest);
185
196
  validateBLOSettings(manifest);
197
+ validateMaxInstancesPerSite(manifest);
186
198
  }
187
199
  Manifest.validateBasicManifest = validateBasicManifest;
188
200
  function validateLibraryName(libraryName) {
@@ -433,6 +445,27 @@ function validateAddonOrAnalyticsSupported(manifest) {
433
445
  throw new Error(`For project type "${manifest.type}", "dpid" is not supported!"`);
434
446
  }
435
447
  }
448
+ function validateMaxInstancesPerSite(manifest) {
449
+ const { maxInstancesPerSite } = manifest;
450
+ // If maxInstancesPerSite is not defined, check if it's required
451
+ if (maxInstancesPerSite === undefined) {
452
+ if (Manifest.isAddon(manifest)) {
453
+ throw new Error('Property "maxInstancesPerSite" is required for addon projects!');
454
+ }
455
+ return;
456
+ }
457
+ // If maxInstancesPerSite is defined, validate it's only allowed for addon projects
458
+ if (!Manifest.isAddon(manifest)) {
459
+ throw new Error(`Property "maxInstancesPerSite" is only allowed for addon projects!`);
460
+ }
461
+ // Validate the value
462
+ if (!Number.isInteger(maxInstancesPerSite)) {
463
+ throw new Error('Property "maxInstancesPerSite" must be an integer!');
464
+ }
465
+ if (maxInstancesPerSite < 1 || maxInstancesPerSite > 100) {
466
+ throw new Error('Property "maxInstancesPerSite" must be between 1 and 100!');
467
+ }
468
+ }
436
469
  /**
437
470
  * Check based on the current env if the given version string is correct
438
471
  * @param manifest
@@ -54,7 +54,7 @@ var APM;
54
54
  tagHeader.required_hwfw = undefined;
55
55
  tagHeader.required_productcode = undefined;
56
56
  tagHeader.addon_max_sources = 1;
57
- tagHeader.addon_max_per_site = 1;
57
+ tagHeader.addon_max_per_site = manifest.maxInstancesPerSite;
58
58
  }
59
59
  return tagHeader;
60
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microtronics/studio-cli",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "Microtronics Studio CLI Tool",
5
5
  "main": "./out/api.js",
6
6
  "typings": "./dist/studio-cli.d.ts",