@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.
- package/core/common/date.pipe.d.ts +2 -2
- package/core/common/stringify-object.pipe.d.ts +2 -1
- package/ecosystem/application-properties/update-application-modal/update-application-modal.component.d.ts +0 -2
- package/ecosystem/packages/deploy-application/deploy-application.component.d.ts +7 -6
- package/ecosystem/shared/ecosystem.constants.d.ts +3 -1
- package/ecosystem/shared/ecosystem.model.d.ts +3 -1
- package/ecosystem/shared/ecosystem.service.d.ts +11 -5
- package/esm2020/core/common/date.pipe.mjs +12 -7
- package/esm2020/core/common/stringify-object.pipe.mjs +11 -8
- package/esm2020/ecosystem/application-properties/application-properties.component.mjs +6 -4
- package/esm2020/ecosystem/application-properties/update-application-modal/update-application-modal.component.mjs +11 -22
- package/esm2020/ecosystem/applications/install-from-package/install-from-package.component.mjs +2 -2
- package/esm2020/ecosystem/packages/deploy-application/deploy-application.component.mjs +28 -85
- package/esm2020/ecosystem/shared/ecosystem.constants.mjs +5 -3
- package/esm2020/ecosystem/shared/ecosystem.model.mjs +3 -1
- package/esm2020/ecosystem/shared/ecosystem.service.mjs +121 -24
- package/fesm2015/c8y-ngx-components-ecosystem-shared.mjs +139 -27
- package/fesm2015/c8y-ngx-components-ecosystem-shared.mjs.map +1 -1
- package/fesm2015/c8y-ngx-components-ecosystem.mjs +110 -179
- package/fesm2015/c8y-ngx-components-ecosystem.mjs.map +1 -1
- package/fesm2015/c8y-ngx-components.mjs +20 -13
- package/fesm2015/c8y-ngx-components.mjs.map +1 -1
- package/fesm2020/c8y-ngx-components-ecosystem-shared.mjs +126 -25
- package/fesm2020/c8y-ngx-components-ecosystem-shared.mjs.map +1 -1
- package/fesm2020/c8y-ngx-components-ecosystem.mjs +110 -176
- package/fesm2020/c8y-ngx-components-ecosystem.mjs.map +1 -1
- package/fesm2020/c8y-ngx-components.mjs +20 -13
- package/fesm2020/c8y-ngx-components.mjs.map +1 -1
- package/locales/locales.pot +15 -3
- 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
|
|
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('
|
|
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(
|
|
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
|
}
|