@c8y/ngx-components 1018.0.147 → 1018.0.151

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.
Files changed (30) hide show
  1. package/core/common/date.pipe.d.ts +2 -2
  2. package/core/common/stringify-object.pipe.d.ts +2 -1
  3. package/ecosystem/application-properties/update-application-modal/update-application-modal.component.d.ts +0 -2
  4. package/ecosystem/packages/deploy-application/deploy-application.component.d.ts +7 -6
  5. package/ecosystem/shared/ecosystem.constants.d.ts +3 -1
  6. package/ecosystem/shared/ecosystem.model.d.ts +3 -1
  7. package/ecosystem/shared/ecosystem.service.d.ts +11 -5
  8. package/esm2020/core/common/date.pipe.mjs +12 -7
  9. package/esm2020/core/common/stringify-object.pipe.mjs +11 -8
  10. package/esm2020/ecosystem/application-properties/application-properties.component.mjs +6 -4
  11. package/esm2020/ecosystem/application-properties/update-application-modal/update-application-modal.component.mjs +11 -22
  12. package/esm2020/ecosystem/applications/install-from-package/install-from-package.component.mjs +2 -2
  13. package/esm2020/ecosystem/packages/deploy-application/deploy-application.component.mjs +28 -85
  14. package/esm2020/ecosystem/shared/ecosystem.constants.mjs +5 -3
  15. package/esm2020/ecosystem/shared/ecosystem.model.mjs +3 -1
  16. package/esm2020/ecosystem/shared/ecosystem.service.mjs +121 -24
  17. package/fesm2015/c8y-ngx-components-ecosystem-shared.mjs +139 -27
  18. package/fesm2015/c8y-ngx-components-ecosystem-shared.mjs.map +1 -1
  19. package/fesm2015/c8y-ngx-components-ecosystem.mjs +110 -179
  20. package/fesm2015/c8y-ngx-components-ecosystem.mjs.map +1 -1
  21. package/fesm2015/c8y-ngx-components.mjs +20 -13
  22. package/fesm2015/c8y-ngx-components.mjs.map +1 -1
  23. package/fesm2020/c8y-ngx-components-ecosystem-shared.mjs +126 -25
  24. package/fesm2020/c8y-ngx-components-ecosystem-shared.mjs.map +1 -1
  25. package/fesm2020/c8y-ngx-components-ecosystem.mjs +110 -176
  26. package/fesm2020/c8y-ngx-components-ecosystem.mjs.map +1 -1
  27. package/fesm2020/c8y-ngx-components.mjs +20 -13
  28. package/fesm2020/c8y-ngx-components.mjs.map +1 -1
  29. package/locales/locales.pot +15 -3
  30. package/package.json +1 -1
@@ -6,7 +6,7 @@ import * as i2 from '@c8y/ngx-components';
6
6
  import { gettext, PackageType, Status, DropAreaComponent, C8yStepper, CoreModule, hookWizard } from '@c8y/ngx-components';
7
7
  import * as i2$1 from '@ngx-translate/core';
8
8
  import { saveAs } from 'file-saver';
9
- import { groupBy, uniqBy, get, pick, kebabCase, cloneDeep } from 'lodash-es';
9
+ import { groupBy, uniqBy, get, pick, omit, isUndefined, kebabCase, cloneDeep } from 'lodash-es';
10
10
  import { BehaviorSubject, defer, merge, combineLatest } from 'rxjs';
11
11
  import { map, shareReplay, debounceTime, take, distinctUntilKeyChanged, tap, filter, switchMap } from 'rxjs/operators';
12
12
  import { gt, coerce } from 'semver';
@@ -36,6 +36,7 @@ var ERROR_TYPE;
36
36
  (function (ERROR_TYPE) {
37
37
  ERROR_TYPE["TYPE_VALIDATION"] = "TYPE_VALIDATION";
38
38
  ERROR_TYPE["ALREADY_SUBSCRIBED"] = "ALREADY_SUBSCRIBED";
39
+ ERROR_TYPE["ALREADY_EXIST"] = "ALREADY_EXIST";
39
40
  ERROR_TYPE["INTERNAL_ERROR"] = "INTERNAL_ERROR";
40
41
  ERROR_TYPE["NO_MANIFEST_FILE"] = "NO_MANIFEST_FILE";
41
42
  ERROR_TYPE["INVALID_PACKAGE"] = "INVALID_PACKAGE";
@@ -43,6 +44,7 @@ var ERROR_TYPE;
43
44
  ERROR_TYPE["MICROSERVICE_NAME_TOO_LONG"] = "MICROSERVICE_NAME_TOO_LONG";
44
45
  ERROR_TYPE["APPLICATION_CREATION_FAILED"] = "APPLICATION_CREATION_FAILED";
45
46
  ERROR_TYPE["KEY_OR_CONTEXT_PATH_MISMATCH"] = "KEY_OR_CONTEXT_PATH_MISMATCH";
47
+ ERROR_TYPE["VERSION_NOT_FOUND"] = "VERSION_NOT_FOUND";
46
48
  })(ERROR_TYPE || (ERROR_TYPE = {}));
47
49
  const PRODUCT_EXPERIENCE = {
48
50
  APPLICATIONS: {
@@ -95,6 +97,7 @@ const PRODUCT_EXPERIENCE = {
95
97
  };
96
98
 
97
99
  const ERROR_MESSAGES = {
100
+ [ERROR_TYPE.ALREADY_EXIST]: gettext('Could not deploy the application, as the application with the same name, context-path or key exist already.'),
98
101
  [ERROR_TYPE.TYPE_VALIDATION]: gettext('Wrong file format. Expected a *.zip file with a valid manifest.'),
99
102
  [ERROR_TYPE.ALREADY_SUBSCRIBED]: gettext('Could not subscribe to the microservice because another application with the same context path is already subscribed.'),
100
103
  [ERROR_TYPE.NO_MANIFEST_FILE]: gettext('Could not find a manifest.'),
@@ -103,7 +106,8 @@ const ERROR_MESSAGES = {
103
106
  [ERROR_TYPE.INTERNAL_ERROR]: gettext('An internal error occurred, try to upload again.'),
104
107
  [ERROR_TYPE.MICROSERVICE_NAME_TOO_LONG]: gettext('Microservice name "{{ name }}" must not be longer than {{ maxChars }} characters.'),
105
108
  [ERROR_TYPE.APPLICATION_CREATION_FAILED]: gettext('Application creation failed.'),
106
- [ERROR_TYPE.KEY_OR_CONTEXT_PATH_MISMATCH]: gettext('The contextPath or key of the uploaded archive do not match with the existing app.')
109
+ [ERROR_TYPE.KEY_OR_CONTEXT_PATH_MISMATCH]: gettext('The "contextPath`KEEP_ORIGINAL`" or "key`KEEP_ORIGINAL`" of the uploaded archive do not match with the existing application.'),
110
+ [ERROR_TYPE.VERSION_NOT_FOUND]: gettext('The selected version was not found on the server.')
107
111
  };
108
112
  const APP_STATE = {
109
113
  SUBSCRIBED: {
@@ -158,7 +162,7 @@ const PACKAGE_TYPE_LABELS = {
158
162
  };
159
163
  const packageProperties = [
160
164
  {
161
- label: gettext('Version'),
165
+ label: gettext('Latest version'),
162
166
  key: 'version'
163
167
  },
164
168
  {
@@ -323,6 +327,14 @@ class EcosystemService {
323
327
  return webApps.sort((a, b) => a.name.localeCompare(b.name));
324
328
  }
325
329
  async getPackageApplications(customFilter = {}) {
330
+ const filterCallback = app => this.isPackage(app);
331
+ return this.getApplicationsFiltered(customFilter, filterCallback);
332
+ }
333
+ async getHostedAndPackageApplications(customFilter = {}) {
334
+ const filterCallback = app => this.isPackage(app) || this.isApplication(app);
335
+ return this.getApplicationsFiltered(customFilter, filterCallback);
336
+ }
337
+ async getApplicationsFiltered(customFilter = {}, filterCallback = (app) => !!app) {
326
338
  const filter = Object.assign({}, customFilter);
327
339
  const sharedFilter = Object.assign({
328
340
  availability: ApplicationAvailability.SHARED,
@@ -333,7 +345,7 @@ class EcosystemService {
333
345
  this.getApplications(filter),
334
346
  this.applicationService.list(sharedFilter)
335
347
  ]);
336
- const webApps = [...apps, ...shared].filter(app => this.isPackage(app));
348
+ const webApps = [...apps, ...shared].filter(filterCallback);
337
349
  // an app could be subscribed to a tenant, but also have it's availability set to SHARED, in that case it would occur twice.
338
350
  const uniqWebApps = uniqBy(webApps, (app) => app.id);
339
351
  return uniqWebApps.sort((a, b) => a.name.localeCompare(b.name));
@@ -416,18 +428,6 @@ class EcosystemService {
416
428
  };
417
429
  return config;
418
430
  }
419
- async updateAppManifest(application, sourcePackage) {
420
- const { id } = application;
421
- const cleanedApp = this.removeAppProperties(application);
422
- if (!cleanedApp.manifest) {
423
- cleanedApp.manifest = {};
424
- }
425
- cleanedApp.manifest.isPackage = false;
426
- cleanedApp.manifest.source = sourcePackage.id;
427
- return await this.applicationService
428
- .binary(id)
429
- .updateFiles([{ path: CUMULOCITY_JSON, contents: JSON.stringify(cleanedApp) }]);
430
- }
431
431
  async listArchives(appId) {
432
432
  const filter = {
433
433
  pageSize: 100
@@ -635,6 +635,78 @@ class EcosystemService {
635
635
  this.alertError(ex);
636
636
  }
637
637
  }
638
+ async deployApp(selectedPackage, formGroupValue, model) {
639
+ // Create new app config
640
+ const config = this.createConfig(selectedPackage, formGroupValue);
641
+ const requestedVersion = model.selected.version;
642
+ let cleanManifest;
643
+ try {
644
+ const manifest = await this.applicationService.getAppManifest(selectedPackage, requestedVersion);
645
+ cleanManifest = omit(manifest, ['name', 'contextPath', 'key']);
646
+ }
647
+ catch (ex) {
648
+ throw new EcosystemError(ERROR_TYPE.VERSION_NOT_FOUND);
649
+ }
650
+ config.isSetup = true;
651
+ config.manifest = cleanManifest;
652
+ config.availability = ApplicationAvailability.PRIVATE;
653
+ config.manifest.isPackage = false;
654
+ config.manifest.source = selectedPackage.id;
655
+ config.manifest.package = 'blueprint';
656
+ // because of a issue with SHARED availability we always should check again
657
+ // if the app not exist already
658
+ const allExistingApps = await this.getHostedAndPackageApplications();
659
+ const doesAppKeyOrContextPathExist = this.checkIfAppNameKeyPathExists(allExistingApps, config);
660
+ if (doesAppKeyOrContextPathExist) {
661
+ throw new EcosystemError(ERROR_TYPE.ALREADY_EXIST);
662
+ }
663
+ // Create new app
664
+ const newApp = (await this.applicationService.create(config)).data;
665
+ try {
666
+ // Binary upload can fail if SHARED package
667
+ // in this case, catch error and fall back to
668
+ // clone API.
669
+ await this.uploadBinaryFromOtherPackage(selectedPackage, newApp, model.selected.binaryId, requestedVersion);
670
+ }
671
+ catch (error) {
672
+ if (error?.res?.status === 404) {
673
+ await this.fallbackToClone(newApp, selectedPackage, requestedVersion);
674
+ }
675
+ }
676
+ return newApp;
677
+ }
678
+ async fallbackToClone(application, selectedPackage, requestedVersion) {
679
+ let wasSuccess = true;
680
+ let clonedPkg;
681
+ try {
682
+ clonedPkg = (await this.applicationService.clone(selectedPackage, requestedVersion)).data;
683
+ await this.uploadBinaryFromOtherPackage(selectedPackage, application, clonedPkg.activeVersionId, requestedVersion, clonedPkg);
684
+ }
685
+ catch (error) {
686
+ this.alertError(error);
687
+ wasSuccess = false;
688
+ }
689
+ finally {
690
+ await this.deleteApp(clonedPkg, true);
691
+ }
692
+ return wasSuccess;
693
+ }
694
+ async uploadBinaryFromOtherPackage(selectedPackage, applicationToUploadBinaryTo, binaryId, requestedVersion, useBinariesFrom) {
695
+ const { data: binaryDetails } = await this.inventoryService.detail(binaryId);
696
+ // Get binary from specific package version
697
+ const binary = await this.getBinary(useBinariesFrom || selectedPackage, {
698
+ id: binaryId
699
+ });
700
+ // Create zip
701
+ const fileBinary = new Blob([binary], { type: binaryDetails.contentType });
702
+ const file = new File([fileBinary], binaryDetails.name, {
703
+ type: binaryDetails.contentType
704
+ });
705
+ // Upload binary to new app
706
+ await this.uploadArchiveToApp(file, applicationToUploadBinaryTo);
707
+ // update the app manifest
708
+ await this.updateAppManifest(applicationToUploadBinaryTo, selectedPackage, requestedVersion);
709
+ }
638
710
  getAppState(app) {
639
711
  if (!this.isOwner(app)) {
640
712
  return APP_STATE.SUBSCRIBED;
@@ -722,6 +794,43 @@ class EcosystemService {
722
794
  throw new EcosystemError(ERROR_TYPE.KEY_OR_CONTEXT_PATH_MISMATCH);
723
795
  }
724
796
  }
797
+ filterContainString(name, filterTerm) {
798
+ const term = filterTerm.toLowerCase().trim();
799
+ return name && name.toLowerCase().indexOf(term) > -1;
800
+ }
801
+ async updateAppManifest(application, selectedPackage, requestedVersion) {
802
+ const { id } = application;
803
+ const cleanedApp = this.removeAppProperties(application);
804
+ let manifest = selectedPackage.manifest || {};
805
+ if (requestedVersion) {
806
+ try {
807
+ const versionedAppManifest = await this.applicationService.getAppManifest(selectedPackage, requestedVersion);
808
+ manifest = versionedAppManifest;
809
+ }
810
+ catch (ex) {
811
+ throw new EcosystemError(ERROR_TYPE.VERSION_NOT_FOUND);
812
+ }
813
+ }
814
+ cleanedApp.manifest = manifest;
815
+ cleanedApp.manifest.isPackage = false;
816
+ cleanedApp.manifest.source = selectedPackage.id;
817
+ return await this.applicationService
818
+ .binary(id)
819
+ .updateFiles([{ path: CUMULOCITY_JSON, contents: JSON.stringify(cleanedApp) }]);
820
+ }
821
+ checkIfAppNameKeyPathExists(existingApps, app, retryNo) {
822
+ if (isUndefined(retryNo)) {
823
+ return existingApps.find(existingApp => existingApp.name === app.name ||
824
+ existingApp.key === app.key ||
825
+ existingApp.contextPath === app.contextPath);
826
+ }
827
+ return existingApps.find(existingApp => existingApp.name === app.name ||
828
+ existingApp.key === app.key ||
829
+ existingApp.contextPath === app.contextPath ||
830
+ existingApp.name === [app.name, retryNo].join('-') ||
831
+ existingApp.key === [app.key, retryNo].join('-') ||
832
+ existingApp.contextPath === [app.contextPath, retryNo].join('-'));
833
+ }
725
834
  getAppKey(appModel, name) {
726
835
  let key = appModel?.key;
727
836
  if (!key) {
@@ -758,17 +867,9 @@ class EcosystemService {
758
867
  }
759
868
  return baseName;
760
869
  }
761
- checkIfAppNameKeyPathExists(existingApps, app, retryNo) {
762
- return existingApps.find(existingApp => existingApp.name === app.name ||
763
- existingApp.key === app.key ||
764
- existingApp.contextPath === app.contextPath ||
765
- existingApp.name === [app.name, retryNo].join('-') ||
766
- existingApp.key === [app.key, retryNo].join('-') ||
767
- existingApp.contextPath === [app.contextPath, retryNo].join('-'));
768
- }
769
870
  removeAppProperties(app) {
770
871
  const tempApp = cloneDeep(app);
771
- const propertiesToRemove = ['id', 'owner', 'activeVersionId', 'self'];
872
+ const propertiesToRemove = ['id', 'owner', 'activeVersionId', 'self', 'type'];
772
873
  propertiesToRemove.forEach(prop => delete tempApp[prop]);
773
874
  return tempApp;
774
875
  }