@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
2,355 lines • 185 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, Injectable, ViewChild, Input, Component, Pipe, Output, forwardRef, inject, NgModule } from '@angular/core';
import * as i3$1 from '@c8y/client';
import { ApplicationAvailability, ApplicationType } from '@c8y/client';
import { gettext } from '@c8y/ngx-components/gettext';
import * as i1 from '@c8y/ngx-components';
import { PackageType, Status, WizardHeaderComponent, IconDirective, WizardBodyComponent, FormGroupComponent, DropAreaComponent, MessagesComponent, MessageDirective, OperationResultComponent, WizardFooterComponent, C8yTranslateDirective, C8yTranslatePipe, AppIconComponent, IfAllowedDirective, HumanizeAppNamePipe, RequiredInputPlaceholderDirective, LoadingComponent, C8yStepper, TypeaheadComponent, ForOfDirective, ListItemComponent, ListItemIconComponent, HighlightComponent, internalApps, FilterInputComponent, PluginsService, EmptyStateComponent, MarkdownToHtmlPipe, CoreModule, hookWizard } from '@c8y/ngx-components';
import * as i3 from '@ngx-translate/core';
import { saveAs } from 'file-saver';
import { groupBy, uniqBy, get, pick, omit, isUndefined, kebabCase, cloneDeep } from 'lodash-es';
import { BehaviorSubject, defer, merge, combineLatest, Subject, pipe } from 'rxjs';
import { map, shareReplay, debounceTime, take, filter, distinctUntilKeyChanged, tap, switchMap, takeUntil } from 'rxjs/operators';
import { satisfies, gt, coerce, lt } from 'semver';
import { NgIf, NgFor, AsyncPipe, NgClass, NgPlural, NgPluralCase, TitleCasePipe } from '@angular/common';
import { PopoverDirective, PopoverModule } from 'ngx-bootstrap/popover';
import * as i1$1 from '@angular/forms';
import { FormsModule, Validators, ReactiveFormsModule, NG_VALUE_ACCESSOR, FormControl } from '@angular/forms';
import * as i4 from '@angular/router';
import { TooltipDirective, TooltipModule } from 'ngx-bootstrap/tooltip';
import { BsDropdownDirective, BsDropdownToggleDirective, BsDropdownMenuDirective, BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { CdkTrapFocus, A11yModule } from '@angular/cdk/a11y';
import { IconSelectorWrapperComponent, IconSelectorModule } from '@c8y/ngx-components/icon-selector';
import * as i1$2 from 'ngx-bootstrap/modal';
import { CdkStep } from '@angular/cdk/stepper';
/** Wizard types */
var EcosystemWizards;
(function (EcosystemWizards) {
EcosystemWizards["APPLICATION_UPLOAD"] = "ecosystemApplicationUpload";
EcosystemWizards["MICROSERVICE_UPLOAD"] = "ecosystemMicroserviceUpload";
EcosystemWizards["PACKAGE_UPLOAD"] = "ecosystemPackageUpload";
EcosystemWizards["BLUEPRINT_DEPLOYMENT"] = "ecosystemBlueprintDeployment";
EcosystemWizards["LICENSE_CONFIRM"] = "ecosystemLicenseConfirm";
EcosystemWizards["ARCHIVED_CONFIRM"] = "ecosystemArchivedConfirm";
})(EcosystemWizards || (EcosystemWizards = {}));
var ERROR_TYPE;
(function (ERROR_TYPE) {
ERROR_TYPE["TYPE_VALIDATION"] = "TYPE_VALIDATION";
ERROR_TYPE["ALREADY_SUBSCRIBED"] = "ALREADY_SUBSCRIBED";
ERROR_TYPE["ALREADY_EXIST"] = "ALREADY_EXIST";
ERROR_TYPE["INTERNAL_ERROR"] = "INTERNAL_ERROR";
ERROR_TYPE["NO_MANIFEST_FILE"] = "NO_MANIFEST_FILE";
ERROR_TYPE["INVALID_PACKAGE"] = "INVALID_PACKAGE";
ERROR_TYPE["INVALID_APPLICATION"] = "INVALID_APPLICATION";
ERROR_TYPE["MICROSERVICE_NAME_TOO_LONG"] = "MICROSERVICE_NAME_TOO_LONG";
ERROR_TYPE["APPLICATION_CREATION_FAILED"] = "APPLICATION_CREATION_FAILED";
ERROR_TYPE["KEY_OR_CONTEXT_PATH_MISMATCH"] = "KEY_OR_CONTEXT_PATH_MISMATCH";
ERROR_TYPE["VERSION_NOT_FOUND"] = "VERSION_NOT_FOUND";
})(ERROR_TYPE || (ERROR_TYPE = {}));
const PRODUCT_EXPERIENCE_ECOSYSTEM = {
APPLICATIONS: {
EVENTS: {
AVAILABILITY: 'availability',
APPLICATION_CARD: 'applicationCard',
APPLICATION_PROPERTIES: 'applicationProperties',
DEPLOY_APPLICATION: 'deployApplication',
DUPLICATE_APPLICATION: 'duplicateApplication',
INSTALLED_PLUGINS: 'installedPlugins',
PACKAGE_PLUGINS: 'packagePlugins',
PACKAGE_VERSIONS: 'packageVersions',
FILTER_LIST: 'filterList'
},
COMPONENTS: {
APPLICATION_CARD: 'application-card',
APPLICATION_PLUGINS: 'application-plugins',
APPLICATION_PROPERTIES: 'application-properties',
DEPLOY_APPLICATION: 'deploy-application',
DUPLICATE_APPLICATION_PROPERTIES: 'duplicate-application-properties',
PLUGIN_LIST: 'plugin-list',
PACKAGE_VERSIONS: 'package-versions-list',
UPDATE_PLUGIN_OF_APP: 'update-plugin-of-app',
LIST_FILTERS: 'list-filters',
PACKAGE_DETAILS: 'package-details'
},
ACTIONS: {
AVAILABILITY_CHANGE: 'availabilityChange',
CANCEL: 'cancel',
CLONE: 'clone',
CHANGE_PLUGIN_VERSION: 'changePluginVersion',
DELETE: 'delete',
DOWNLOAD: 'download',
DEPLOY_APPLICATION: 'deployApplication',
EDIT: 'edit',
INSTALL_PLUGIN: 'installPlugin',
INSTALL_PLUGINS: 'installPlugins',
UNINSTALL_PLUGIN: 'uninstallPlugin',
SELECT_VERSION: 'selectVersion',
SET_AS_LATEST: 'setAsLatest',
UPDATE_AVAILABLE: 'updateAvailable',
UPLOAD: 'upload',
SET_FILTER_TERM: 'setFilterTerm',
SET_PREDEFINED_FILTERS: 'setPredefinedFilters',
RESET_FILTER: 'resetFilter',
INSTALL_PLUGINS_INITIATED: 'installPluginsInitiated',
DEPLOY_APPLICATION_INITIATED: 'deployApplicationInitiated'
},
RESULTS: {
DEPLOYED: 'deployed',
DUPLICATED: 'duplicated',
PLUGIN_INSTALLED: 'pluginInstalled',
PLUGIN_REMOVED: 'pluginRemoved',
PLUGIN_VERSION_CHANGED: 'pluginVersionChanged',
SERVER_FAILURE: 'serverFailure',
SUCCESS: 'success'
}
}
};
const ERROR_MESSAGES = {
[ERROR_TYPE.ALREADY_EXIST]: gettext('Could not deploy the application, as an application with the same name`KEEP_ORIGINAL`, context-path`KEEP_ORIGINAL` or key`KEEP_ORIGINAL` exists already.'),
[ERROR_TYPE.TYPE_VALIDATION]: gettext('Wrong file format. Expected a *.zip file with a valid manifest.'),
[ERROR_TYPE.ALREADY_SUBSCRIBED]: gettext('Could not subscribe to the microservice because another application with the same context path is already subscribed.'),
[ERROR_TYPE.NO_MANIFEST_FILE]: gettext('Could not find a manifest.'),
[ERROR_TYPE.INVALID_PACKAGE]: gettext('You have not uploaded a valid package.'),
[ERROR_TYPE.INVALID_APPLICATION]: gettext('You have not uploaded a valid application.'),
[ERROR_TYPE.INTERNAL_ERROR]: gettext('An internal error occurred, try to upload again.'),
[ERROR_TYPE.MICROSERVICE_NAME_TOO_LONG]: gettext('Microservice name "{{ name }}" must not be longer than {{ maxChars }} characters.'),
[ERROR_TYPE.APPLICATION_CREATION_FAILED]: gettext('Application creation failed.'),
[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.'),
[ERROR_TYPE.VERSION_NOT_FOUND]: gettext('The selected version was not found on the server.')
};
const APP_STATE = {
SUBSCRIBED: {
label: gettext('Subscribed`application`'),
class: 'label-primary',
tooltip: gettext('Provided by parent tenant.')
},
CUSTOM: {
label: gettext('Custom`application`'),
class: 'label-info',
tooltip: gettext('Manually uploaded to the platform.')
},
EXTERNAL: {
label: gettext('External`application`'),
class: 'label-warning',
tooltip: gettext('Application hosted outside of the platform.')
},
UNPACKED: {
label: gettext('Unpacked`application`'),
class: 'label-success',
tooltip: gettext('Deployed from a package available under "Packages".')
},
PACKAGE_BLUEPRINT: {
label: gettext('Blueprint'),
class: 'label-success',
tooltip: gettext('Contains an application and may include plugins.')
},
PACKAGE_PLUGIN: {
label: gettext('Plugins'),
class: 'label-info',
tooltip: gettext('Contains only plugins.')
},
PACKAGE_UNKNOWN: {
label: gettext('Unknown`package-type`'),
class: 'label-info',
tooltip: gettext('Package contents could not be determined.')
}
};
const PACKAGE_TYPE_LABELS = {
[PackageType.COMMUNITY]: {
label: gettext('COMMUNITY`Package created by the developer community.`'),
tooltip: gettext('Package created by the developer community.')
},
[PackageType.OFFICIAL]: {
label: gettext('OFFICIAL`Package maintained by Cumulocity.`'),
tooltip: gettext('Package maintained by Cumulocity.')
},
[PackageType.UNKNOWN]: {
label: gettext('CUSTOM`Package maintained by an unknown source.`'),
tooltip: gettext('Package maintainer unknown.')
},
[PackageType.ARCHIVED]: {
label: gettext('ARCHIVED`Package out of maintenance.`'),
tooltip: gettext('The package was marked by the author as archived.')
}
};
const packageProperties = [
{
label: gettext('Latest version'),
key: 'version'
},
{
label: gettext('Author'),
key: 'author'
},
{
label: gettext('Keywords'),
key: 'keywords'
},
{
label: gettext('Source'),
key: 'repository',
transform: (repository) => (repository?.url ? repository.url : repository),
type: 'link',
action: (e, link) => window.open(link, '_blank', 'noopener,noreferrer')
},
{
label: gettext('Homepage'),
key: 'homepage',
type: 'link',
action: (e, link) => window.open(link, '_blank', 'noopener,noreferrer')
},
{
label: gettext('License'),
key: 'license'
}
];
class EcosystemError extends Error {
constructor(type) {
super(ERROR_MESSAGES[type]);
this.type = type;
}
}
const CUMULOCITY_JSON = 'cumulocity.json';
const MICROSERVICE_NAME_MAX_LENGTH = 23;
class EcosystemService {
constructor(modal, alertService, humanizeAppName, translateService, applicationService, appStateService, zipService, tenantService, inventoryService, wizardModalService, pluginService) {
this.modal = modal;
this.alertService = alertService;
this.humanizeAppName = humanizeAppName;
this.translateService = translateService;
this.applicationService = applicationService;
this.appStateService = appStateService;
this.zipService = zipService;
this.tenantService = tenantService;
this.inventoryService = inventoryService;
this.wizardModalService = wizardModalService;
this.pluginService = pluginService;
this.appDeleted = new EventEmitter();
this.progress = new BehaviorSubject(null);
this.appsGroupedByContextPath$ = defer(() => this.getWebApplications()).pipe(map(webApps => groupBy(webApps, 'contextPath')), shareReplay({ bufferSize: 1, refCount: true }));
}
getUniqueAppConfig(srcApp, existingApps) {
let app = {
name: srcApp.name,
key: srcApp.key,
contextPath: srcApp.contextPath
};
for (let retryNo = 0; retryNo < 9;) {
if (this.checkIfAppNameKeyPathExists(existingApps, app, retryNo)) {
retryNo++;
app = {
name: [srcApp.name, retryNo].join('-'),
key: [srcApp.key, retryNo].join('-'),
contextPath: [srcApp.contextPath, retryNo].join('-')
};
}
else {
return app;
}
}
return app;
}
/**
* Verify versions compatibility for blueprints. If a blueprint version
* is not compatible, a warning is shown.
*
* @param blueprint The blueprint to install.
* @returns true if the installation can continue or false if it should be aborted.
*/
async verifyBlueprintVersionsCompatibility(blueprint) {
const api = await this.getPlatformVersion();
if (blueprint.versioningMatrix) {
try {
const pluginApiVersion = blueprint.versioningMatrix[blueprint.version].api;
if (!satisfies(api, pluginApiVersion)) {
return await this.showModal(blueprint, false, api);
}
}
catch {
return await this.showModal(blueprint, false, api);
}
}
return true;
}
/**
* Verify versions compatibility for plugins. In case a version does not exist in the
* versioningMatrix we don't do anything due to backward compatibility. If a plugin version
* is not compatible, a warning is shown.
*
* @param pluginsToInstall The list of plugins to install.
* @returns true if the installation can continue or false if it should be aborted.
*/
async verifyPluginVersionsCompatibility(pluginsToInstall, app) {
const api = await this.getPlatformVersion();
const sdk = app.manifest?.webSdkVersion;
for (const plugin of pluginsToInstall) {
if (plugin.versioningMatrix) {
try {
const pluginSdkVersion = plugin.versioningMatrix[plugin.version].sdk;
const pluginApiVersion = plugin.versioningMatrix[plugin.version].api;
if (!satisfies(sdk, pluginSdkVersion) || !satisfies(api, pluginApiVersion)) {
return await this.showModal(plugin, true, api, sdk);
}
}
catch {
return await this.showModal(plugin, false, api, sdk);
}
}
}
return true;
}
/**
* Community plugins need to verify the license agreement. If a package is a community
* package, the license is shown.
*
* @param pluginsToInstall The list of plugins to install.
* @returns true if the installation can continue.
*/
async verifyLicenses(pluginsToInstall) {
let _resolve;
const result = new Promise(resolve => {
_resolve = resolve;
});
pluginsToInstall = pluginsToInstall.filter(plugin => plugin.type !== PackageType.CUSTOM && plugin.type !== PackageType.OFFICIAL);
if (pluginsToInstall.length === 0) {
return Promise.resolve(true);
}
const initialState = {
id: EcosystemWizards.LICENSE_CONFIRM,
componentInitialState: {
pluginsToInstall
}
};
const modalOptions = { initialState };
const wizard = this.wizardModalService.show(modalOptions);
wizard.content.onClose.subscribe(confirmed => {
_resolve(confirmed);
});
return result;
}
/**
* If the plugin is archived, a warning should be shown.
*
* @param pluginsToInstall The list of plugins to install.
* @returns true if the installation can continue.
*/
async verifyArchived(pluginsToInstall) {
let _resolve;
const result = new Promise(resolve => {
_resolve = resolve;
});
pluginsToInstall = pluginsToInstall.filter(plugin => plugin.type === PackageType.ARCHIVED);
if (pluginsToInstall.length === 0) {
return Promise.resolve(true);
}
const initialState = {
id: EcosystemWizards.ARCHIVED_CONFIRM
};
const modalOptions = { initialState };
const wizard = this.wizardModalService.show(modalOptions);
wizard.content.onClose.subscribe(confirmed => {
_resolve(confirmed);
});
return result;
}
/**
* @description
* Compares currently deployed application version with application version tagged as "latest"
*
* @param {string} currentApplicationVersion Deployed application version
* @param {object} latestApp Latest application version object
*
* @returns {boolean} Returns true if latest version is greater than current, otherwise false
*/
shouldUpgradePackage(currentApplicationVersion, latestApp) {
const latestApplicationVersion = latestApp?.version;
if (!latestApplicationVersion || !currentApplicationVersion) {
return false;
}
return gt(coerce(latestApplicationVersion), coerce(currentApplicationVersion));
}
/**
* @description
* Gets an object that contains searched tag
*
* @param {array} applicationVersions Array with all available versions
* @param {string} tagName Searched tag
*
* @returns {object} Returns an object with searched tag
*/
getApplicationVersionObjectByTag(applicationVersions, tagName) {
return applicationVersions?.find(element => element.tags.includes(tagName));
}
async getApplication(appId) {
return (await this.applicationService.detail(appId)).data;
}
getApplications(customFilter = {}) {
const filter = {
pageSize: 2000,
withTotalPages: true
};
Object.assign(filter, customFilter);
const currentTenant = this.appStateService.currentTenant.value;
return this.applicationService.listByTenant(currentTenant.name, filter);
}
async getMicroservices() {
const apps = (await this.getApplications()).data;
const microservices = apps.filter(app => this.isMicroservice(app));
return microservices.sort((a, b) => a.name.localeCompare(b.name));
}
async getWebApplications(customFilter = {}) {
const apps = (await this.getApplications(customFilter)).data;
const webApps = apps.filter(app => this.isApplication(app));
return webApps.sort((a, b) => a.name.localeCompare(b.name));
}
async getFeatureApplications(customFilter = {}) {
const apps = (await this.getApplications(customFilter)).data;
const webApps = apps.filter(app => this.isFeature(app));
return webApps.sort((a, b) => a.name.localeCompare(b.name));
}
async getPackageApplications(customFilter = {}) {
const filterCallback = app => this.isPackage(app);
return this.getApplicationsFiltered(customFilter, filterCallback);
}
async getHostedAndPackageApplications(customFilter = {}) {
const filterCallback = app => this.isPackage(app) || this.isApplication(app);
return this.getApplicationsFiltered(customFilter, filterCallback);
}
async getApplicationsFiltered(customFilter = {}, filterCallback = (app) => !!app) {
const filter = Object.assign({}, customFilter);
const sharedFilter = Object.assign({
availability: ApplicationAvailability.SHARED,
type: 'HOSTED',
pageSize: 2000
}, customFilter);
const [{ data: apps }, { data: shared }] = await Promise.all([
this.getApplications(filter),
this.applicationService.list(sharedFilter)
]);
const webApps = [...apps, ...shared].filter(filterCallback);
// an app could be subscribed to a tenant, but also have it's availability set to SHARED, in that case it would occur twice.
const uniqWebApps = uniqBy(webApps, (app) => app.id);
return uniqWebApps.sort((a, b) => a.name.localeCompare(b.name));
}
async isMicroserviceHostingAllowed() {
const { data: apps } = await this.applicationService.listByName('feature-microservice-hosting');
return !!apps.filter(app => app.owner?.tenant?.id === 'management').length;
}
canOpenAppInBrowser(app) {
const isNotAFeature = !this.isFeature(app);
const hasProperType = [ApplicationType.HOSTED, ApplicationType.EXTERNAL].includes(app.type);
const isNotPackage = !this.isPackage(app);
return isNotAFeature && hasProperType && isNotPackage;
}
openApp(app) {
window.open(this.applicationService.getHref(app), '_blank', 'noopener,noreferrer');
}
async canDeleteApp(app) {
return (this.isOwner(app) && (!this.isCurrentApp(app) || (await this.hasSubscribedAppParent(app))));
}
isOwner(app) {
const currentTenant = this.appStateService.currentTenant.value;
const appOwner = get(app, 'owner.tenant.id');
return currentTenant?.name === appOwner;
}
isFeature(app) {
return !!app.name.match(/feature-/);
}
isMicroservice(app) {
return app.type === 'MICROSERVICE';
}
isExternal(app) {
return app.type === 'EXTERNAL';
}
isPackage(app) {
return app.manifest?.isPackage === true;
}
isPlugin(app) {
return app.manifest?.package === 'plugin';
}
cancelAppCreation(app) {
if (this.xhr) {
this.xhr.abort();
}
if (app) {
this.applicationService.delete(app);
}
}
updateUploadProgress(event) {
if (event.lengthComputable) {
const currentProgress = this.progress.value;
this.progress.next(currentProgress + (event.loaded / event.total) * (95 - currentProgress));
}
}
setAppActiveVersion(app, activeVersionId) {
return this.applicationService.update({ id: app.id, activeVersionId });
}
setPackageVersionTag(app, version, tags) {
return this.applicationService.setPackageVersionTag(app, version, tags);
}
deletePackageVersion(app, params) {
return this.applicationService.deleteVersionPackage(app, params);
}
getHumanizedAppName(app) {
return this.humanizeAppName.transform(app.name).pipe(debounceTime(250), take(1)).toPromise();
}
createConfig(app, formGroupValue) {
const { id, type, availability } = app;
let config = pick(formGroupValue, ['name', 'key', 'contextPath']);
config = {
...config,
isSetup: true,
id,
type,
availability
};
return config;
}
async listArchives(appId) {
const filter = {
pageSize: 100
};
return (await this.applicationService.binary(appId).list(filter)).data;
}
async deleteArchive(archive, app) {
const humanizedArchiveName = await this.getHumanizedAppName(archive);
try {
await this.modal.confirm(gettext('Delete archive'), this.translateService.instant(gettext(`You are about to delete archive "{{ humanizedArchiveName }}". Do you want to proceed?`), { humanizedArchiveName }), Status.DANGER, { ok: gettext('Delete'), cancel: gettext('Cancel') });
await this.applicationService.binary(app).delete(archive.id);
this.alertService.success(gettext('Archive deleted.'));
}
catch (ex) {
if (ex) {
this.alertService.danger(get(ex, 'data.message'), ex.data);
}
throw new Error('Cancelled');
}
}
async getArchiveManagedObject(binaryId) {
return (await this.inventoryService.detail(binaryId)).data;
}
async downloadArchive(app, archive) {
try {
const binary = await this.getBinary(app, archive);
const fileBinary = new Blob([binary], { type: 'application/x-zip-compressed' });
saveAs(fileBinary, archive.name);
}
catch (e) {
// empty
}
}
async updateApp(app, deleteOnFailure = false) {
try {
return await this.applicationService.update(app);
}
catch (ex) {
this.alertError(ex);
if (deleteOnFailure) {
await this.applicationService.delete(app.id);
throw new EcosystemError(ERROR_TYPE.APPLICATION_CREATION_FAILED);
}
}
}
async deleteApp(app, silent = false) {
const humanizedAppName = await this.getHumanizedAppName(app);
if (!silent) {
await this.modal.confirm(gettext('Delete application'), this.translateService.instant(gettext(`You are about to delete application "{{ humanizedAppName }}". Do you want to proceed?`), { humanizedAppName }), Status.DANGER, { ok: gettext('Delete'), cancel: gettext('Cancel') });
}
await this.applicationService.delete(app.id);
if (!silent) {
this.alertService.success(gettext('Application deleted.'));
}
this.appDeleted.emit(app);
}
async checkIfSubscribed(app) {
const currentTenant = await this.tenantService.current();
const subscribedApps = currentTenant.data.applications.references;
return subscribedApps.some(application => application.application.id === app.id);
}
async subscribeApp(app) {
const currentTenant = this.appStateService.currentTenant.value;
try {
await this.tenantService.subscribeApplication(currentTenant, app);
this.alertService.success(gettext('Successfully subscribed to application.'));
}
catch (ex) {
this.alertError(ex);
}
}
async unsubscribeApp(app) {
const currentTenant = this.appStateService.currentTenant.value;
try {
await this.tenantService.unsubscribeApplication(currentTenant, app);
this.alertService.success(gettext('Successfully unsubscribed from application.'));
}
catch (ex) {
this.alertError(ex);
}
}
async isValidAppType(archive, appType) {
try {
const currentType = await this.getAppType(archive);
if (currentType !== appType) {
throw new EcosystemError(ERROR_TYPE.TYPE_VALIDATION);
}
else {
this.progress.next(this.progress.value + 10);
return true;
}
}
catch (ex) {
throw new EcosystemError(ERROR_TYPE.TYPE_VALIDATION);
}
}
async uploadArchiveToApp(archive, app, isNewVersion = false) {
let uploadOverrides;
if (isNewVersion) {
uploadOverrides = await this.getUploadOverrides(archive, app);
}
const binaryService = this.applicationService.binary(app);
this.xhr = binaryService.uploadWithProgressXhr(archive, this.updateUploadProgress.bind(this), '', uploadOverrides);
const binaryMo = await binaryService.getXMLHttpResponse(this.xhr);
// TODO commented it due to: https://cumulocity.atlassian.net/browse/MTM-48553
// Add it back when BE fixes issues with activeVersion.
// if (isNewVersion) {
// return await this.getApplication(app);
// }
const notInitialPackage = app.applicationVersions?.length > 0;
if (notInitialPackage) {
return (await this.applicationService.update({ id: app.id })).data;
}
return (await this.setAppActiveVersion(app, (binaryMo.binaryId || binaryMo.id))).data;
}
async validateArchiveToAppCompatibility(archive, app) {
const appType = await this.getAppType(archive);
if (appType !== app.type) {
throw new EcosystemError(ERROR_TYPE.INVALID_APPLICATION);
}
else {
this.progress.next(this.progress.value + 10);
}
if (this.isMicroservice(app)) {
return;
}
const manifest = await this.getCumulocityJson(archive);
// A user can upload an app without a Cumulocity JSON file (e.g. a react app).
// This is allowed and should not trigger a validation error.
if (!manifest) {
return;
}
await this.validatePackageKeyAndContextPath(manifest, app);
}
async getCumulocityJson(archive) {
try {
const c8yManifest = await this.getCumulocityJson$(archive).toPromise();
return c8yManifest;
}
catch (ex) {
return null;
}
}
async createAppForArchive(archive, isPackageTypeArchive = false) {
let isPackage = false;
const appType = await this.getAppType(archive);
let appModel = {};
const supportedAppTypes = [ApplicationType.HOSTED, ApplicationType.MICROSERVICE];
if (supportedAppTypes.includes(appType)) {
try {
appModel = await this.getCumulocityJson$(archive).toPromise();
isPackage = appModel.isPackage;
}
catch (e) {
// do nothing, we allow having HOSTED applications without the manifest file
}
}
const name = this.getBaseNameFromArchiveOrAppModel(archive, appType, appModel);
const clearedName = this.removeForbiddenCharacters(name);
const key = this.getAppKey(appModel, clearedName);
const contextPath = this.getContextPath(appModel, name);
const appToSave = {
type: appType,
name,
key,
contextPath
};
if (isPackageTypeArchive && !isPackage) {
throw new EcosystemError(ERROR_TYPE.INVALID_PACKAGE);
}
else if (!isPackageTypeArchive && isPackage) {
throw new EcosystemError(ERROR_TYPE.INVALID_APPLICATION);
}
else if (this.isNameLengthExceeded(name, appType)) {
const error = new Error();
error.name = ERROR_TYPE.MICROSERVICE_NAME_TOO_LONG;
error.message = this.translateService.instant(ERROR_MESSAGES[error.name], {
name,
maxChars: MICROSERVICE_NAME_MAX_LENGTH
});
throw error;
}
return (await this.applicationService.create({
...appToSave,
manifest: {
isPackage,
...(appModel?.package && { package: appModel.package })
}
})).data;
}
async reactivateArchive(app) {
try {
await this.applicationService.reactivateArchive(app.id);
this.alertService.success(gettext('Application reactivated.'));
}
catch (ex) {
this.alertError(ex);
}
}
async removeOldestArchive(app, archives) {
try {
await this.modal.confirm(gettext('Delete oldest archive and continue'), gettext('Up to 6 archives can be saved in the platform. If you upload a new archive, the oldest archive that is not active will be deleted. Do you want to proceed?'), Status.INFO, { ok: gettext('Delete and continue') });
const archiveToDelete = archives[archives.length - 2];
await this.applicationService.binary(app).delete(archiveToDelete.id);
this.alertService.success(gettext('Archive deleted.'));
}
catch (ex) {
if (ex) {
this.alertError(ex);
}
else {
return Promise.reject('cancelled');
}
}
}
async deployApp(selectedPackage, formGroupValue, model) {
// Create new app config
const config = this.createConfig(selectedPackage, formGroupValue);
const requestedVersion = model.selected.version;
let cleanManifest;
try {
const manifest = await this.applicationService.getAppManifest(selectedPackage, requestedVersion);
cleanManifest = omit(manifest, ['name', 'contextPath', 'key']);
}
catch (ex) {
throw new EcosystemError(ERROR_TYPE.VERSION_NOT_FOUND);
}
config.isSetup = true;
config.manifest = cleanManifest;
config.availability = ApplicationAvailability.PRIVATE;
config.manifest.isPackage = false;
config.manifest.source = selectedPackage.id;
config.manifest.package = 'blueprint';
// because of a issue with SHARED availability we always should check again
// if the app not exist already
const allExistingApps = await this.getHostedAndPackageApplications();
const doesAppKeyOrContextPathExist = this.checkIfAppNameKeyPathExists(allExistingApps, config);
if (doesAppKeyOrContextPathExist) {
throw new EcosystemError(ERROR_TYPE.ALREADY_EXIST);
}
// Create new app
const newApp = (await this.applicationService.create(config)).data;
try {
// Binary upload can fail if SHARED package
// in this case, catch error and fall back to
// clone API.
await this.uploadBinaryFromOtherPackage(selectedPackage, newApp, model.selected.binaryId, requestedVersion);
}
catch (error) {
if (error?.res?.status === 404) {
await this.fallbackToClone(newApp, selectedPackage, requestedVersion);
}
}
// update the icon if a custom one is selected
if (formGroupValue.icon) {
await this.applicationService.update({
id: newApp.id,
config: { icon: formGroupValue.icon }
});
}
return newApp;
}
async fallbackToClone(application, selectedPackage, requestedVersion) {
let wasSuccess = true;
let clonedPkg;
try {
clonedPkg = (await this.applicationService.clone(selectedPackage, requestedVersion)).data;
await this.uploadBinaryFromOtherPackage(selectedPackage, application, clonedPkg.activeVersionId, requestedVersion, clonedPkg);
}
catch (error) {
this.alertError(error);
wasSuccess = false;
}
finally {
await this.deleteApp(clonedPkg, true);
}
return wasSuccess;
}
async uploadBinaryFromOtherPackage(selectedPackage, applicationToUploadBinaryTo, binaryId, requestedVersion, useBinariesFrom) {
const { data: binaryDetails } = await this.inventoryService.detail(binaryId);
// Get binary from specific package version
const binary = await this.getBinary(useBinariesFrom || selectedPackage, {
id: binaryId
});
// Create zip
const fileBinary = new Blob([binary], { type: binaryDetails.contentType });
const file = new File([fileBinary], binaryDetails.name, {
type: binaryDetails.contentType
});
// Upload binary to new app
await this.uploadArchiveToApp(file, applicationToUploadBinaryTo);
// update the app manifest
await this.updateAppManifest(applicationToUploadBinaryTo, selectedPackage, requestedVersion);
}
getAppState(app) {
if (!this.isOwner(app)) {
return APP_STATE.SUBSCRIBED;
}
else if (this.isUnpacked(app)) {
return APP_STATE.UNPACKED;
}
else if (app.type === ApplicationType.EXTERNAL) {
return APP_STATE.EXTERNAL;
}
return APP_STATE.CUSTOM;
}
getPackageContentState(app) {
if (!this.isPackage(app)) {
return;
}
if (this.isPackageBlueprint(app)) {
return APP_STATE.PACKAGE_BLUEPRINT;
}
if (this.isPluginsPackage(app)) {
return APP_STATE.PACKAGE_PLUGIN;
}
return APP_STATE.PACKAGE_UNKNOWN;
}
isPackageBlueprint(app) {
return this.isPackage(app) && app.manifest.package === 'blueprint';
}
isPluginsPackage(app) {
return this.isPackage(app) && app.manifest.package === 'plugin';
}
isUnpacked(app) {
return !!app.manifest?.source;
}
hasExports(app) {
return !!app.manifest?.exports?.length;
}
isApplication(app) {
return (app.type !== ApplicationType.MICROSERVICE && !this.isFeature(app) && !this.isPackage(app));
}
isCustomMicroservice(app) {
return this.isOwner(app) && app.type === ApplicationType.MICROSERVICE;
}
async getBinary(app, archive) {
let binary;
try {
const res = await this.applicationService.binary(app).downloadArchive(archive.id);
binary = await res.arrayBuffer();
}
catch (ex) {
const msg = gettext('Could not get the binary.');
this.alertService.danger(msg);
}
return binary;
}
async isOverwrittenByCustomApp(app) {
return !this.isOwner(app) && (await this.hasSubscribedAppParent(app));
}
async hasSubscribedAppParent(app) {
const appsGroupedByContextPath = await this.appsGroupedByContextPath$.pipe(take(1)).toPromise();
return app.contextPath && appsGroupedByContextPath[app.contextPath]?.length === 2;
}
/**
* @deprecated
*/
setAvailabilityToPrivateIfNotSetAlready(app) {
app.availability = ApplicationAvailability.PRIVATE;
return app;
}
/**
* Shows an error dialog.
* @param error Either a server error or an internal [[EcosystemError]].
*/
alertError(error) {
if (error instanceof EcosystemError) {
this.alertService.danger(error.message);
}
else {
this.alertService.addServerFailure(error);
}
}
async validatePackageKeyAndContextPath(manifest, app) {
const contextPath = get(manifest, 'contextPath');
const appKey = get(manifest, 'key');
if (contextPath !== app.contextPath || appKey !== app.key) {
throw new EcosystemError(ERROR_TYPE.KEY_OR_CONTEXT_PATH_MISMATCH);
}
}
filterContainString(name, filterTerm) {
const term = filterTerm.toLowerCase().trim();
return name && name.toLowerCase().indexOf(term) > -1;
}
async updateAppManifest(application, selectedPackage, requestedVersion) {
const { id } = application;
const cleanedApp = this.removeAppProperties(application);
let manifest = selectedPackage.manifest || {};
if (requestedVersion) {
try {
const versionedAppManifest = await this.applicationService.getAppManifest(selectedPackage, requestedVersion);
manifest = versionedAppManifest;
}
catch (ex) {
throw new EcosystemError(ERROR_TYPE.VERSION_NOT_FOUND);
}
}
cleanedApp.manifest = manifest;
cleanedApp.manifest.isPackage = false;
cleanedApp.manifest.source = selectedPackage.id;
return await this.applicationService
.binary(id)
.updateFiles([{ path: CUMULOCITY_JSON, contents: JSON.stringify(cleanedApp) }]);
}
/**
* Creates object containing properties for filtering applications on lists.
*
* @param {object} app Application to create filter properties from.
* @returns {object} Properties to filter by applications list.
*/
getAppFilterProps(app) {
return {
type: this.pluginService.getPackageType(app),
availability: this.getAppState(app)?.label,
content: this.getPackageContentState(app)?.label
};
}
checkIfAppNameKeyPathExists(existingApps, app, retryNo) {
if (isUndefined(retryNo)) {
return existingApps.find(existingApp => existingApp.name === app.name ||
existingApp.key === app.key ||
existingApp.contextPath === app.contextPath);
}
return existingApps.find(existingApp => existingApp.name === app.name ||
existingApp.key === app.key ||
existingApp.contextPath === app.contextPath ||
existingApp.name === [app.name, retryNo].join('-') ||
existingApp.key === [app.key, retryNo].join('-') ||
existingApp.contextPath === [app.contextPath, retryNo].join('-'));
}
async showModal(packageType, isPlugin, apiVersion, sdkVersion) {
try {
const messages = {
plugin: {
title: gettext('Plugin installation'),
body: sdkVersion
? gettext('The current version of the plugin that you are trying to install does not satisfy the requirements for the following API version "{{ apiVersion }}" or SDK version "{{ sdkVersion }}". Do you want to proceed?')
: gettext('The current version of the plugin that you are trying to install does not satisfy the requirements for the following API version "{{ apiVersion }}". Do you want to proceed?')
},
blueprint: {
title: gettext('Blueprint installation'),
body: sdkVersion
? gettext('The current version of the blueprint that you are trying to install does not satisfy the requirements for the following API version "{{ apiVersion }}" or SDK version "{{ sdkVersion }}". Do you want to proceed?')
: gettext('The current version of the blueprint that you are trying to install does not satisfy the requirements for the following API version "{{ apiVersion }}". Do you want to proceed?')
}
};
const entityType = isPlugin ? 'plugin' : 'blueprint';
const selectedMessages = messages[entityType];
const translatedBody = this.translateService.instant(selectedMessages.body, {
name: packageType.name,
apiVersion,
sdkVersion
});
await this.modal.confirm(selectedMessages.title, translatedBody, 'warning', {
ok: gettext('Continue'),
cancel: gettext('Cancel')
});
}
catch {
// modal canceled
return false;
}
return true;
}
async getPlatformVersion() {
return await this.appStateService.state$
.pipe(take(1), map(state => state?.versions?.backend), filter(backendVersion => !!backendVersion))
.toPromise();
}
getAppKey(appModel, name) {
let key = appModel?.key;
if (!key) {
key = `${kebabCase(name)}-key`;
}
return key;
}
getContextPath(appModel, name) {
return appModel?.contextPath || name.toLowerCase();
}
removeForbiddenCharacters(str) {
return str.replace(/[^a-zA-Z0-9-_]/g, '');
}
isCurrentApp(app) {
const currentApp = this.appStateService.state.app;
return currentApp.contextPath === app.contextPath;
}
getCumulocityJson$(archive) {
return this.zipService.getJsonData(archive, {
filename: CUMULOCITY_JSON
});
}
getAppType(archive) {
return this.getCumulocityJson$(archive)
.toPromise()
.then(data => get(data, 'type') ||
(get(data, 'apiVersion') ? ApplicationType.MICROSERVICE : ApplicationType.HOSTED))
.catch(() => ApplicationType.HOSTED);
}
getBaseNameFromArchiveOrAppModel(archive, appType, appModel) {
let baseName = appModel?.name || archive.name.replace(/\.zip$/i, '');
if (appType === 'MICROSERVICE') {
baseName = this.removeVersionFromName(baseName);
}
return baseName;
}
removeAppProperties(app) {
const tempApp = cloneDeep(app);
const propertiesToRemove = ['id', 'owner', 'activeVersionId', 'self', 'type'];
propertiesToRemove.forEach(prop => delete tempApp[prop]);
return tempApp;
}
async getUploadOverrides(archive, app) {
const { version } = await this.getCumulocityJson$(archive).toPromise();
const isInitialPackage = app.applicationVersions?.length === 0;
return {
listUrl: 'versions',
headers: {
Accept: 'application/vnd.com.nsn.cumulocity.applicationVersion+json;charset=UTF-8;ver=0.9'
},
bodyFileProperty: 'applicationBinary',
requestBody: {
applicationVersion: { version, ...(isInitialPackage && { tags: ['latest'] }) }
}
};
}
removeVersionFromName(name) {
const versionRegExp = /-\d+\.\d+\.\d+(\.\d+)?(-\d+)?(.*)$/;
return name.replace(versionRegExp, '');
}
isNameLengthExceeded(name, appType) {
return name.length > MICROSERVICE_NAME_MAX_LENGTH && appType === ApplicationType.MICROSERVICE;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: EcosystemService, deps: [{ token: i1.ModalService }, { token: i1.AlertService }, { token: i1.HumanizeAppNamePipe }, { token: i3.TranslateService }, { token: i3$1.ApplicationService }, { token: i1.AppStateService }, { token: i1.ZipService }, { token: i3$1.TenantService }, { token: i3$1.InventoryService }, { token: i1.WizardModalService }, { token: i1.PluginsService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: EcosystemService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: EcosystemService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1.ModalService }, { type: i1.AlertService }, { type: i1.HumanizeAppNamePipe }, { type: i3.TranslateService }, { type: i3$1.ApplicationService }, { type: i1.AppStateService }, { type: i1.ZipService }, { type: i3$1.TenantService }, { type: i3$1.InventoryService }, { type: i1.WizardModalService }, { type: i1.PluginsService }] });
class PackageAvailabilityService {
constructor(appState, alert, modal, application, gainsightService, translateService) {
this.appState = appState;
this.alert = alert;
this.modal = modal;
this.application = application;
this.gainsightService = gainsightService;
this.translateService = translateService;
this.CURRENT_LOCATION = location.href;
this.availabilities = [
{ label: gettext('Private`package availability`'), value: ApplicationAvailability.PRIVATE },
{ label: gettext('Market`package availability`'), value: ApplicationAvailability.MARKET },
{ label: gettext('Shared`package availability`'), value: ApplicationAvailability.SHARED }
];
}
async askIfAvailabilityShouldBeSetTo(applicationPackage, availability) {
// availability does not matter for tenant that do not have or can create subtenants
if (!this.appState.currentTenant.value?.allowCreateTenants) {
return applicationPackage;
}
try {
await this.openAvailabilityModal(availability);
// user confirmed changing availability
return await this.setAvailability(applicationPackage, availability);
}
catch {
// user canceled changing availability
return applicationPackage;
}
}
async setAvailability(applicationPackage, availability) {
try {
const { data: app } = await this.application.updateApplicationAvailability(applicationPackage, availability);
this.alert.success(gettext('Updated package availability.'));
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.AVAILABILITY, {
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.AVAILABILITY_CHANGE,
result: availability.toString().toLocaleLowerCase(),
url: this.CURRENT_LOCATION
});
return app;
}
catch (e) {
this.alert.warning(gettext('Failed to set package availability.'));
this.alert.addServerFailure(e);
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.AVAILABILITY, {
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.AVAILABILITY_CHANGE,
result: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.RESULTS.SERVER_FAILURE,
url: this.CURRENT_LOCATION
});
return applicationPackage;
}
}
getConfirmationBody(availability) {
switch (availability) {
case ApplicationAvailability.SHARED:
return gettext('Do you want to set the package availability to "Shared"? This will make the package available to all subtenants without explicitly subscribing the package.');
case ApplicationAvailability.MARKET:
return gettext('Do you want to set the package availability to "Market"? This will make the package available to your own tenant and needs to be subscribed to subtenants individually.');
case ApplicationAvailability.PRIVATE:
return gettext('Do you want to set the package availability to "Private"? This will make the package only available to your own tenant.');
default:
return '';
}
}
async openAvailabilityModal(availability) {
const body = this.getConfirmationBody(availability);
const availabilityLabel = this.availabilities.find(obj => obj.value === availability).label;
await this.modal.confirm(gettext('Package availability'), body, 'info', {
ok: this.translateService.instant(gettext('Set to "{{ packageAvailability }}"'), {
packageAvailability: this.translateService.instant(availabilityLabel)
}),
cancel: gettext('Cancel')
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PackageAvailabilityService, deps: [{ token: i1.AppStateService }, { token: i1.AlertService }, { token: i1.ModalService }, { token: i3$1.ApplicationService }, { token: i1.GainsightService }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PackageAvailabilityService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PackageAvailabilityService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1.AppStateService }, { type: i1.AlertService }, { type: i1.ModalService }, { type: i3$1.ApplicationService }, { type: i1.GainsightService }, { type: i3.TranslateService }] });
class AddApplicationComponent {
constructor(ecosystemService, alertService, applicationService, wizardComponent, translateService, packageAvailability) {
this.ecosystemService = ecosystemService;
this.alertService = alertService;
this.applicationService = applicationService;
this.wizardComponent = wizardComponent;
this.translateService = translateService;
this.packageAvailability = packageAvailability;
this.canGoBack = false;
this.applicationType = 'application';
this.canOpenInBrowser = false;
this.selectedAvailability = ApplicationAvailability.PRIVATE;
this.availabilityHelpPopoverText = gettext(`
<p class="m-b-8"><b>Shared</b> availability will make the package available to all subtenants without explicitly subscribing the package.</p>
<p class="m-b-8"><b>Market</b> availability will make the package available to your own tenant and needs to be subscribed to subtenants individually.</p>
<p class="m-b-8"><b>Private</b> availability will make the package only available to your own tenant.</p>
`);
this.uploadCanceled = false;
}
get progress() {
return this.ecosystemService.progress;
}
onFileDroppedEvent(event) {
if (event && event.length > 0) {
const file = event[0].file;
this.onFile(file);
}
}
async onFile(file) {
this.isLoading = true;
this.errorMessage = null;
this.progress.next(0);
try {
this.createdApp = await this.createApplicationHandler(file);
if (this.applicationType === 'package') {
this.createdApp = await this.packageAvailability.setAvailability(this.createdApp, this.selectedAvailability);
}
await this.uploadApplicationHandler(file, this.createdApp);
this.canOpenInBrowser = this.ecosystemService.canOpenAppInBrowser(this.createdApp);
this.isAppCreated = true;
}
catch (ex) {
this.ecosystemService.cancelAppCreation(this.createdApp);
this.createdApp = null;
this.dropAreaComponent.onDelete();
// prepare translation of static message if it exists
const staticErrorMessage = ERROR_MESSAGES[ex.message] && this.translateService.instant(ERROR_MESSAGES[ex.message]);
// if there is no static message, use dynamic one from the exception
this.errorMessage = staticErrorMessage ?? ex.message;
if (!this.errorMessage && !this.uploadCanceled) {
this.alertService.addServerFailure(ex);
}
}
this.progress.next(100);
this.isLoading = false;
}
getHref(app) {
return this.applicationService.getHref(app);
}
cancel() {
this.cancelFileUpload();
this.wizardComponent.close();
}
done() {
this.wizardComponent.close();
}
back() {
this.wizardComponent.reset();
}
cancelFileUpload() {
this.uploadCanceled = true;
this.ecosystemService.cancelAppCreation(this.createdApp);
this.createdApp = null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AddApplicationComponent, deps: [{ token: EcosystemService }, { token: i1.AlertService }, { token: i3$1.ApplicationService }, { token: i1.WizardComponent }, { token: i3.TranslateService }, { token: PackageAvailabilityService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: AddApplicationComponent, isStandalone: true, selector: "c8y-add-application", inputs: { headerText: "headerText", headerIcon: "headerIcon", successText: "successText", createApplicationHandler: "createApplicationHandler", uploadApplicationHandler: "uploadApplicationHandler", canGoBack: "canGoBack", applicationType: "applicationType" }, viewQueries: [{ propertyName: "dropAreaComponent", first: true, predicate: DropAreaComponent, descendants: true }], ngImport: i0, template: "<c8y-wizard-header>\n <i [c8yIcon]=\"headerIcon\"></i>\n <h4 id=\"modal-title\">{{ headerText | translate }}</h4>\n</c8y-wizard-header>\n\n<c8y-wizard-body>\n <p class=\"p-16 text-center text-medium separator-bottom sticky-top bg-component\">\n {{ 'Upload a *.zip file' | translate }}\n </p>\n <c8y-form-group\n class=\"m-t-16 m-l-auto m-r-auto\"\n style=\"max-width: 265px\"\n id=\"modal-body\"\n *ngIf=\"!isAppCreated; else appCreated\"\n [hasError]=\"!!errorMessage\"\n >\n <c8y-drop-area\n class=\"drop-area\"\n (dropped)=\"onFileDroppedEvent($event)\"\n [accept]=\"'.zip'\"\n [loading]=\"isLoading\"\n [maxAllowedFiles]=\"1\"\n [progress]=\"progress | async\"\n ></c8y-drop-area>\n <c8y-messages>\n <c8y-message *ngIf=\"errorMessage\">\n {{ errorMessage | translate }}\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <div class=\"card-block\" *ngIf=\"applicationType === 'package'\">\n <c8y-form-group>\n <label\n class=\"m-r-8 d-inline-block\"\n id=\"availabilitySelectorLabel\"\n for=\"availabilitySelector\"\n >\n {{ 'Select extension availability' | translate }}\n </label>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"availabilityHelpPopover\"\n placement=\"top\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"availabilitySelector\"\n name=\"availabilitySelector\"\n [(ngModel)]=\"selectedAvailability\"\n >\n <option\n *ngFor=\"let availability of packageAvailability.availabilities\"\n [ngValue]=\"availability.value\"\n >\n {{ availability.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n <ng-template #appCreated>\n <div\n class=\"d-flex a-i-center j-c-center\"\n style=\"min-height: 285px\"\n >\n <c8y-operation-result\n class=\"lead\"\n type=\"success\"\n text=\"{{ successText | translate }}\"\n [vertical]=\"true\"\n [size]=\"84\"\n ></c8y-operation-result>\n </div>\n </ng-template>\n <ng-template #availabilityHelpPopover>\n <div [innerHtml]=\"availabilityHelpPopoverText | translate\"></div>\n </ng-template>\n</c8y-wizard-body>\n\n<c8y-wizard-footer>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Back' | translate }}\"\n type=\"button\"\n (click)=\"back()\"\n *ngIf=\"!isAppCreated && canGoBack\"\n translate\n >\n Back\n </button>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"cancel()\"\n *ngIf=\"!isAppCreated\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Done' | translate }}\"\n type=\"button\"\n (click)=\"done()\"\n *ngIf=\"isAppCreated\"\n translate\n >\n Done\n </button>\n <a\n class=\"btn btn-primary\"\n title=\"{{ 'Open' | translate }}\"\n target=\"_blank\"\n (click)=\"$event.stopPropagation()\"\n *ngIf=\"isAppCreated && canOpenInBrowser\"\n [href]=\"getHref(createdApp)\"\n rel=\"noopener noreferrer\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"external-link\"\n ></i>\n {{ 'Open' | translate }}\n </a>\n</c8y-wizard-footer>\n", dependencies: [{ kind: "component", type: WizardHeaderComponent, selector: "c8y-wizard-header" }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: WizardBodyComponent, selector: "c8y-wizard-body" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: OperationResultComponent, selector: "c8y-operation-result", inputs: ["text", "vertical", "size", "type"] }, { kind: "component", type: WizardFooterComponent, selector: "c8y-wizard-footer" }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AddApplicationComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-add-application', imports: [
WizardHeaderComponent,
IconDirective,
WizardBodyComponent,
NgIf,
FormGroupComponent,
DropAreaComponent,
MessagesComponent,
MessageDirective,
PopoverDirective,
FormsModule,
NgFor,
OperationResultComponent,
WizardFooterComponent,
C8yTranslateDirective,
C8yTranslatePipe,
AsyncPipe
], template: "<c8y-wizard-header>\n <i [c8yIcon]=\"headerIcon\"></i>\n <h4 id=\"modal-title\">{{ headerText | translate }}</h4>\n</c8y-wizard-header>\n\n<c8y-wizard-body>\n <p class=\"p-16 text-center text-medium separator-bottom sticky-top bg-component\">\n {{ 'Upload a *.zip file' | translate }}\n </p>\n <c8y-form-group\n class=\"m-t-16 m-l-auto m-r-auto\"\n style=\"max-width: 265px\"\n id=\"modal-body\"\n *ngIf=\"!isAppCreated; else appCreated\"\n [hasError]=\"!!errorMessage\"\n >\n <c8y-drop-area\n class=\"drop-area\"\n (dropped)=\"onFileDroppedEvent($event)\"\n [accept]=\"'.zip'\"\n [loading]=\"isLoading\"\n [maxAllowedFiles]=\"1\"\n [progress]=\"progress | async\"\n ></c8y-drop-area>\n <c8y-messages>\n <c8y-message *ngIf=\"errorMessage\">\n {{ errorMessage | translate }}\n </c8y-message>\n </c8y-messages>\n </c8y-form-group>\n <div class=\"card-block\" *ngIf=\"applicationType === 'package'\">\n <c8y-form-group>\n <label\n class=\"m-r-8 d-inline-block\"\n id=\"availabilitySelectorLabel\"\n for=\"availabilitySelector\"\n >\n {{ 'Select extension availability' | translate }}\n </label>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"availabilityHelpPopover\"\n placement=\"top\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n id=\"availabilitySelector\"\n name=\"availabilitySelector\"\n [(ngModel)]=\"selectedAvailability\"\n >\n <option\n *ngFor=\"let availability of packageAvailability.availabilities\"\n [ngValue]=\"availability.value\"\n >\n {{ availability.label | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </div>\n <ng-template #appCreated>\n <div\n class=\"d-flex a-i-center j-c-center\"\n style=\"min-height: 285px\"\n >\n <c8y-operation-result\n class=\"lead\"\n type=\"success\"\n text=\"{{ successText | translate }}\"\n [vertical]=\"true\"\n [size]=\"84\"\n ></c8y-operation-result>\n </div>\n </ng-template>\n <ng-template #availabilityHelpPopover>\n <div [innerHtml]=\"availabilityHelpPopoverText | translate\"></div>\n </ng-template>\n</c8y-wizard-body>\n\n<c8y-wizard-footer>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Back' | translate }}\"\n type=\"button\"\n (click)=\"back()\"\n *ngIf=\"!isAppCreated && canGoBack\"\n translate\n >\n Back\n </button>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"cancel()\"\n *ngIf=\"!isAppCreated\"\n translate\n >\n Cancel\n </button>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Done' | translate }}\"\n type=\"button\"\n (click)=\"done()\"\n *ngIf=\"isAppCreated\"\n translate\n >\n Done\n </button>\n <a\n class=\"btn btn-primary\"\n title=\"{{ 'Open' | translate }}\"\n target=\"_blank\"\n (click)=\"$event.stopPropagation()\"\n *ngIf=\"isAppCreated && canOpenInBrowser\"\n [href]=\"getHref(createdApp)\"\n rel=\"noopener noreferrer\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"external-link\"\n ></i>\n {{ 'Open' | translate }}\n </a>\n</c8y-wizard-footer>\n" }]
}], ctorParameters: () => [{ type: EcosystemService }, { type: i1.AlertService }, { type: i3$1.ApplicationService }, { type: i1.WizardComponent }, { type: i3.TranslateService }, { type: PackageAvailabilityService }], propDecorators: { headerText: [{
type: Input
}], headerIcon: [{
type: Input
}], successText: [{
type: Input
}], createApplicationHandler: [{
type: Input
}], uploadApplicationHandler: [{
type: Input
}], canGoBack: [{
type: Input
}], applicationType: [{
type: Input
}], dropAreaComponent: [{
type: ViewChild,
args: [DropAreaComponent]
}] } });
class TranslatePackageLabelPipe {
constructor(translate) {
this.translate = translate;
}
transform(rawPackageLabel) {
const isOneOfTheKnownOnes = !!PACKAGE_TYPE_LABELS[rawPackageLabel?.toUpperCase()];
if (isOneOfTheKnownOnes) {
return this.translate.instant(PACKAGE_TYPE_LABELS[rawPackageLabel?.toUpperCase()].label);
}
return rawPackageLabel;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TranslatePackageLabelPipe, deps: [{ token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: TranslatePackageLabelPipe, isStandalone: true, name: "translatePackageLabel" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TranslatePackageLabelPipe, decorators: [{
type: Pipe,
args: [{ name: 'translatePackageLabel' }]
}], ctorParameters: () => [{ type: i3.TranslateService }] });
class ApplicationCardComponent {
get openButtonTitle() {
return !this.disableOpenInBrowser
? gettext('Open')
: gettext('This application is overridden.');
}
constructor(applicationService, ecosystemService, alertService, router, wizardModalService, alert, pluginService, gainsightService) {
this.applicationService = applicationService;
this.ecosystemService = ecosystemService;
this.alertService = alertService;
this.router = router;
this.wizardModalService = wizardModalService;
this.alert = alert;
this.pluginService = pluginService;
this.gainsightService = gainsightService;
this.CURRENT_LOCATION = location.href;
this.canEdit = true;
this.onAppDeleted = new EventEmitter();
this.onAppCloned = new EventEmitter();
this.packageTypeLabels = PACKAGE_TYPE_LABELS;
this.PACKAGE_TYPE = PackageType;
this.UNPACKED_LABEL = APP_STATE.UNPACKED.label;
this.CANNOT_DELETE_HINT = gettext(`Subscribed or current applications can't be deleted. Delete the application on the parent tenant or unsubscribe it from the current.`);
this.PACKAGE_CONTENT_UNDETERMINED_LABEL = gettext('Package contents could not be determined.');
}
async ngOnInit() {
this.appState = this.ecosystemService.getAppState(this.app);
this.canOpenInBrowser = this.ecosystemService.canOpenAppInBrowser(this.app);
this.packageType = this.pluginService.getPackageType(this.app);
this.disableOpenInBrowser =
this.canOpenInBrowser && (await this.ecosystemService.isOverwrittenByCustomApp(this.app));
this.canDelete = await this.ecosystemService.canDeleteApp(this.app);
this.isPackage = this.ecosystemService.isPackage(this.app);
this.packageContentState = this.ecosystemService.getPackageContentState(this.app);
this.isPlugin = this.ecosystemService.isPlugin(this.app);
this.isFeature = this.ecosystemService.isFeature(this.app);
this.isMicroservice = this.ecosystemService.isMicroservice(this.app);
this.isExternal = this.ecosystemService.isExternal(this.app);
this.canClone = !this.isMicroservice && !this.isPlugin;
this.isUnpacked = this.ecosystemService.isUnpacked(this.app);
this.isShowVersion = this.shouldShowVersion();
await this.applicationUpdateCheck();
}
shouldShowVersion() {
return !!this.app.manifest?.version;
}
detail() {
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.APPLICATION_CARD, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.APPLICATION_CARD,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.EDIT,
url: this.CURRENT_LOCATION
});
if (this.isMicroservice) {
this.router.navigateByUrl(`/ecosystem/microservice/microservices/${this.app.id}`);
return;
}
if (this.isPackage) {
this.router.navigateByUrl(`/ecosystem/extension/extensions/${this.app.id}`);
return;
}
if (this.isFeature) {
this.router.navigateByUrl(`/ecosystem/application/features/${this.app.id}`);
return;
}
this.router.navigateByUrl(`/ecosystem/application/applications/${this.app.id}`);
}
openApp() {
this.ecosystemService.openApp(this.app);
}
async delete() {
try {
await this.ecosystemService.deleteApp(this.app);
this.onAppDeleted.emit();
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.APPLICATION_CARD, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.APPLICATION_CARD,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.DELETE,
url: this.CURRENT_LOCATION
});
}
catch (ex) {
if (ex) {
this.alertService.addServerFailure(ex);
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.APPLICATION_CARD, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.APPLICATION_CARD,
result: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.RESULTS.SERVER_FAILURE,
url: this.CURRENT_LOCATION
});
}
}
}
async clone() {
const wizardConfig = {
headerText: 'Add Application',
headerIcon: 'c8y-atom'
};
const initialState = {
wizardConfig,
componentInitialState: {
selectedApp: new BehaviorSubject(this.app)
},
id: 'duplicateApplication'
};
const modalOptions = { initialState };
const modalRef = this.wizardModalService.show(modalOptions);
modalRef.content.onClose.subscribe(() => {
this.onAppCloned.next();
});
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.APPLICATION_CARD, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.APPLICATION_CARD,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.CLONE,
url: this.CURRENT_LOCATION
});
}
async applicationUpdateCheck() {
if (this.app.manifest?.source) {
try {
this.blueprintApplicationVersion = (await this.applicationService.listVersions(this.app.manifest?.source)).data;
this.latestApplicationVersion = this.ecosystemService.getApplicationVersionObjectByTag(this.blueprintApplicationVersion, 'latest');
this.shouldUpgradePackage = this.ecosystemService.shouldUpgradePackage(this.app.manifest?.version, this.latestApplicationVersion);
}
catch (error) {
if (!this.blueprintApplicationVersion) {
return;
}
this.alert.addServerFailure(error);
}
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ApplicationCardComponent, deps: [{ token: i3$1.ApplicationService }, { token: EcosystemService }, { token: i1.AlertService }, { token: i4.Router }, { token: i1.WizardModalService }, { token: i1.AlertService }, { token: i1.PluginsService }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ApplicationCardComponent, isStandalone: true, selector: "c8y-application-card", inputs: { app: "app", canEdit: "canEdit" }, outputs: { onAppDeleted: "onAppDeleted", onAppCloned: "onAppCloned" }, ngImport: i0, template: "<div\n class=\"card pointer card--d-col notFeature\"\n data-cy=\"application-card--extensions-list\"\n *ngIf=\"!isFeature\"\n>\n <div\n class=\"card-block text-center card-column-40\"\n data-cy=\"application-card--package-name\"\n (click)=\"detail()\"\n >\n <button\n class=\"card__ribbon btn-clean\"\n [attr.aria-label]=\"\n (app.label || app.manifest?.label | translatePackageLabel) +\n ': ' +\n (packageTypeLabels[packageType].tooltip | translate)\n \"\n tooltip=\"{{ packageTypeLabels[packageType].tooltip | translate }}\"\n placement=\"right\"\n type=\"button\"\n *ngIf=\"packageType !== PACKAGE_TYPE.CUSTOM\"\n [delay]=\"500\"\n >\n <span\n [ngClass]=\"{\n 'bg-info': packageType === PACKAGE_TYPE.COMMUNITY,\n 'bg-primary': packageType === PACKAGE_TYPE.OFFICIAL,\n 'bg-warning': packageType === PACKAGE_TYPE.ARCHIVED\n }\"\n >\n {{ app.label || app.manifest?.label | translatePackageLabel }}\n </span>\n </button>\n <div class=\"card-appicon\">\n <c8y-app-icon\n *ngIf=\"!isPackage && !isFeature && !isMicroservice && !isExternal\"\n [app]=\"app\"\n [contextPath]=\"app.contextPath\"\n [name]=\"app.name\"\n ></c8y-app-icon>\n <i\n c8yIcon=\"big-parcel\"\n *ngIf=\"isPackage\"\n ></i>\n <i\n c8yIcon=\"microchip\"\n *ngIf=\"isMicroservice\"\n ></i>\n <i\n c8yIcon=\"globe1\"\n *ngIf=\"isExternal\"\n ></i>\n </div>\n <div style=\"line-height: 1.3\">\n <p class=\"e2e-appCardName text-medium text-break-word\">\n {{ app | humanizeAppName | async }}\n </p>\n <ng-container *ngIf=\"isShowVersion\">\n <p class=\"small text-muted\">\n <em>{{ app.manifest.version }}</em>\n <button\n class=\"btn-clean m-l-4\"\n [attr.aria-label]=\"'Update available' | translate\"\n [tooltip]=\"'Update available' | translate\"\n *ngIf=\"shouldUpgradePackage && isUnpacked\"\n >\n <i\n class=\"text-warning icon-16\"\n c8yIcon=\"exclamation-triangle\"\n ></i>\n </button>\n </p>\n </ng-container>\n <p\n class=\"small text-break-word\"\n *ngIf=\"app.manifest?.author\"\n >\n {{ app.manifest.author }}\n </p>\n </div>\n </div>\n <div\n class=\"card-block p-0 no-min-height card--d-col card-column-80 flex-grow\"\n (click)=\"detail()\"\n >\n <div class=\"card-block no-min-height p-t-0 p-b-0 card-column-80\">\n <div class=\"text-center-grid\">\n <div class=\"p-b-8 card-hidden-grid\"></div>\n <p\n class=\"small l-h-tight p-b-8 p-l-0 p-r-0 text-break-word\"\n *ngIf=\"app.description || app.manifest?.description\"\n >\n {{ app.description || app.manifest?.description | translate }}\n </p>\n <p\n class=\"small l-h-tight p-b-8 p-l-0 p-r-0\"\n *ngIf=\"!app.description && !app.manifest?.description\"\n >\n <em class=\"text-muted\">{{ 'No description available.' | translate }}</em>\n </p>\n </div>\n </div>\n <div\n class=\"card-block no-min-height p-t-0 p-b-0 d-flex card-column-20 text-center-grid flex-wrap gap-8\"\n >\n <button\n class=\"btn-clean\"\n [attr.aria-label]=\"\n ((isUnpacked ? UNPACKED_LABEL : appState?.label) | translate) +\n ': ' +\n (appState?.tooltip | translate)\n \"\n [tooltip]=\"appState?.tooltip | translate\"\n placement=\"top\"\n [delay]=\"500\"\n >\n <span\n class=\"label\"\n [ngClass]=\"appState?.class\"\n >\n {{ appState?.label | translate }}\n </span>\n </button>\n <button\n class=\"btn-clean\"\n [attr.aria-label]=\"\n (packageContentState?.label | translate) +\n ': ' +\n (packageContentState?.tooltip\n ? packageContentState?.tooltip\n : PACKAGE_CONTENT_UNDETERMINED_LABEL) | translate\n \"\n [tooltip]=\"\n (packageContentState?.tooltip\n ? packageContentState?.tooltip\n : PACKAGE_CONTENT_UNDETERMINED_LABEL\n ) | translate\n \"\n placement=\"top\"\n *ngIf=\"isPackage\"\n >\n <span\n class=\"label\"\n [ngClass]=\"packageContentState?.class ? packageContentState?.class : 'label-danger'\"\n >\n {{\n packageContentState?.label || packageTypeLabels[PACKAGE_TYPE.UNKNOWN].label | translate\n }}\n </span>\n </button>\n </div>\n <div class=\"card-footer no-min-height p-t-0 p-b-0 card-column-20 text-center-grid\">\n <div class=\"p-b-16 card-hidden-list\"></div>\n <button\n class=\"btn btn-sm btn-default\"\n [title]=\"openButtonTitle | translate\"\n type=\"button\"\n (click)=\"openApp(); $event.stopPropagation()\"\n *ngIf=\"canOpenInBrowser\"\n [disabled]=\"disableOpenInBrowser\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"external-link\"\n ></i>\n {{ 'Open' | translate }}\n </button>\n <div class=\"p-b-32 card-hidden-list\"></div>\n </div>\n </div>\n <div class=\"p-r-40 card-hidden-grid\"></div>\n <div\n class=\"card-actions m-t-0\"\n *ngIf=\"canEdit || canOpenInBrowser || canDelete\"\n >\n <div\n class=\"dropdown\"\n #appCardDropdown=\"bs-dropdown\"\n dropdown\n [cdkTrapFocus]=\"appCardDropdown.isOpen\"\n >\n <button\n class=\"dropdown-toggle c8y-dropdown\"\n title=\"{{ 'Settings' | translate }}\"\n data-cy=\"application-card--settings-button\"\n type=\"button\"\n dropdownToggle\n >\n <i c8yIcon=\"ellipsis-v\"></i>\n </button>\n <ul\n class=\"dropdown-menu dropdown-menu-right\"\n *dropdownMenu\n >\n <li *ngIf=\"canEdit\">\n <button\n title=\"{{ 'Edit' | translate }}\"\n data-cy=\"application-card--edit-button\"\n (click)=\"detail()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"pencil\"\n ></i>\n {{ 'Edit' | translate }}\n </button>\n </li>\n <ng-container *c8yIfAllowed=\"['ROLE_APPLICATION_MANAGEMENT_ADMIN']\">\n <li>\n <button\n [title]=\"canDelete ? ('Delete' | translate) : (CANNOT_DELETE_HINT | translate)\"\n type=\"button\"\n data-cy=\"application-card--delete-button\"\n [disabled]=\"!canDelete\"\n (click)=\"delete()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"delete\"\n ></i>\n {{ 'Delete' | translate }}\n </button>\n </li>\n <li *ngIf=\"canClone\">\n <button\n [title]=\"'Clone`package,verb`' | translate\"\n type=\"button\"\n *ngIf=\"isPackage\"\n (click)=\"clone()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"copy\"\n ></i>\n {{ 'Clone`package,verb`' | translate }}\n </button>\n <button\n [title]=\"'Clone`application,verb`' | translate\"\n type=\"button\"\n *ngIf=\"!isPackage\"\n (click)=\"clone()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"copy\"\n ></i>\n {{ 'Clone`application,verb`' | translate }}\n </button>\n </li>\n </ng-container>\n </ul>\n </div>\n </div>\n</div>\n\n<button\n class=\"card pointer card--d-col\"\n type=\"button\"\n (click)=\"detail()\"\n *ngIf=\"isFeature\"\n>\n <div class=\"card-block text-center card-column-40\">\n <div class=\"card-appicon\">\n <c8y-app-icon\n *ngIf=\"!isPackage && !isFeature && !isMicroservice && !isExternal\"\n [app]=\"app\"\n [contextPath]=\"app.contextPath\"\n [name]=\"app.name\"\n ></c8y-app-icon>\n <i c8yIcon=\"tab\"></i>\n </div>\n <div>\n <p\n class=\"e2e-appCardName text-medium\"\n title=\"{{ app | humanizeAppName | async }}\"\n >\n {{ app | humanizeAppName | async }}\n </p>\n <ng-container *ngIf=\"isShowVersion\">\n <p class=\"small text-muted\">\n <em>{{ app.manifest.version }}</em>\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'Update available' | translate\"\n *ngIf=\"shouldUpgradePackage && isUnpacked\"\n ></i>\n </p>\n </ng-container>\n </div>\n </div>\n <div class=\"card-block p-0 no-min-height card--d-col card-column-80 flex-grow\">\n <div class=\"card-block no-min-height p-t-0 p-b-0 card-column-80\">\n <div\n class=\"text-center-grid\"\n *ngIf=\"app.description || app.manifest?.description\"\n >\n <div class=\"p-b-8 card-hidden-grid\"></div>\n <p class=\"small l-h-tight p-b-8 p-l-0 p-r-0\">\n {{ app.description || app.manifest?.description | translate }}\n </p>\n </div>\n </div>\n <div class=\"card-block no-min-height p-t-0 p-b-0 card-column-20 text-center-grid\">\n <span\n class=\"label\"\n [ngClass]=\"appState.class\"\n >\n {{ appState.label | translate }}\n </span>\n </div>\n <div\n class=\"card-block no-min-height p-t-0 p-b-0 card-column-20 text-center-grid\"\n *ngIf=\"isPackage\"\n >\n <span\n class=\"label\"\n [ngClass]=\"packageContentState.class\"\n >\n {{ packageContentState.label | translate }}\n </span>\n </div>\n <div class=\"p-b-32 card-hidden-list\"></div>\n </div>\n</button>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppIconComponent, selector: "c8y-app-icon", inputs: ["contextPath", "name", "app"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: IfAllowedDirective, selector: "[c8yIfAllowed]", inputs: ["c8yIfAllowed", "c8yIfAllowedAllowAny"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: HumanizeAppNamePipe, name: "humanizeAppName" }, { kind: "pipe", type: TranslatePackageLabelPipe, name: "translatePackageLabel" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ApplicationCardComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-application-card', imports: [
NgIf,
TooltipDirective,
NgClass,
AppIconComponent,
IconDirective,
BsDropdownDirective,
CdkTrapFocus,
BsDropdownToggleDirective,
BsDropdownMenuDirective,
IfAllowedDirective,
C8yTranslatePipe,
AsyncPipe,
HumanizeAppNamePipe,
TranslatePackageLabelPipe
], template: "<div\n class=\"card pointer card--d-col notFeature\"\n data-cy=\"application-card--extensions-list\"\n *ngIf=\"!isFeature\"\n>\n <div\n class=\"card-block text-center card-column-40\"\n data-cy=\"application-card--package-name\"\n (click)=\"detail()\"\n >\n <button\n class=\"card__ribbon btn-clean\"\n [attr.aria-label]=\"\n (app.label || app.manifest?.label | translatePackageLabel) +\n ': ' +\n (packageTypeLabels[packageType].tooltip | translate)\n \"\n tooltip=\"{{ packageTypeLabels[packageType].tooltip | translate }}\"\n placement=\"right\"\n type=\"button\"\n *ngIf=\"packageType !== PACKAGE_TYPE.CUSTOM\"\n [delay]=\"500\"\n >\n <span\n [ngClass]=\"{\n 'bg-info': packageType === PACKAGE_TYPE.COMMUNITY,\n 'bg-primary': packageType === PACKAGE_TYPE.OFFICIAL,\n 'bg-warning': packageType === PACKAGE_TYPE.ARCHIVED\n }\"\n >\n {{ app.label || app.manifest?.label | translatePackageLabel }}\n </span>\n </button>\n <div class=\"card-appicon\">\n <c8y-app-icon\n *ngIf=\"!isPackage && !isFeature && !isMicroservice && !isExternal\"\n [app]=\"app\"\n [contextPath]=\"app.contextPath\"\n [name]=\"app.name\"\n ></c8y-app-icon>\n <i\n c8yIcon=\"big-parcel\"\n *ngIf=\"isPackage\"\n ></i>\n <i\n c8yIcon=\"microchip\"\n *ngIf=\"isMicroservice\"\n ></i>\n <i\n c8yIcon=\"globe1\"\n *ngIf=\"isExternal\"\n ></i>\n </div>\n <div style=\"line-height: 1.3\">\n <p class=\"e2e-appCardName text-medium text-break-word\">\n {{ app | humanizeAppName | async }}\n </p>\n <ng-container *ngIf=\"isShowVersion\">\n <p class=\"small text-muted\">\n <em>{{ app.manifest.version }}</em>\n <button\n class=\"btn-clean m-l-4\"\n [attr.aria-label]=\"'Update available' | translate\"\n [tooltip]=\"'Update available' | translate\"\n *ngIf=\"shouldUpgradePackage && isUnpacked\"\n >\n <i\n class=\"text-warning icon-16\"\n c8yIcon=\"exclamation-triangle\"\n ></i>\n </button>\n </p>\n </ng-container>\n <p\n class=\"small text-break-word\"\n *ngIf=\"app.manifest?.author\"\n >\n {{ app.manifest.author }}\n </p>\n </div>\n </div>\n <div\n class=\"card-block p-0 no-min-height card--d-col card-column-80 flex-grow\"\n (click)=\"detail()\"\n >\n <div class=\"card-block no-min-height p-t-0 p-b-0 card-column-80\">\n <div class=\"text-center-grid\">\n <div class=\"p-b-8 card-hidden-grid\"></div>\n <p\n class=\"small l-h-tight p-b-8 p-l-0 p-r-0 text-break-word\"\n *ngIf=\"app.description || app.manifest?.description\"\n >\n {{ app.description || app.manifest?.description | translate }}\n </p>\n <p\n class=\"small l-h-tight p-b-8 p-l-0 p-r-0\"\n *ngIf=\"!app.description && !app.manifest?.description\"\n >\n <em class=\"text-muted\">{{ 'No description available.' | translate }}</em>\n </p>\n </div>\n </div>\n <div\n class=\"card-block no-min-height p-t-0 p-b-0 d-flex card-column-20 text-center-grid flex-wrap gap-8\"\n >\n <button\n class=\"btn-clean\"\n [attr.aria-label]=\"\n ((isUnpacked ? UNPACKED_LABEL : appState?.label) | translate) +\n ': ' +\n (appState?.tooltip | translate)\n \"\n [tooltip]=\"appState?.tooltip | translate\"\n placement=\"top\"\n [delay]=\"500\"\n >\n <span\n class=\"label\"\n [ngClass]=\"appState?.class\"\n >\n {{ appState?.label | translate }}\n </span>\n </button>\n <button\n class=\"btn-clean\"\n [attr.aria-label]=\"\n (packageContentState?.label | translate) +\n ': ' +\n (packageContentState?.tooltip\n ? packageContentState?.tooltip\n : PACKAGE_CONTENT_UNDETERMINED_LABEL) | translate\n \"\n [tooltip]=\"\n (packageContentState?.tooltip\n ? packageContentState?.tooltip\n : PACKAGE_CONTENT_UNDETERMINED_LABEL\n ) | translate\n \"\n placement=\"top\"\n *ngIf=\"isPackage\"\n >\n <span\n class=\"label\"\n [ngClass]=\"packageContentState?.class ? packageContentState?.class : 'label-danger'\"\n >\n {{\n packageContentState?.label || packageTypeLabels[PACKAGE_TYPE.UNKNOWN].label | translate\n }}\n </span>\n </button>\n </div>\n <div class=\"card-footer no-min-height p-t-0 p-b-0 card-column-20 text-center-grid\">\n <div class=\"p-b-16 card-hidden-list\"></div>\n <button\n class=\"btn btn-sm btn-default\"\n [title]=\"openButtonTitle | translate\"\n type=\"button\"\n (click)=\"openApp(); $event.stopPropagation()\"\n *ngIf=\"canOpenInBrowser\"\n [disabled]=\"disableOpenInBrowser\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"external-link\"\n ></i>\n {{ 'Open' | translate }}\n </button>\n <div class=\"p-b-32 card-hidden-list\"></div>\n </div>\n </div>\n <div class=\"p-r-40 card-hidden-grid\"></div>\n <div\n class=\"card-actions m-t-0\"\n *ngIf=\"canEdit || canOpenInBrowser || canDelete\"\n >\n <div\n class=\"dropdown\"\n #appCardDropdown=\"bs-dropdown\"\n dropdown\n [cdkTrapFocus]=\"appCardDropdown.isOpen\"\n >\n <button\n class=\"dropdown-toggle c8y-dropdown\"\n title=\"{{ 'Settings' | translate }}\"\n data-cy=\"application-card--settings-button\"\n type=\"button\"\n dropdownToggle\n >\n <i c8yIcon=\"ellipsis-v\"></i>\n </button>\n <ul\n class=\"dropdown-menu dropdown-menu-right\"\n *dropdownMenu\n >\n <li *ngIf=\"canEdit\">\n <button\n title=\"{{ 'Edit' | translate }}\"\n data-cy=\"application-card--edit-button\"\n (click)=\"detail()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"pencil\"\n ></i>\n {{ 'Edit' | translate }}\n </button>\n </li>\n <ng-container *c8yIfAllowed=\"['ROLE_APPLICATION_MANAGEMENT_ADMIN']\">\n <li>\n <button\n [title]=\"canDelete ? ('Delete' | translate) : (CANNOT_DELETE_HINT | translate)\"\n type=\"button\"\n data-cy=\"application-card--delete-button\"\n [disabled]=\"!canDelete\"\n (click)=\"delete()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"delete\"\n ></i>\n {{ 'Delete' | translate }}\n </button>\n </li>\n <li *ngIf=\"canClone\">\n <button\n [title]=\"'Clone`package,verb`' | translate\"\n type=\"button\"\n *ngIf=\"isPackage\"\n (click)=\"clone()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"copy\"\n ></i>\n {{ 'Clone`package,verb`' | translate }}\n </button>\n <button\n [title]=\"'Clone`application,verb`' | translate\"\n type=\"button\"\n *ngIf=\"!isPackage\"\n (click)=\"clone()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"copy\"\n ></i>\n {{ 'Clone`application,verb`' | translate }}\n </button>\n </li>\n </ng-container>\n </ul>\n </div>\n </div>\n</div>\n\n<button\n class=\"card pointer card--d-col\"\n type=\"button\"\n (click)=\"detail()\"\n *ngIf=\"isFeature\"\n>\n <div class=\"card-block text-center card-column-40\">\n <div class=\"card-appicon\">\n <c8y-app-icon\n *ngIf=\"!isPackage && !isFeature && !isMicroservice && !isExternal\"\n [app]=\"app\"\n [contextPath]=\"app.contextPath\"\n [name]=\"app.name\"\n ></c8y-app-icon>\n <i c8yIcon=\"tab\"></i>\n </div>\n <div>\n <p\n class=\"e2e-appCardName text-medium\"\n title=\"{{ app | humanizeAppName | async }}\"\n >\n {{ app | humanizeAppName | async }}\n </p>\n <ng-container *ngIf=\"isShowVersion\">\n <p class=\"small text-muted\">\n <em>{{ app.manifest.version }}</em>\n <i\n class=\"text-warning m-l-4\"\n c8yIcon=\"exclamation-triangle\"\n [tooltip]=\"'Update available' | translate\"\n *ngIf=\"shouldUpgradePackage && isUnpacked\"\n ></i>\n </p>\n </ng-container>\n </div>\n </div>\n <div class=\"card-block p-0 no-min-height card--d-col card-column-80 flex-grow\">\n <div class=\"card-block no-min-height p-t-0 p-b-0 card-column-80\">\n <div\n class=\"text-center-grid\"\n *ngIf=\"app.description || app.manifest?.description\"\n >\n <div class=\"p-b-8 card-hidden-grid\"></div>\n <p class=\"small l-h-tight p-b-8 p-l-0 p-r-0\">\n {{ app.description || app.manifest?.description | translate }}\n </p>\n </div>\n </div>\n <div class=\"card-block no-min-height p-t-0 p-b-0 card-column-20 text-center-grid\">\n <span\n class=\"label\"\n [ngClass]=\"appState.class\"\n >\n {{ appState.label | translate }}\n </span>\n </div>\n <div\n class=\"card-block no-min-height p-t-0 p-b-0 card-column-20 text-center-grid\"\n *ngIf=\"isPackage\"\n >\n <span\n class=\"label\"\n [ngClass]=\"packageContentState.class\"\n >\n {{ packageContentState.label | translate }}\n </span>\n </div>\n <div class=\"p-b-32 card-hidden-list\"></div>\n </div>\n</button>\n" }]
}], ctorParameters: () => [{ type: i3$1.ApplicationService }, { type: EcosystemService }, { type: i1.AlertService }, { type: i4.Router }, { type: i1.WizardModalService }, { type: i1.AlertService }, { type: i1.PluginsService }, { type: i1.GainsightService }], propDecorators: { app: [{
type: Input
}], canEdit: [{
type: Input
}], onAppDeleted: [{
type: Output
}], onAppCloned: [{
type: Output
}] } });
class ApplicationPropertiesFormComponent {
constructor(formBuilder) {
this.formBuilder = formBuilder;
this.disabled = false;
this.initForm();
}
ngOnChanges(changes) {
if (changes.application &&
changes.application.currentValue !== changes.application.previousValue) {
this.setForm(this.application);
}
if (changes.disabled && changes.disabled.currentValue !== changes.disabled.previousValue) {
changes.disabled.currentValue ? this.formGroup.disable() : this.formGroup.enable();
}
}
initForm() {
this.formGroup = this.formBuilder.group({
name: [Validators.required, Validators.maxLength(120)],
key: [Validators.required, Validators.maxLength(120)],
contextPath: [Validators.required, Validators.maxLength(120)],
icon: [undefined, [Validators.minLength(1)]]
});
}
setForm(applicationConfig) {
this.formGroup.patchValue({ ...applicationConfig });
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ApplicationPropertiesFormComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ApplicationPropertiesFormComponent, isStandalone: true, selector: "c8y-application-properties-form", inputs: { application: "application", disabled: "disabled" }, usesOnChanges: true, ngImport: i0, template: "<form\n class=\"c8y-wizard-form text-left\"\n name=\"appDeployForm\"\n (ngSubmit)=\"(formGroup.valid)\"\n [formGroup]=\"formGroup\"\n novalidate\n>\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. My application' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n formControlName=\"name\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"key\"\n translate\n >\n Application key\n </label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. my-application-key' | translate }}\"\n name=\"key\"\n type=\"text\"\n required\n formControlName=\"key\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"contextPath\"\n translate\n >\n Path\n </label>\n <div class=\"input-group\">\n <span class=\"input-group-addon\">/apps/</span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. my-application' | translate }}\"\n name=\"contextPath\"\n type=\"text\"\n required\n formControlName=\"contextPath\"\n />\n </div>\n </c8y-form-group>\n <c8y-form-group>\n <label>{{ 'Select icon' | translate }}</label>\n <c8y-icon-selector-wrapper\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </c8y-form-group>\n</form>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: IconSelectorWrapperComponent, selector: "c8y-icon-selector-wrapper", inputs: ["canRemoveIcon", "selectedIcon", "iconSize"], outputs: ["onSelect"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ApplicationPropertiesFormComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-application-properties-form', imports: [
FormsModule,
ReactiveFormsModule,
FormGroupComponent,
C8yTranslateDirective,
RequiredInputPlaceholderDirective,
IconSelectorWrapperComponent,
C8yTranslatePipe
], template: "<form\n class=\"c8y-wizard-form text-left\"\n name=\"appDeployForm\"\n (ngSubmit)=\"(formGroup.valid)\"\n [formGroup]=\"formGroup\"\n novalidate\n>\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. My application' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n formControlName=\"name\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"key\"\n translate\n >\n Application key\n </label>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. my-application-key' | translate }}\"\n name=\"key\"\n type=\"text\"\n required\n formControlName=\"key\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"contextPath\"\n translate\n >\n Path\n </label>\n <div class=\"input-group\">\n <span class=\"input-group-addon\">/apps/</span>\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. my-application' | translate }}\"\n name=\"contextPath\"\n type=\"text\"\n required\n formControlName=\"contextPath\"\n />\n </div>\n </c8y-form-group>\n <c8y-form-group>\n <label>{{ 'Select icon' | translate }}</label>\n <c8y-icon-selector-wrapper\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </c8y-form-group>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1$1.FormBuilder }], propDecorators: { application: [{
type: Input
}], disabled: [{
type: Input
}] } });
class DuplicateApplicationListComponent {
constructor(ecosystemService, wizardComponent) {
this.ecosystemService = ecosystemService;
this.wizardComponent = wizardComponent;
this.onSelectedApp = new EventEmitter();
}
select(selectedApp) {
this.onSelectedApp.emit(selectedApp);
this.stepper.next();
}
cancel() {
this.wizardComponent.close();
}
back() {
this.wizardComponent.reset();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DuplicateApplicationListComponent, deps: [{ token: EcosystemService }, { token: i1.WizardComponent }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DuplicateApplicationListComponent, isStandalone: true, selector: "c8y-duplicate-application-list", inputs: { stepper: "stepper", existingApps: "existingApps" }, outputs: { onSelectedApp: "onSelectedApp" }, ngImport: i0, template: "<div class=\"c8y-wizard-list-nav\">\n <button\n (click)=\"select(app)\"\n *ngFor=\"let app of existingApps\"\n class=\"list-group-item text-truncate p-t-8 p-b-8 bg-level-0\"\n type=\"button\"\n >\n <c8y-app-icon\n [app]=\"app\"\n [contextPath]=\"app.contextPath\"\n [name]=\"app.name\"\n class=\"list-group-icon\"\n ></c8y-app-icon>\n <span [innerText]=\"app | humanizeAppName | async\" class=\"pointer\"></span>\n <span [ngClass]=\"ecosystemService.getAppState(app).class\" class=\"label m-l-16\">\n {{ ecosystemService.getAppState(app).label | translate }}\n </span>\n </button>\n</div>\n<c8y-wizard-footer>\n <button (click)=\"back()\" class=\"btn btn-default\" title=\"{{ 'Back' | translate }}\" type=\"button\">\n {{ 'Back' | translate }}\n </button>\n <button\n (click)=\"cancel()\"\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n >\n {{ 'Cancel' | translate }}\n </button>\n</c8y-wizard-footer>\n", dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: AppIconComponent, selector: "c8y-app-icon", inputs: ["contextPath", "name", "app"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: WizardFooterComponent, selector: "c8y-wizard-footer" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: HumanizeAppNamePipe, name: "humanizeAppName" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DuplicateApplicationListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-duplicate-application-list', imports: [
NgFor,
AppIconComponent,
NgClass,
WizardFooterComponent,
C8yTranslatePipe,
AsyncPipe,
HumanizeAppNamePipe
], template: "<div class=\"c8y-wizard-list-nav\">\n <button\n (click)=\"select(app)\"\n *ngFor=\"let app of existingApps\"\n class=\"list-group-item text-truncate p-t-8 p-b-8 bg-level-0\"\n type=\"button\"\n >\n <c8y-app-icon\n [app]=\"app\"\n [contextPath]=\"app.contextPath\"\n [name]=\"app.name\"\n class=\"list-group-icon\"\n ></c8y-app-icon>\n <span [innerText]=\"app | humanizeAppName | async\" class=\"pointer\"></span>\n <span [ngClass]=\"ecosystemService.getAppState(app).class\" class=\"label m-l-16\">\n {{ ecosystemService.getAppState(app).label | translate }}\n </span>\n </button>\n</div>\n<c8y-wizard-footer>\n <button (click)=\"back()\" class=\"btn btn-default\" title=\"{{ 'Back' | translate }}\" type=\"button\">\n {{ 'Back' | translate }}\n </button>\n <button\n (click)=\"cancel()\"\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n >\n {{ 'Cancel' | translate }}\n </button>\n</c8y-wizard-footer>\n" }]
}], ctorParameters: () => [{ type: EcosystemService }, { type: i1.WizardComponent }], propDecorators: { stepper: [{
type: Input
}], existingApps: [{
type: Input
}], onSelectedApp: [{
type: Output
}] } });
class DuplicateApplicationPropertiesComponent {
constructor(bsModalRef, ecosystemService, applicationService, gainsightService) {
this.bsModalRef = bsModalRef;
this.ecosystemService = ecosystemService;
this.applicationService = applicationService;
this.gainsightService = gainsightService;
this.CURRENT_LOCATION = location.href;
this.isFirstStep = false;
this.duplicatedApp = new EventEmitter();
this.disableForm = false;
this.inProgress = false;
}
ngOnInit() {
this.isSubscribedApp$ = this.selectedApp.pipe(distinctUntilKeyChanged('id'), map(app => (app ? this.ecosystemService.getAppState(app) === APP_STATE.SUBSCRIBED : true)), tap(isSubscribed => {
this.disableForm = isSubscribed;
this.getAppConfig();
}));
this.getAppConfig();
}
async duplicateApp() {
const formGroupValue = this.applicationPropertiesForm.formGroup.getRawValue();
this.inProgress = true;
let { data: clonedApp } = await this.applicationService.clone(this.selectedApp.value);
// TODO: remove once MTM-48474 has been merged
clonedApp = this.ecosystemService.setAvailabilityToPrivateIfNotSetAlready(clonedApp);
Object.assign(clonedApp, formGroupValue);
if (formGroupValue.icon) {
const icon = { class: formGroupValue.icon };
clonedApp.config = { ...clonedApp.config, icon };
if (clonedApp.manifest) {
clonedApp.manifest.icon = icon;
}
}
delete clonedApp.type;
await this.updateApp(clonedApp);
this.inProgress = false;
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.DUPLICATE_APPLICATION, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.DUPLICATE_APPLICATION_PROPERTIES,
result: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.RESULTS.DUPLICATED,
url: this.CURRENT_LOCATION
});
}
cancel() {
this.bsModalRef.hide();
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.DUPLICATE_APPLICATION, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.DUPLICATE_APPLICATION_PROPERTIES,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.CANCEL,
url: this.CURRENT_LOCATION
});
}
back() {
this.stepper.previous();
}
getAppConfig() {
if (this.disableForm) {
const { name, key, contextPath } = this.selectedApp.value;
this.newAppConfig = { name, key, contextPath };
}
else {
this.newAppConfig = this.ecosystemService.getUniqueAppConfig(this.selectedApp.value, this.existingApps);
}
}
async updateApp(clonedAppConfig) {
try {
const { data: app } = await this.ecosystemService.updateApp(clonedAppConfig, true);
const manifest = await this.getAppManifest(app);
if (manifest) {
await this.updateManifest(app, manifest);
}
this.duplicatedApp.emit(app);
this.stepper.next();
}
catch (err) {
this.inProgress = false;
}
}
async updateManifest(app, manifest) {
const keysToUpdate = ['name', 'key', 'contextPath'];
const someKeyDiffers = keysToUpdate.some(key => app[key] !== manifest[key]);
if (someKeyDiffers) {
keysToUpdate.forEach(key => {
manifest[key] = app[key];
});
await this.applicationService.storeAppManifest(app, manifest);
}
}
async getAppManifest(app) {
if (!app?.contextPath) {
return;
}
try {
const manifest = await this.applicationService.getAppManifest(app);
return manifest;
}
catch (ex) {
return;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DuplicateApplicationPropertiesComponent, deps: [{ token: i1$2.BsModalRef }, { token: EcosystemService }, { token: i3$1.ApplicationService }, { token: i1.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DuplicateApplicationPropertiesComponent, isStandalone: true, selector: "c8y-duplicate-application-properties", inputs: { stepper: "stepper", existingApps: "existingApps", selectedApp: "selectedApp", isFirstStep: "isFirstStep" }, outputs: { duplicatedApp: "duplicatedApp" }, viewQueries: [{ propertyName: "applicationPropertiesForm", first: true, predicate: ApplicationPropertiesFormComponent, descendants: true }], ngImport: i0, template: "<p\n class=\"p-16 text-center text-medium separator-bottom sticky-top bg-level-0 fit-w\"\n *ngIf=\"!inProgress\"\n>\n {{ 'Provide application details' | translate }}\n</p>\n\n<ng-container *ngIf=\"!inProgress\">\n <label class=\"c8y-switch\" *ngIf=\"isSubscribedApp$ | async\">\n <input type=\"checkbox\" [(ngModel)]=\"disableForm\" (change)=\"getAppConfig()\" />\n <span></span> {{ 'Overrule subscribed application' | translate }}\n </label>\n\n <c8y-application-properties-form\n [application]=\"newAppConfig\"\n class=\"d-block fit-w\"\n [disabled]=\"disableForm\"\n ></c8y-application-properties-form>\n</ng-container>\n<c8y-loading\n *ngIf=\"inProgress\"\n [message]=\"'Duplicating\u2026' | translate\"\n layout=\"application\"\n class=\"text-center d-block\"\n></c8y-loading>\n\n<c8y-wizard-footer>\n <button\n *ngIf=\"!isFirstStep\"\n (click)=\"back()\"\n [disabled]=\"inProgress\"\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Back' | translate }}\"\n >\n {{ 'Back' | translate }}\n </button>\n <button (click)=\"cancel()\" class=\"btn btn-default\" title=\"{{ 'Cancel' | translate }}\">\n {{ 'Cancel' | translate }}\n </button>\n <button\n (click)=\"duplicateApp()\"\n [disabled]=\"inProgress\"\n class=\"btn btn-primary\"\n type=\"button\"\n title=\"{{ 'Duplicate' | translate }}\"\n >\n {{ 'Duplicate' | translate }}\n </button>\n</c8y-wizard-footer>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ApplicationPropertiesFormComponent, selector: "c8y-application-properties-form", inputs: ["application", "disabled"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: WizardFooterComponent, selector: "c8y-wizard-footer" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DuplicateApplicationPropertiesComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-duplicate-application-properties', imports: [
NgIf,
FormsModule,
ApplicationPropertiesFormComponent,
LoadingComponent,
WizardFooterComponent,
C8yTranslatePipe,
AsyncPipe
], template: "<p\n class=\"p-16 text-center text-medium separator-bottom sticky-top bg-level-0 fit-w\"\n *ngIf=\"!inProgress\"\n>\n {{ 'Provide application details' | translate }}\n</p>\n\n<ng-container *ngIf=\"!inProgress\">\n <label class=\"c8y-switch\" *ngIf=\"isSubscribedApp$ | async\">\n <input type=\"checkbox\" [(ngModel)]=\"disableForm\" (change)=\"getAppConfig()\" />\n <span></span> {{ 'Overrule subscribed application' | translate }}\n </label>\n\n <c8y-application-properties-form\n [application]=\"newAppConfig\"\n class=\"d-block fit-w\"\n [disabled]=\"disableForm\"\n ></c8y-application-properties-form>\n</ng-container>\n<c8y-loading\n *ngIf=\"inProgress\"\n [message]=\"'Duplicating\u2026' | translate\"\n layout=\"application\"\n class=\"text-center d-block\"\n></c8y-loading>\n\n<c8y-wizard-footer>\n <button\n *ngIf=\"!isFirstStep\"\n (click)=\"back()\"\n [disabled]=\"inProgress\"\n class=\"btn btn-default\"\n type=\"button\"\n title=\"{{ 'Back' | translate }}\"\n >\n {{ 'Back' | translate }}\n </button>\n <button (click)=\"cancel()\" class=\"btn btn-default\" title=\"{{ 'Cancel' | translate }}\">\n {{ 'Cancel' | translate }}\n </button>\n <button\n (click)=\"duplicateApp()\"\n [disabled]=\"inProgress\"\n class=\"btn btn-primary\"\n type=\"button\"\n title=\"{{ 'Duplicate' | translate }}\"\n >\n {{ 'Duplicate' | translate }}\n </button>\n</c8y-wizard-footer>\n" }]
}], ctorParameters: () => [{ type: i1$2.BsModalRef }, { type: EcosystemService }, { type: i3$1.ApplicationService }, { type: i1.GainsightService }], propDecorators: { stepper: [{
type: Input
}], existingApps: [{
type: Input
}], selectedApp: [{
type: Input
}], isFirstStep: [{
type: Input
}], duplicatedApp: [{
type: Output
}], applicationPropertiesForm: [{
type: ViewChild,
args: [ApplicationPropertiesFormComponent]
}] } });
class DuplicateApplicationComponent {
constructor(ecosystemService, applicationService, wizardComponent) {
this.ecosystemService = ecosystemService;
this.applicationService = applicationService;
this.wizardComponent = wizardComponent;
this.selectedApp = new BehaviorSubject(null);
this.existingApps = [];
this.directlyMoveToSecondStep = false;
this.noBackButton = false;
}
async ngOnInit() {
this.existingApps = await this.ecosystemService.getWebApplications();
if (this.selectedApp.value) {
this.directlyMoveToSecondStep = true;
}
}
onSelectedApp(app) {
this.selectedApp.next(app);
}
getHref(app) {
return this.applicationService.getHref(app);
}
done() {
this.wizardComponent.close();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DuplicateApplicationComponent, deps: [{ token: EcosystemService }, { token: i3$1.ApplicationService }, { token: i1.WizardComponent }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DuplicateApplicationComponent, isStandalone: true, selector: "c8y-duplicate-application", viewQueries: [{ propertyName: "stepper", first: true, predicate: C8yStepper, descendants: true, static: true }], ngImport: i0, template: "<c8y-wizard-header>\n <i [c8yIcon]=\"'copy'\"></i>\n <h4\n id=\"modal-title\"\n translate\n >\n Duplicate application\n </h4>\n</c8y-wizard-header>\n\n<c8y-wizard-body id=\"modal-body\">\n <c8y-stepper\n [disableDefaultIcons]=\"{ edit: true, done: false }\"\n [customClasses]=\"['hidden']\"\n linear\n >\n <!-- 1st step -->\n <cdk-step *ngIf=\"!directlyMoveToSecondStep\">\n <p class=\"p-16 text-center text-medium separator-bottom sticky-top bg-level-0\">\n {{ 'Select application to duplicate' | translate }}\n </p>\n <c8y-duplicate-application-list\n class=\"d-block\"\n style=\"min-height: 257px\"\n (onSelectedApp)=\"onSelectedApp($event)\"\n *ngIf=\"stepper.selectedIndex === 0\"\n [existingApps]=\"existingApps\"\n [stepper]=\"stepper\"\n ></c8y-duplicate-application-list>\n </cdk-step>\n <!-- 2nd step -->\n <cdk-step>\n <c8y-duplicate-application-properties\n class=\"d-flex d-col j-c-center a-i-center\"\n style=\"min-height: 257px\"\n (duplicatedApp)=\"duplicatedApp = $event\"\n *ngIf=\"\n stepper.selectedIndex === 1 || (stepper.selectedIndex === 0 && directlyMoveToSecondStep)\n \"\n [existingApps]=\"existingApps\"\n [selectedApp]=\"selectedApp\"\n [stepper]=\"stepper\"\n [isFirstStep]=\"directlyMoveToSecondStep\"\n ></c8y-duplicate-application-properties>\n </cdk-step>\n <!-- Final step -->\n <cdk-step>\n <ng-container\n *ngIf=\"\n stepper.selectedIndex === 2 || (stepper.selectedIndex === 1 && directlyMoveToSecondStep)\n \"\n >\n <div\n class=\"d-flex a-i-center j-c-center\"\n style=\"min-height: 257px\"\n *ngIf=\"duplicatedApp; else duplicateAppFailed\"\n >\n <c8y-operation-result\n type=\"success\"\n text=\"{{ 'Application created' | translate }}\"\n [size]=\"84\"\n [vertical]=\"true\"\n ></c8y-operation-result>\n </div>\n <ng-template #duplicateAppFailed>\n <div\n class=\"d-flex a-i-center j-c-center\"\n style=\"min-height: 257px\"\n >\n <c8y-operation-result\n type=\"error\"\n text=\"{{ 'Application creation failed' | translate }}\"\n [size]=\"84\"\n [vertical]=\"true\"\n ></c8y-operation-result>\n </div>\n </ng-template>\n </ng-container>\n </cdk-step>\n </c8y-stepper>\n</c8y-wizard-body>\n\n<c8y-wizard-footer *ngIf=\"stepper.selectedIndex === 0 && noBackButton\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"done()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n</c8y-wizard-footer>\n\n<c8y-wizard-footer\n *ngIf=\"stepper.selectedIndex === 2 || (stepper.selectedIndex === 1 && directlyMoveToSecondStep)\"\n>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Done' | translate }}\"\n type=\"button\"\n (click)=\"done()\"\n >\n {{ 'Done' | translate }}\n </button>\n <a\n class=\"btn btn-primary\"\n title=\"{{ 'Open' | translate }}\"\n target=\"_blank\"\n (click)=\"$event.stopPropagation()\"\n [href]=\"getHref(duplicatedApp)\"\n rel=\"noopener noreferrer\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"external-link\"\n ></i>\n {{ 'Open' | translate }}\n </a>\n</c8y-wizard-footer>\n", dependencies: [{ kind: "component", type: WizardHeaderComponent, selector: "c8y-wizard-header" }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: WizardBodyComponent, selector: "c8y-wizard-body" }, { kind: "component", type: C8yStepper, selector: "c8y-stepper", inputs: ["disableDefaultIcons", "disableProgressButtons", "customClasses", "hideStepProgress", "useStepLabelsAsTitlesOnly"], outputs: ["onStepChange"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CdkStep, selector: "cdk-step", inputs: ["stepControl", "label", "errorMessage", "aria-label", "aria-labelledby", "state", "editable", "optional", "completed", "hasError"], outputs: ["interacted"], exportAs: ["cdkStep"] }, { kind: "component", type: DuplicateApplicationListComponent, selector: "c8y-duplicate-application-list", inputs: ["stepper", "existingApps"], outputs: ["onSelectedApp"] }, { kind: "component", type: DuplicateApplicationPropertiesComponent, selector: "c8y-duplicate-application-properties", inputs: ["stepper", "existingApps", "selectedApp", "isFirstStep"], outputs: ["duplicatedApp"] }, { kind: "component", type: OperationResultComponent, selector: "c8y-operation-result", inputs: ["text", "vertical", "size", "type"] }, { kind: "component", type: WizardFooterComponent, selector: "c8y-wizard-footer" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DuplicateApplicationComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-duplicate-application', imports: [
WizardHeaderComponent,
IconDirective,
C8yTranslateDirective,
WizardBodyComponent,
C8yStepper,
NgIf,
CdkStep,
DuplicateApplicationListComponent,
DuplicateApplicationPropertiesComponent,
OperationResultComponent,
WizardFooterComponent,
C8yTranslatePipe
], template: "<c8y-wizard-header>\n <i [c8yIcon]=\"'copy'\"></i>\n <h4\n id=\"modal-title\"\n translate\n >\n Duplicate application\n </h4>\n</c8y-wizard-header>\n\n<c8y-wizard-body id=\"modal-body\">\n <c8y-stepper\n [disableDefaultIcons]=\"{ edit: true, done: false }\"\n [customClasses]=\"['hidden']\"\n linear\n >\n <!-- 1st step -->\n <cdk-step *ngIf=\"!directlyMoveToSecondStep\">\n <p class=\"p-16 text-center text-medium separator-bottom sticky-top bg-level-0\">\n {{ 'Select application to duplicate' | translate }}\n </p>\n <c8y-duplicate-application-list\n class=\"d-block\"\n style=\"min-height: 257px\"\n (onSelectedApp)=\"onSelectedApp($event)\"\n *ngIf=\"stepper.selectedIndex === 0\"\n [existingApps]=\"existingApps\"\n [stepper]=\"stepper\"\n ></c8y-duplicate-application-list>\n </cdk-step>\n <!-- 2nd step -->\n <cdk-step>\n <c8y-duplicate-application-properties\n class=\"d-flex d-col j-c-center a-i-center\"\n style=\"min-height: 257px\"\n (duplicatedApp)=\"duplicatedApp = $event\"\n *ngIf=\"\n stepper.selectedIndex === 1 || (stepper.selectedIndex === 0 && directlyMoveToSecondStep)\n \"\n [existingApps]=\"existingApps\"\n [selectedApp]=\"selectedApp\"\n [stepper]=\"stepper\"\n [isFirstStep]=\"directlyMoveToSecondStep\"\n ></c8y-duplicate-application-properties>\n </cdk-step>\n <!-- Final step -->\n <cdk-step>\n <ng-container\n *ngIf=\"\n stepper.selectedIndex === 2 || (stepper.selectedIndex === 1 && directlyMoveToSecondStep)\n \"\n >\n <div\n class=\"d-flex a-i-center j-c-center\"\n style=\"min-height: 257px\"\n *ngIf=\"duplicatedApp; else duplicateAppFailed\"\n >\n <c8y-operation-result\n type=\"success\"\n text=\"{{ 'Application created' | translate }}\"\n [size]=\"84\"\n [vertical]=\"true\"\n ></c8y-operation-result>\n </div>\n <ng-template #duplicateAppFailed>\n <div\n class=\"d-flex a-i-center j-c-center\"\n style=\"min-height: 257px\"\n >\n <c8y-operation-result\n type=\"error\"\n text=\"{{ 'Application creation failed' | translate }}\"\n [size]=\"84\"\n [vertical]=\"true\"\n ></c8y-operation-result>\n </div>\n </ng-template>\n </ng-container>\n </cdk-step>\n </c8y-stepper>\n</c8y-wizard-body>\n\n<c8y-wizard-footer *ngIf=\"stepper.selectedIndex === 0 && noBackButton\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"done()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n</c8y-wizard-footer>\n\n<c8y-wizard-footer\n *ngIf=\"stepper.selectedIndex === 2 || (stepper.selectedIndex === 1 && directlyMoveToSecondStep)\"\n>\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Done' | translate }}\"\n type=\"button\"\n (click)=\"done()\"\n >\n {{ 'Done' | translate }}\n </button>\n <a\n class=\"btn btn-primary\"\n title=\"{{ 'Open' | translate }}\"\n target=\"_blank\"\n (click)=\"$event.stopPropagation()\"\n [href]=\"getHref(duplicatedApp)\"\n rel=\"noopener noreferrer\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"external-link\"\n ></i>\n {{ 'Open' | translate }}\n </a>\n</c8y-wizard-footer>\n" }]
}], ctorParameters: () => [{ type: EcosystemService }, { type: i3$1.ApplicationService }, { type: i1.WizardComponent }], propDecorators: { stepper: [{
type: ViewChild,
args: [C8yStepper, { static: true }]
}] } });
class PackageVersionSelectComponent {
constructor(applicationService, pluginsService) {
this.applicationService = applicationService;
this.pluginsService = pluginsService;
this.label = gettext('Use plugin version');
this.onInput$ = new BehaviorSubject('');
this.isDisabled = false;
this.packageContextPath$ = new BehaviorSubject('');
this.packageId$ = new BehaviorSubject('');
const packageIdFromContextPath$ = this.packageContextPath$.pipe(filter(path => !!path), switchMap(path => this.getPackageIdForContextPath(path)));
const packageId$ = merge(packageIdFromContextPath$, this.packageId$.pipe(filter(id => !!id)));
const packageVersions$ = packageId$.pipe(switchMap(id => this.getPackageVersions(id)), shareReplay(1));
this.versions$ = combineLatest([packageVersions$, this.onInput$.asObservable()]).pipe(map(([resultList, filterStr]) => this.applyFilterToResultList(resultList, filterStr)));
}
writeValue(obj) {
this.selectedVersion = obj;
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this.isDisabled = isDisabled;
}
ngOnChanges(changes) {
if (changes.packageContextPath) {
this.packageContextPath$.next(this.packageContextPath);
}
if (changes.packageId) {
this.packageId$.next(this.packageId);
}
}
async getPackageVersions(packageId) {
return await this.applicationService.listVersions(packageId);
}
onVersionSelect(version) {
this.selectedVersion = version;
if (this.onChange) {
this.onChange(version);
}
if (this.onTouched) {
this.onTouched();
}
}
async getPackageIdForContextPath(contextPath) {
const { data: application } = await this.applicationService.getManifestOfContextPath(contextPath);
const packageAppId = application.id;
return packageAppId;
}
setInitialValueForInput(versions) {
if (!this.selectedVersion && versions.length > 0) {
const latest = versions.find(v => v.tags.includes('latest'));
this.selectedVersion = latest || versions[0];
if (this.onChange) {
this.onChange(this.selectedVersion);
}
}
}
filterAppVersions(appVersions, filterStr) {
return filterStr === ''
? appVersions
: appVersions.filter(appVersion => appVersion.version.includes(filterStr) ||
appVersion.tags?.some(tag => tag.includes(filterStr)));
}
applyFilterToResultList(resultList, filterStr) {
const versionsFilteredByStr = this.filterAppVersions(resultList.data, filterStr);
const sortedAppVersions = this.pluginsService.sortVersions({
list: versionsFilteredByStr,
path: ['version']
}, 'desc');
this.setInitialValueForInput(sortedAppVersions);
return { data: sortedAppVersions, res: resultList.res };
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PackageVersionSelectComponent, deps: [{ token: i3$1.ApplicationService }, { token: i1.PluginsService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: PackageVersionSelectComponent, isStandalone: true, selector: "c8y-package-version-select", inputs: { label: "label", packageContextPath: "packageContextPath", packageId: "packageId" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => PackageVersionSelectComponent)
}
], usesOnChanges: true, ngImport: i0, template: "<label for=\"packageVersion\">{{ label | translate }}</label>\n<c8y-form-group>\n <c8y-typeahead\n [(ngModel)]=\"selectedVersion\"\n name=\"packageVersion\"\n (onSearch)=\"onInput$.next($event)\"\n placeholder=\"{{ 'Select below or start typing' | translate }}\"\n [displayProperty]=\"'version'\"\n [required]=\"true\"\n [disabled]=\"isDisabled\"\n [hideNew]=\"true\"\n [container]=\"'body'\"\n >\n <c8y-li\n *c8yFor=\"let appVersion of versions$; loadMore: 'auto'; notFound: notFoundTemplate\"\n (click)=\"onVersionSelect(appVersion)\"\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n [active]=\"selectedVersion === appVersion\"\n >\n <c8y-li-icon icon=\"big-parcel\"></c8y-li-icon>\n <c8y-highlight\n [text]=\"appVersion.version || '--'\"\n [pattern]=\"onInput$ | async\"\n ></c8y-highlight>\n <span>\n <span *ngFor=\"let tag of appVersion.tags\" class=\"label label-info m-l-4\">\n {{ tag }}\n </span>\n </span>\n </c8y-li>\n <ng-template #notFoundTemplate>\n <c8y-li\n class=\"bg-gray-lighter p-8\"\n *ngIf=\"(onInput$ | async)?.length > 0 && (versions$ | async)?.data?.length === 0\"\n >\n <span translate>No match found.</span>\n </c8y-li>\n </ng-template>\n </c8y-typeahead>\n</c8y-form-group>\n", dependencies: [{ kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: TypeaheadComponent, selector: "c8y-typeahead", inputs: ["required", "maxlength", "disabled", "allowFreeEntries", "placeholder", "displayProperty", "icon", "name", "autoClose", "hideNew", "container", "selected", "title", "highlightFirstItem"], outputs: ["onSearch", "onIconClick"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PackageVersionSelectComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-package-version-select', providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => PackageVersionSelectComponent)
}
], imports: [
FormGroupComponent,
TypeaheadComponent,
FormsModule,
ForOfDirective,
ListItemComponent,
ListItemIconComponent,
HighlightComponent,
NgFor,
NgIf,
C8yTranslateDirective,
C8yTranslatePipe,
AsyncPipe
], template: "<label for=\"packageVersion\">{{ label | translate }}</label>\n<c8y-form-group>\n <c8y-typeahead\n [(ngModel)]=\"selectedVersion\"\n name=\"packageVersion\"\n (onSearch)=\"onInput$.next($event)\"\n placeholder=\"{{ 'Select below or start typing' | translate }}\"\n [displayProperty]=\"'version'\"\n [required]=\"true\"\n [disabled]=\"isDisabled\"\n [hideNew]=\"true\"\n [container]=\"'body'\"\n >\n <c8y-li\n *c8yFor=\"let appVersion of versions$; loadMore: 'auto'; notFound: notFoundTemplate\"\n (click)=\"onVersionSelect(appVersion)\"\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n [active]=\"selectedVersion === appVersion\"\n >\n <c8y-li-icon icon=\"big-parcel\"></c8y-li-icon>\n <c8y-highlight\n [text]=\"appVersion.version || '--'\"\n [pattern]=\"onInput$ | async\"\n ></c8y-highlight>\n <span>\n <span *ngFor=\"let tag of appVersion.tags\" class=\"label label-info m-l-4\">\n {{ tag }}\n </span>\n </span>\n </c8y-li>\n <ng-template #notFoundTemplate>\n <c8y-li\n class=\"bg-gray-lighter p-8\"\n *ngIf=\"(onInput$ | async)?.length > 0 && (versions$ | async)?.data?.length === 0\"\n >\n <span translate>No match found.</span>\n </c8y-li>\n </ng-template>\n </c8y-typeahead>\n</c8y-form-group>\n" }]
}], ctorParameters: () => [{ type: i3$1.ApplicationService }, { type: i1.PluginsService }], propDecorators: { label: [{
type: Input
}], packageContextPath: [{
type: Input
}], packageId: [{
type: Input
}] } });
class UploadArchiveComponent {
constructor(ecosystemService, alertService) {
this.ecosystemService = ecosystemService;
this.alertService = alertService;
this.uploadNewVersion = false;
this.applicationChange = new EventEmitter();
this.refresh = new EventEmitter();
this.archives = [];
this.uploadInProgress = false;
this.canReactivate = false;
}
get uploadProgress() {
return this.ecosystemService.progress;
}
async onFileDroppedEvent(event) {
if (this.preUploadCallback) {
try {
await this.preUploadCallback();
}
catch (e) {
// do nothing, i.e. when confirm modal cancels and throws undefined
return;
}
}
if (event && event.length > 0) {
const file = event[0].file;
this.onFile(file);
}
}
async onFile(file) {
try {
await this.ecosystemService.validateArchiveToAppCompatibility(file, this.application);
const isCustomMicroservice = this.ecosystemService.isCustomMicroservice(this.application);
const isPackage = this.ecosystemService.isPackage(this.application);
this.archives = isCustomMicroservice
? []
: await this.ecosystemService.listArchives(this.application.id);
this.archives.sort((a, b) => {
return new Date(b.created) - new Date(a.created);
});
this.uploadInProgress = true;
this.uploadProgress.next(0);
if (this.archives.length === 6 && !isCustomMicroservice && !isPackage) {
await this.ecosystemService.removeOldestArchive(this.application, this.archives);
}
const app = await this.ecosystemService.uploadArchiveToApp(file, this.application, this.uploadNewVersion);
this.applicationChange.emit(app);
this.uploadProgress.next(100);
this.uploadInProgress = false;
this.refresh.emit();
if (this.uploadNewVersion) {
return;
}
this.alertService.success(gettext('Archive uploaded and activated.'));
}
catch (ex) {
this.uploadInProgress = false;
if (ex === 'cancelled') {
return;
}
this.ecosystemService.alertError(ex);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: UploadArchiveComponent, deps: [{ token: EcosystemService }, { token: i1.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: UploadArchiveComponent, isStandalone: true, selector: "c8y-upload-archive", inputs: { application: "application", uploadNewVersion: "uploadNewVersion", preUploadCallback: "preUploadCallback" }, outputs: { applicationChange: "applicationChange", refresh: "refresh" }, ngImport: i0, template: "<c8y-drop-area\n (dropped)=\"onFileDroppedEvent($event)\"\n [accept]=\"'.zip'\"\n [loading]=\"uploadInProgress\"\n [maxAllowedFiles]=\"1\"\n [message]=\"'Upload a *.zip file' | translate\"\n [progress]=\"uploadProgress | async\"\n [forceHideList]=\"true\"\n class=\"drop-area-sm\"\n>\n</c8y-drop-area>\n", dependencies: [{ kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: UploadArchiveComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-upload-archive', imports: [DropAreaComponent, C8yTranslatePipe, AsyncPipe], template: "<c8y-drop-area\n (dropped)=\"onFileDroppedEvent($event)\"\n [accept]=\"'.zip'\"\n [loading]=\"uploadInProgress\"\n [maxAllowedFiles]=\"1\"\n [message]=\"'Upload a *.zip file' | translate\"\n [progress]=\"uploadProgress | async\"\n [forceHideList]=\"true\"\n class=\"drop-area-sm\"\n>\n</c8y-drop-area>\n" }]
}], ctorParameters: () => [{ type: EcosystemService }, { type: i1.AlertService }], propDecorators: { application: [{
type: Input
}], uploadNewVersion: [{
type: Input
}], preUploadCallback: [{
type: Input
}], applicationChange: [{
type: Output
}], refresh: [{
type: Output
}] } });
class ListFiltersComponent {
constructor(formBuilder, ui, translateService, gainsightService, router, activatedRoute) {
this.formBuilder = formBuilder;
this.ui = ui;
this.translateService = translateService;
this.gainsightService = gainsightService;
this.router = router;
this.activatedRoute = activatedRoute;
this.packageTypes = [];
this.packageAvailabilities = [];
this.packageContents = [];
this.filterPipeChange = new EventEmitter();
this.chips = [];
this.customFilters = new Map();
this.CURRENT_LOCATION = location.href;
this.destroy$ = new Subject();
this.formGroup = this.initForm();
}
ngOnInit() {
this.currentFiltersState = this.formGroup.value;
this.predefinedFiltersVisible =
this.packageTypes?.length > 0 ||
this.packageAvailabilities?.length > 0 ||
this.packageContents?.length > 0;
this.currentLang = this.ui.state.lang;
this.subscribeToQueryParams();
}
setFilterTerm(filterTerm) {
this.currentFilterTerm = filterTerm;
this.setPipe();
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.FILTER_LIST, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.LIST_FILTERS,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.SET_FILTER_TERM,
url: this.CURRENT_LOCATION
});
}
applyPredefinedFilters() {
this.currentFiltersState = this.formGroup.value;
this.setChips();
this.setPipe();
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.FILTER_LIST, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.LIST_FILTERS,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.SET_PREDEFINED_FILTERS,
url: this.CURRENT_LOCATION,
filters: this.currentFiltersState
});
}
reset() {
this.formGroup.reset();
this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.EVENTS.FILTER_LIST, {
component: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.COMPONENTS.LIST_FILTERS,
action: PRODUCT_EXPERIENCE_ECOSYSTEM.APPLICATIONS.ACTIONS.RESET_FILTER,
url: this.CURRENT_LOCATION
});
this.applyPredefinedFilters();
}
resetAllFilters() {
this.reset();
this.filterInput.reset();
}
toggleDropdown(isOpen) {
if (!isOpen) {
this.formGroup.patchValue(this.currentFiltersState);
}
}
deselectChip(chip) {
chip[1].setValue(false);
this.applyPredefinedFilters();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
subscribeToQueryParams() {
this.activatedRoute.queryParams
.pipe(takeUntil(this.destroy$))
.subscribe((queryParams) => {
const filters = this.parseQueryParams(queryParams);
this.currentFilterTerm = queryParams.filterTerm || '';
this.formGroup.patchValue(filters);
this.applyPredefinedFilters();
});
}
setQueryParams() {
if (!this.currentFiltersState) {
return;
}
this.router.navigate([], {
queryParams: {
...this.simplifyFilterObject(this.currentFiltersState),
filterTerm: this.currentFilterTerm || null
}
});
}
parseQueryParams(queryParams) {
const filter = {};
for (const fullKey in queryParams) {
const [key, subKey] = fullKey.split('.');
if (!filter[key]) {
filter[key] = {};
}
filter[key][subKey] = queryParams[fullKey] === 'true';
}
return filter;
}
simplifyFilterObject(currentFiltersState) {
const keys = Object.keys(currentFiltersState);
const result = {};
for (const key of keys) {
const subKeys = Object.keys(currentFiltersState[key]);
for (const subKey of subKeys) {
if (currentFiltersState[key][subKey]) {
result[`${key}.${subKey}`] = currentFiltersState[key][subKey];
}
}
}
return result;
}
initForm() {
return this.formBuilder.group({
type: this.formBuilder.group({
[PackageType.OFFICIAL]: [false],
[PackageType.COMMUNITY]: [false]
}),
availability: this.formBuilder.group({
[APP_STATE.SUBSCRIBED.label]: [false],
[APP_STATE.UNPACKED.label]: [false],
[APP_STATE.EXTERNAL.label]: [false],
[APP_STATE.CUSTOM.label]: [false]
}),
content: this.formBuilder.group({
[APP_STATE.PACKAGE_BLUEPRINT.label]: [false],
[APP_STATE.PACKAGE_PLUGIN.label]: [false]
}),
custom: this.formBuilder.group({})
});
}
setPipe() {
this.setQueryParams();
const filterPipe = pipe(map((data) => {
this.data = data.filter((app) => {
let matchesByString;
if (!this.currentFilterTerm || this.currentFilterTerm.trim().length === 0) {
matchesByString = true;
}
else {
const appName = this.currentLang === 'en' || !app.name?.length || !internalApps[app.name]
? app.name
: this.translateService.instant(internalApps[app.name]);
matchesByString =
this.filterContainString(appName, this.currentFilterTerm) ||
this.filterContainString(app.description, this.currentFilterTerm);
}
const matchesByPredefinedProperty = this.appMatchesByPredefinedProperty(app, 'type') &&
this.appMatchesByPredefinedProperty(app, 'availability') &&
this.appMatchesByPredefinedProperty(app, 'content') &&
this.appMatchesByPredefinedProperty(app, 'custom');
return matchesByString && matchesByPredefinedProperty;
});
return this.data;
}));
this.filterPipeChange.emit(filterPipe);
}
filterContainString(name, filterTerm) {
const term = filterTerm.toLowerCase().trim();
return name && name.toLowerCase().indexOf(term) > -1;
}
appMatchesByPredefinedProperty(app, property) {
if (property === 'custom' && this.currentFiltersState) {
return Object.entries(this.currentFiltersState[property]).every(([key, value]) => this.customFilters.get(key)(app, value));
}
// if filter object does not contain property filters or all filters are false, filter is inactive
const filterForPropertyIsActive = this.currentFiltersState?.[property] &&
!Object.values(this.currentFiltersState[property]).every(v => !v);
if (!filterForPropertyIsActive) {
return true;
}
else if (!app.filterProps?.[property]) {
return false;
}
else {
return this.currentFiltersState[property][app.filterProps[property]];
}
}
setChips() {
this.chips = [];
Object.values(this.formGroup.controls).forEach((controlsGroup) => {
Object.entries(controlsGroup.controls).forEach((entry) => {
if (entry[1].value) {
this.chips.push(entry);
}
});
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ListFiltersComponent, deps: [{ token: i1$1.FormBuilder }, { token: i1.AppStateService }, { token: i3.TranslateService }, { token: i1.GainsightService }, { token: i4.Router }, { token: i4.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ListFiltersComponent, isStandalone: true, selector: "c8y-list-filters", inputs: { packageTypes: "packageTypes", packageAvailabilities: "packageAvailabilities", packageContents: "packageContents" }, outputs: { filterPipeChange: "filterPipeChange" }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: FilterInputComponent, descendants: true }], ngImport: i0, template: "<form\n class=\"d-flex a-i-center\"\n [formGroup]=\"formGroup\"\n>\n <c8y-filter\n [filterTerm]=\"currentFilterTerm\"\n (onSearch)=\"setFilterTerm($event)\"\n ></c8y-filter>\n\n <div\n class=\"dropdown m-l-8 m-r-4\"\n *ngIf=\"predefinedFiltersVisible\"\n dropdown\n (isOpenChange)=\"toggleDropdown($event)\"\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group d-inline-flex\">\n <button\n class=\"btn-clean form-control p-l-8 p-r-16 a-s-stretch d-flex a-i-center gap-4 w-auto flex-no-shrink\"\n title=\"{{ 'Filters' | translate }}\"\n dropdownToggle\n >\n <span [ngPlural]=\"chips.length\">\n <ng-template ngPluralCase=\"=0\">\n {{ 'Filters' | translate }}\n </ng-template>\n <ng-template ngPluralCase=\"=1\">\n <span class=\"badge badge-info\">{{ chips.length }}</span>\n {{ 'Filter' | translate }}\n </ng-template>\n <ng-template ngPluralCase=\"other\">\n <span class=\"badge badge-info\">{{ chips.length }}</span>\n {{ 'Filters' | translate }}\n </ng-template>\n </span>\n <span class=\"caret\"></span>\n </button>\n <div\n class=\"input-group-addon p-t-0 p-b-0 d-flex gap-4 a-i-center inner-scroll\"\n *ngIf=\"chips.length > 0\"\n >\n <ng-container *ngFor=\"let chip of chips\">\n <span\n class=\"tag tag--info chip\"\n *ngIf=\"chip[1].value\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"deselectChip(chip)\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n {{ chip[0] | translate | titlecase }}\n </span>\n </ng-container>\n </div>\n </div>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n <div class=\"dropdown-form\">\n <c8y-form-group\n *ngIf=\"packageTypes.length\"\n formGroupName=\"type\"\n >\n <label class=\"m-b-16\">\n {{ 'Type' | translate }}\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"type | translate\"\n *ngFor=\"let type of packageTypes\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"type\"\n [value]=\"type\"\n />\n <span></span>\n <span>{{ type | translate | titlecase }}</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group\n *ngIf=\"packageAvailabilities.length\"\n formGroupName=\"availability\"\n >\n <label class=\"m-b-16\">\n {{ 'Availability`of package based on app state`' | translate }}\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"availability | translate\"\n *ngFor=\"let availability of packageAvailabilities\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"availability\"\n [value]=\"availability\"\n />\n <span></span>\n <span>{{ availability | translate }}</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group\n *ngIf=\"packageContents.length\"\n formGroupName=\"content\"\n >\n <label class=\"m-b-16\">\n {{ 'Content' | translate }}\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"content | translate\"\n *ngFor=\"let content of packageContents\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"content\"\n [value]=\"content\"\n />\n <span></span>\n <span>{{ content | translate }}</span>\n </label>\n </c8y-form-group>\n <c8y-form-group formGroupName=\"custom\">\n <ng-content></ng-content>\n </c8y-form-group>\n </div>\n <div class=\"p-16 d-flex gap-8 separator-top\">\n <button\n class=\"btn btn-default btn-sm flex-grow\"\n title=\"{{ 'Reset' | translate }}\"\n type=\"button\"\n (click)=\"reset(); filtersDropdown.isOpen = false\"\n >\n {{ 'Reset' | translate }}\n </button>\n\n <button\n class=\"btn btn-primary btn-sm flex-grow\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyPredefinedFilters(); filtersDropdown.isOpen = false\"\n [disabled]=\"formGroup.pristine && formGroup.untouched\"\n >\n {{ 'Apply' | translate }}\n </button>\n </div>\n </div>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: FilterInputComponent, selector: "c8y-filter", inputs: ["icon", "filterTerm"], outputs: ["onSearch"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: NgPlural, selector: "[ngPlural]", inputs: ["ngPlural"] }, { kind: "directive", type: NgPluralCase, selector: "[ngPluralCase]" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ListFiltersComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-list-filters', imports: [
FormsModule,
ReactiveFormsModule,
FilterInputComponent,
NgIf,
BsDropdownDirective,
CdkTrapFocus,
BsDropdownToggleDirective,
NgPlural,
NgPluralCase,
NgFor,
IconDirective,
BsDropdownMenuDirective,
FormGroupComponent,
RequiredInputPlaceholderDirective,
C8yTranslatePipe,
TitleCasePipe
], template: "<form\n class=\"d-flex a-i-center\"\n [formGroup]=\"formGroup\"\n>\n <c8y-filter\n [filterTerm]=\"currentFilterTerm\"\n (onSearch)=\"setFilterTerm($event)\"\n ></c8y-filter>\n\n <div\n class=\"dropdown m-l-8 m-r-4\"\n *ngIf=\"predefinedFiltersVisible\"\n dropdown\n (isOpenChange)=\"toggleDropdown($event)\"\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group d-inline-flex\">\n <button\n class=\"btn-clean form-control p-l-8 p-r-16 a-s-stretch d-flex a-i-center gap-4 w-auto flex-no-shrink\"\n title=\"{{ 'Filters' | translate }}\"\n dropdownToggle\n >\n <span [ngPlural]=\"chips.length\">\n <ng-template ngPluralCase=\"=0\">\n {{ 'Filters' | translate }}\n </ng-template>\n <ng-template ngPluralCase=\"=1\">\n <span class=\"badge badge-info\">{{ chips.length }}</span>\n {{ 'Filter' | translate }}\n </ng-template>\n <ng-template ngPluralCase=\"other\">\n <span class=\"badge badge-info\">{{ chips.length }}</span>\n {{ 'Filters' | translate }}\n </ng-template>\n </span>\n <span class=\"caret\"></span>\n </button>\n <div\n class=\"input-group-addon p-t-0 p-b-0 d-flex gap-4 a-i-center inner-scroll\"\n *ngIf=\"chips.length > 0\"\n >\n <ng-container *ngFor=\"let chip of chips\">\n <span\n class=\"tag tag--info chip\"\n *ngIf=\"chip[1].value\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"deselectChip(chip)\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n {{ chip[0] | translate | titlecase }}\n </span>\n </ng-container>\n </div>\n </div>\n <div\n class=\"dropdown-menu\"\n *dropdownMenu\n >\n <div class=\"dropdown-form\">\n <c8y-form-group\n *ngIf=\"packageTypes.length\"\n formGroupName=\"type\"\n >\n <label class=\"m-b-16\">\n {{ 'Type' | translate }}\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"type | translate\"\n *ngFor=\"let type of packageTypes\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"type\"\n [value]=\"type\"\n />\n <span></span>\n <span>{{ type | translate | titlecase }}</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group\n *ngIf=\"packageAvailabilities.length\"\n formGroupName=\"availability\"\n >\n <label class=\"m-b-16\">\n {{ 'Availability`of package based on app state`' | translate }}\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"availability | translate\"\n *ngFor=\"let availability of packageAvailabilities\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"availability\"\n [value]=\"availability\"\n />\n <span></span>\n <span>{{ availability | translate }}</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group\n *ngIf=\"packageContents.length\"\n formGroupName=\"content\"\n >\n <label class=\"m-b-16\">\n {{ 'Content' | translate }}\n </label>\n <label\n class=\"c8y-checkbox\"\n [title]=\"content | translate\"\n *ngFor=\"let content of packageContents\"\n >\n <input\n type=\"checkbox\"\n [formControlName]=\"content\"\n [value]=\"content\"\n />\n <span></span>\n <span>{{ content | translate }}</span>\n </label>\n </c8y-form-group>\n <c8y-form-group formGroupName=\"custom\">\n <ng-content></ng-content>\n </c8y-form-group>\n </div>\n <div class=\"p-16 d-flex gap-8 separator-top\">\n <button\n class=\"btn btn-default btn-sm flex-grow\"\n title=\"{{ 'Reset' | translate }}\"\n type=\"button\"\n (click)=\"reset(); filtersDropdown.isOpen = false\"\n >\n {{ 'Reset' | translate }}\n </button>\n\n <button\n class=\"btn btn-primary btn-sm flex-grow\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyPredefinedFilters(); filtersDropdown.isOpen = false\"\n [disabled]=\"formGroup.pristine && formGroup.untouched\"\n >\n {{ 'Apply' | translate }}\n </button>\n </div>\n </div>\n </div>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: i1.AppStateService }, { type: i3.TranslateService }, { type: i1.GainsightService }, { type: i4.Router }, { type: i4.ActivatedRoute }], propDecorators: { packageTypes: [{
type: Input
}], packageAvailabilities: [{
type: Input
}], packageContents: [{
type: Input
}], filterPipeChange: [{
type: Output
}], filterInput: [{
type: ViewChild,
args: [FilterInputComponent]
}] } });
class ArchivedFilterComponent {
constructor(filterComponent) {
this.filterComponent = filterComponent;
this.filterLabel = gettext('Show archived packages');
this.archivedFormControl = new FormControl();
this.filterComponent.formGroup.controls.custom.addControl(this.filterLabel, this.archivedFormControl);
filterComponent.customFilters.set(this.filterLabel, (appOrPlugin, enabled) => {
return (appOrPlugin.filterProps.type !== PackageType.ARCHIVED ||
(appOrPlugin.filterProps.type === PackageType.ARCHIVED && enabled));
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ArchivedFilterComponent, deps: [{ token: ListFiltersComponent }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ArchivedFilterComponent, isStandalone: true, selector: "c8y-archived-filter", ngImport: i0, template: "<label class=\"c8y-switch\">\n <input\n type=\"checkbox\"\n checked=\"checked\"\n [formControl]=\"archivedFormControl\"\n />\n <span></span>\n {{ 'Show archived packages' | translate }}\n</label>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ArchivedFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-archived-filter', imports: [FormsModule, ReactiveFormsModule, C8yTranslatePipe], template: "<label class=\"c8y-switch\">\n <input\n type=\"checkbox\"\n checked=\"checked\"\n [formControl]=\"archivedFormControl\"\n />\n <span></span>\n {{ 'Show archived packages' | translate }}\n</label>\n" }]
}], ctorParameters: () => [{ type: ListFiltersComponent }] });
/**
* Component to display the change log of a package.
* It fetches the change log file from the package's repository and displays it.
* It can fetch the change log for the latest version or a specific version if provided.
* It can also compare the change log with the previous version if provided and display the difference between the two versions.
*/
class PackageChangelogComponent {
constructor() {
this.selectedVersion = 'latest';
this.previousVersion = null;
this.isLoading = true;
this.pluginsService = inject(PluginsService);
}
async ngOnChanges() {
if (!this.package) {
this.changelog = '';
this.isLoading = false;
return;
}
this.isLoading = true;
this.changelog = await this.getChangeLog();
this.isLoading = false;
}
async getChangeLog() {
if (this.selectedVersion === this.previousVersion || !this.selectedVersion) {
return '';
}
const selectedVersionChangelogUrl = this.baseUrlForVersion(this.selectedVersion);
this.baseUrl = selectedVersionChangelogUrl;
if (!this.previousVersion) {
return await this.pluginsService.getChangelogFileContent(selectedVersionChangelogUrl);
}
const previousVersionChangelogUrl = this.baseUrlForVersion(this.previousVersion);
const changelogsPromises = [
this.pluginsService.getChangelogFileContent(previousVersionChangelogUrl),
this.pluginsService.getChangelogFileContent(selectedVersionChangelogUrl)
];
const [previousVersionChangelog, selectedVersionChangelog] = await Promise.all(changelogsPromises);
// Determine if this is a downgrade using semver comparison
let isDowngrade;
try {
isDowngrade = lt(this.selectedVersion, this.previousVersion);
}
catch {
isDowngrade = false;
}
if (isDowngrade) {
return previousVersionChangelog.replace(selectedVersionChangelog, '');
}
else {
return selectedVersionChangelog.replace(previousVersionChangelog, '');
}
}
baseUrlForVersion(version) {
return `/apps/${this.package.contextPath}@${version}/`;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PackageChangelogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: PackageChangelogComponent, isStandalone: true, selector: "c8y-contents-changelog", inputs: { package: "package", selectedVersion: "selectedVersion", previousVersion: "previousVersion" }, usesOnChanges: true, ngImport: i0, template: "<c8y-loading *ngIf=\"isLoading\"></c8y-loading>\n\n<ng-container *ngIf=\"!isLoading\">\n <div\n *ngIf=\"changelog\"\n class=\"markdown-content markdown-content--to-h3\"\n [innerHTML]=\"changelog | markdownToHtml: { baseUrl } | async\"\n ></div>\n\n <c8y-ui-empty-state\n [icon]=\"'user-manual'\"\n [title]=\"'No CHANGELOG.md found for package version' | translate\"\n [subtitle]=\"\n 'To view the contents of "CHANGELOG", add the file "CHANGELOG.md" to the package.'\n | translate\n \"\n *ngIf=\"!changelog\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</ng-container>\n", dependencies: [{ kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PackageChangelogComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-contents-changelog', standalone: true, imports: [
LoadingComponent,
MarkdownToHtmlPipe,
EmptyStateComponent,
AsyncPipe,
C8yTranslatePipe,
NgIf
], template: "<c8y-loading *ngIf=\"isLoading\"></c8y-loading>\n\n<ng-container *ngIf=\"!isLoading\">\n <div\n *ngIf=\"changelog\"\n class=\"markdown-content markdown-content--to-h3\"\n [innerHTML]=\"changelog | markdownToHtml: { baseUrl } | async\"\n ></div>\n\n <c8y-ui-empty-state\n [icon]=\"'user-manual'\"\n [title]=\"'No CHANGELOG.md found for package version' | translate\"\n [subtitle]=\"\n 'To view the contents of "CHANGELOG", add the file "CHANGELOG.md" to the package.'\n | translate\n \"\n *ngIf=\"!changelog\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n</ng-container>\n" }]
}], propDecorators: { package: [{
type: Input
}], selectedVersion: [{
type: Input
}], previousVersion: [{
type: Input
}] } });
class SharedEcosystemModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SharedEcosystemModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: SharedEcosystemModule, imports: [CoreModule,
BsDropdownModule,
TooltipModule,
A11yModule,
FormsModule,
PopoverModule,
ReactiveFormsModule,
IconSelectorModule,
PackageChangelogComponent,
AddApplicationComponent,
ApplicationPropertiesFormComponent,
UploadArchiveComponent,
ApplicationCardComponent,
DuplicateApplicationListComponent,
DuplicateApplicationPropertiesComponent,
DuplicateApplicationComponent,
PackageVersionSelectComponent,
TranslatePackageLabelPipe,
ListFiltersComponent,
ArchivedFilterComponent], exports: [AddApplicationComponent,
ApplicationPropertiesFormComponent,
UploadArchiveComponent,
ApplicationCardComponent,
DuplicateApplicationListComponent,
DuplicateApplicationPropertiesComponent,
DuplicateApplicationComponent,
PackageVersionSelectComponent,
TranslatePackageLabelPipe,
ListFiltersComponent,
ArchivedFilterComponent,
PackageChangelogComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SharedEcosystemModule, providers: [
hookWizard({
wizardId: EcosystemWizards.APPLICATION_UPLOAD,
component: DuplicateApplicationComponent,
name: 'Duplicate existing application',
c8yIcon: 'copy'
}),
hookWizard({
wizardId: 'duplicateApplication',
component: DuplicateApplicationComponent,
name: 'Duplicate existing application',
c8yIcon: 'copy'
})
], imports: [CoreModule,
BsDropdownModule,
TooltipModule,
A11yModule,
FormsModule,
PopoverModule,
ReactiveFormsModule,
IconSelectorModule,
PackageChangelogComponent,
AddApplicationComponent,
ApplicationPropertiesFormComponent,
UploadArchiveComponent,
ApplicationCardComponent,
DuplicateApplicationListComponent,
DuplicateApplicationPropertiesComponent,
DuplicateApplicationComponent,
PackageVersionSelectComponent,
ListFiltersComponent,
ArchivedFilterComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SharedEcosystemModule, decorators: [{
type: NgModule,
args: [{
imports: [
CoreModule,
BsDropdownModule,
TooltipModule,
A11yModule,
FormsModule,
PopoverModule,
ReactiveFormsModule,
IconSelectorModule,
PackageChangelogComponent,
AddApplicationComponent,
ApplicationPropertiesFormComponent,
UploadArchiveComponent,
ApplicationCardComponent,
DuplicateApplicationListComponent,
DuplicateApplicationPropertiesComponent,
DuplicateApplicationComponent,
PackageVersionSelectComponent,
TranslatePackageLabelPipe,
ListFiltersComponent,
ArchivedFilterComponent
],
exports: [
AddApplicationComponent,
ApplicationPropertiesFormComponent,
UploadArchiveComponent,
ApplicationCardComponent,
DuplicateApplicationListComponent,
DuplicateApplicationPropertiesComponent,
DuplicateApplicationComponent,
PackageVersionSelectComponent,
TranslatePackageLabelPipe,
ListFiltersComponent,
ArchivedFilterComponent,
PackageChangelogComponent
],
providers: [
hookWizard({
wizardId: EcosystemWizards.APPLICATION_UPLOAD,
component: DuplicateApplicationComponent,
name: 'Duplicate existing application',
c8yIcon: 'copy'
}),
hookWizard({
wizardId: 'duplicateApplication',
component: DuplicateApplicationComponent,
name: 'Duplicate existing application',
c8yIcon: 'copy'
})
]
}]
}] });
const defaultPackageTypes = [
PackageType.OFFICIAL,
PackageType.COMMUNITY
];
const defaultPackageAvailabilities = [
APP_STATE.SUBSCRIBED.label,
APP_STATE.UNPACKED.label,
APP_STATE.EXTERNAL.label,
APP_STATE.CUSTOM.label
];
const defaultPackageContents = [
APP_STATE.PACKAGE_BLUEPRINT.label,
APP_STATE.PACKAGE_PLUGIN.label
];
/**
* Generated bundle index. Do not edit.
*/
export { APP_STATE, AddApplicationComponent, ApplicationCardComponent, ApplicationPropertiesFormComponent, ArchivedFilterComponent, DuplicateApplicationComponent, DuplicateApplicationListComponent, DuplicateApplicationPropertiesComponent, ERROR_MESSAGES, ERROR_TYPE, EcosystemService, EcosystemWizards, ListFiltersComponent, PACKAGE_TYPE_LABELS, PRODUCT_EXPERIENCE_ECOSYSTEM, PackageAvailabilityService, PackageChangelogComponent, PackageVersionSelectComponent, SharedEcosystemModule, TranslatePackageLabelPipe, UploadArchiveComponent, defaultPackageAvailabilities, defaultPackageContents, defaultPackageTypes, packageProperties };
//# sourceMappingURL=c8y-ngx-components-ecosystem-shared.mjs.map