Skip to content
This repository has been archived by the owner on Nov 15, 2024. It is now read-only.

Commit

Permalink
updated as per previous comments
Browse files Browse the repository at this point in the history
  • Loading branch information
DarpanLalani committed Mar 15, 2022
1 parent aa2eeaa commit 08a4b16
Show file tree
Hide file tree
Showing 20 changed files with 569 additions and 33 deletions.
36 changes: 36 additions & 0 deletions builder/app-builder-upgrade/app-builder-upgrade.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 Software AG, Darmstadt, Germany and/or its licensors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export interface AppBuilderConfig {
versionInfo: VersionInfo;
externalApps: ExternalApp[];
}

export interface VersionInfo {
updateAvailable?: string;
updateURL?: string;
fileName?: string;
contextPath?: string;
}

export interface ExternalApp {
appName?: string;
binaryLink?: string;
fileName?: string;
contextPath?:string;
}
290 changes: 290 additions & 0 deletions builder/app-builder-upgrade/app-builder-upgrade.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
/*
* Copyright (c) 2019 Software AG, Darmstadt, Germany and/or its licensors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { DOCUMENT } from "@angular/common";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Inject, Injectable, isDevMode, Renderer2, RendererFactory2 } from "@angular/core";
import { AlertService, AppStateService } from "@c8y/ngx-components";
import { ApplicationService, UserService } from "@c8y/ngx-components/api";
import { AppBuilderExternalAssetsService } from "app-builder-external-assets";
import { SettingsService } from "../settings/settings.service";
import { AlertMessageModalComponent } from "../utils/alert-message-modal/alert-message-modal.component";
import { ProgressIndicatorModalComponent } from "../utils/progress-indicator-modal/progress-indicator-modal.component";
import { ProgressIndicatorService } from "../utils/progress-indicator-modal/progress-indicator.service";
import * as JSZip from "jszip";
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
import { Observable } from "rxjs";
import { AppBuilderConfig } from "./app-builder-upgrade.model";
import { version } from '../../package.json';
import { AppIdService } from "../app-id.service";

@Injectable({ providedIn: 'root' })
export class AppBuilderUpgradeService {
private renderer: Renderer2;
private progressModal: BsModalRef;
private GATEWAY_URL_GitHubAsset = '';
private GATEWAY_URL_GitHubAPI = '';
private GATEWAY_URL_Labcase = '';
private appBuilderConfigPath = '/appbuilderConfig/app-builder-config.json';
private devBranchPath = "?ref=development";
private appBuilderConfigModel: AppBuilderConfig;
private applicationsList = [];
userHasAdminRights: boolean;

public appVersion: string = version;
public newVersion: boolean = false;
public errorReported = false;


constructor(private http: HttpClient, public rendererFactory: RendererFactory2, @Inject(DOCUMENT) private _document: Document,
private modalService: BsModalService, private progressIndicatorService: ProgressIndicatorService,
private appService: ApplicationService, private externalService: AppBuilderExternalAssetsService,
private settingService: SettingsService, private userService: UserService, private appStateService: AppStateService,
appIdService: AppIdService, private alertService: AlertService) {
this.GATEWAY_URL_GitHubAsset = this.externalService.getURL('GITHUB', 'gatewayURL_GitHubAsset');
this.GATEWAY_URL_GitHubAPI = this.externalService.getURL('GITHUB', 'gatewayURL_Github');
this.GATEWAY_URL_Labcase = this.externalService.getURL('DBCATALOG', 'gatewayURL');
appIdService.appIdDelayedUntilAfterLogin$.subscribe(() => {
this.userHasAdminRights = userService.hasRole(appStateService.currentUser.value, "ROLE_APPLICATION_MANAGEMENT_ADMIN")
});
}

private readonly HTTP_HEADERS = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
})
};

async loadUpgradeBanner() {
const isAppBuilderUpgradeNotification = await this.settingService.isAppUpgradeNotification();
if (this.userHasAdminRights && isAppBuilderUpgradeNotification) {
await this.getAppBuilderConfig();
}
}

private createAndRenderBanner() {
this.renderer = this.rendererFactory.createRenderer(null, null);
const appbuilderUpgradeBanner = this.renderer.createElement("div");
const textElement = this.renderer.createText(`Application Builder ${this.appBuilderConfigModel.versionInfo.updateAvailable} available.`);
const textElementUpdateLink = this.renderer.createText(' Update now!');
const updateLink = this.renderer.createElement("a");
this.renderer.addClass(appbuilderUpgradeBanner, 'app-builder-upgrade-banner');
this.renderer.appendChild(appbuilderUpgradeBanner, textElement);
this.renderer.appendChild(updateLink, textElementUpdateLink);
this.renderer.appendChild(appbuilderUpgradeBanner, updateLink);
this.renderer.appendChild(this._document.body, appbuilderUpgradeBanner);
const clicklistener = this.renderer.listen(updateLink, 'click', (evt) => this.initiateUpgrade(evt));
}

private async getAppBuilderConfig() {
await this.fetchAppBuilderConfig().subscribe(async appBuilderConfig => {
this.appBuilderConfigModel = appBuilderConfig;
let isValidContextPath = false;
if (this.appBuilderConfigModel && this.appBuilderConfigModel.versionInfo) {
if (this.appVersion >= this.appBuilderConfigModel.versionInfo.updateAvailable) {
this.newVersion = false;
} else {
this.newVersion = true;
}
const appList = await this.getApplicationList();
const currentTenantId = this.settingService.getTenantName();
const appBuilderApp = appList.find(
app => this.appBuilderConfigModel.versionInfo.contextPath &&
app.contextPath === this.appBuilderConfigModel.versionInfo.contextPath);
const appBuilderTenantId = (appBuilderApp && appBuilderApp.owner && appBuilderApp.owner.tenant ? appBuilderApp.owner.tenant.id : undefined);
if(appBuilderApp && currentTenantId === appBuilderTenantId) { isValidContextPath = true;}
/* else {
this.alertService.warning("Unable to detect valid Application Builder",
"Context Path of installed version of application builder is not matching with server");
} */
}
if (this.newVersion && isValidContextPath) {
this.createAndRenderBanner();
}
});
}

private initiateUpgrade(event: any) {
const currentHost = window.location.host.split(':')[0];
if (currentHost === 'localhost' || currentHost === '127.0.0.1' || isDevMode()) {
this.alertService.warning("Application Updation isn't supported when running Application Builder on localhost or in development mode.");
return;
}
const alertMessage = {
title: 'Installation Confirmation',
description: `You are about to upgrade Application Builder.
Do you want to proceed?`,
type: 'info',
alertType: 'confirm', //info|confirm
confirmPrimary: true //confirm Button is primary
}
const upgradeAppBuilderDialogRef = this.alertModalDialog(alertMessage);
upgradeAppBuilderDialogRef.content.event.subscribe(async data => {
if (data && data.isConfirm) {
this.showProgressModalDialog('Updating Application Builder...');
const updateURL = this.appBuilderConfigModel.versionInfo.updateURL;
const fileName = updateURL.replace(/^.*[\\\/]/, '');
await this.downloadAndInstall(updateURL, fileName, true, 'UPGRADE');
this.progressModal.hide();
if(!this.errorReported) {
const postUpdationMsg = {
title: 'Updation Completed',
description: 'Application Builder is successfully updated.',
type: 'info',
alertType: 'info' //info|confirm
};
const postUpdationDialogRef = this.alertModalDialog(postUpdationMsg);
await postUpdationDialogRef.content.event.subscribe(data => {
window.location.reload();
});
}
}
});
}

showProgressModalDialog(message: string): void {
this.progressModal = this.modalService.show(ProgressIndicatorModalComponent, { class: 'c8y-wizard', initialState: { message } });
}

hideProgressModalDialog() {
this.progressModal.hide();
}

alertModalDialog(message: any): BsModalRef {
return this.modalService.show(AlertMessageModalComponent, { class: 'c8y-wizard', initialState: { message } });
}


async downloadAndInstall(binaryLocation: string, fileName: string, isGithub: boolean, installationType: 'INSTALL' | 'UPGRADE' | 'ANY') {
this.errorReported = false;
if (!binaryLocation) {
console.error('Missing link to download binary');
this.alertService.danger("Missing link to download binary");
this.errorReported = true;
return;
}
this.progressIndicatorService.setProgress(20);
const data: ArrayBuffer = await this.downloadBinary(binaryLocation, isGithub);
const blob = new Blob([data], {
type: 'application/zip'
});
const binaryFile = new File([blob], fileName, { type: "'application/zip'" })
this.progressIndicatorService.setProgress(30);
let appC8yJson;
try {
this.progressIndicatorService.setProgress(40);
const binaryZip = await JSZip.loadAsync(binaryFile);
appC8yJson = JSON.parse(await binaryZip.file('cumulocity.json').async("text"));
if (appC8yJson.contextPath === undefined) {
this.alertService.danger("Unable to download new version.");
this.errorReported = true;
throw Error("Application's Context Path not found.");
}
} catch (e) {
console.log(e);
this.alertService.danger("Unable to download new version.");
this.errorReported = true;
throw Error("Not a Binary");
}

await this.upgradeApp(binaryFile, appC8yJson, installationType);
}

private async upgradeApp(binaryFile: any, appC8yJson: any, installationType: any) {
if (installationType !== "INSTALL") {
const appList = await this.getApplicationList();
const appName = appList.find(app => app.contextPath === appC8yJson.contextPath);
const appTenantId = (appName && appName.owner && appName.owner.tenant ? appName.owner.tenant.id : undefined);
if(appName && (String(appName.availability) === 'PRIVATE' || appTenantId === this.settingService.getTenantName())) {
this.progressIndicatorService.setProgress(50);
// Upload the binary
const appBinary = (await this.appService.binary(appName).upload(binaryFile)).data;
// Update the app
this.progressIndicatorService.setProgress(70);
await this.appService.update({
...appC8yJson,
id: appName.id,
activeVersionId: appBinary.id.toString()
});
if (window && window['aptrinsic']) {
window['aptrinsic']('track', 'gp_application_updated', {
"appBuilder": appName.name,
"tenantId": this.settingService.getTenantName(),
});
}
} else {
this.alertService.danger("Unable to upgrade application!", "Please verify that you are owner of the tenant and not using subscribed application.")
this.errorReported = true;
return;
}

} else if(installationType !== "UPGRADE") {
// Create the custom App
this.progressIndicatorService.setProgress(50);
const custmApp = (await this.appService.create({
...appC8yJson,
resourcesUrl: "/",
type: "HOSTED"
} as any)).data;

// Upload the binary
const appBinary = (await this.appService.binary(custmApp).upload(binaryFile)).data;
this.progressIndicatorService.setProgress(70);
// Update the app
await this.appService.update({
id: custmApp.id,
activeVersionId: appBinary.id.toString()
});
if (window && window['aptrinsic']) {
window['aptrinsic']('track', 'gp_application_installed', {
"widgetName": custmApp.name,
"tenantId": this.settingService.getTenantName(),
});
}
} else {
this.alertService.danger("Unable to Install/Upgrade your application.", "Please verify that you are not using subscribed application for upgrade. If you are using subscribed application, then try again from Management/Enterprise tenant.")
this.errorReported = true;
return;
}
this.progressIndicatorService.setProgress(80);
}

private downloadBinary(binaryId: string, isGithub: boolean): Promise<ArrayBuffer> {
let url = `${this.GATEWAY_URL_GitHubAsset}${binaryId}`;
if (!isGithub) {
url = `${this.GATEWAY_URL_Labcase}${binaryId}`
}
return this.http.get(url, {
responseType: 'arraybuffer'
}).toPromise();
}

fetchAppBuilderConfig(): Observable<AppBuilderConfig> {
if(isDevMode()){
return this.http.get<AppBuilderConfig>(`${this.GATEWAY_URL_GitHubAPI}${this.appBuilderConfigPath}${this.devBranchPath}`, this.HTTP_HEADERS);
}
return this.http.get<AppBuilderConfig>(`${this.GATEWAY_URL_GitHubAPI}${this.appBuilderConfigPath}`, this.HTTP_HEADERS);
}

async getApplicationList() {
if(this.applicationsList && this.applicationsList.length > 0) { return this.applicationsList; }
this.applicationsList = (await this.appService.list({ pageSize: 2000, withTotalPages: true }) as any).data ;
return this.applicationsList;
}
}
4 changes: 3 additions & 1 deletion builder/builder.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { SettingsService } from './settings/settings.service';
import { HomeComponent } from './home/home.component';
import { WidgetCatalogModule } from './widget-catalog/widget-catalog.module';
import { AlertMessageModalModule } from "./utils/alert-message-modal/alert-message-modal.module";
import { AppBuilderUpgradeService } from "./app-builder-upgrade/app-builder-upgrade.service";
@NgModule({
imports: [
ApplicationModule,
Expand Down Expand Up @@ -133,7 +134,7 @@ import { AlertMessageModalModule } from "./utils/alert-message-modal/alert-messa
export class BuilderModule {
private renderer: Renderer2;
constructor(appStateService: AppStateService, loginService: LoginService, simSvc: SimulatorCommunicationService,
appIdService: AppIdService, private settingService: SettingsService,
appIdService: AppIdService, private settingService: SettingsService, private appBuilderUpgradeService: AppBuilderUpgradeService,
rendererFactory: RendererFactory2, @Inject(DOCUMENT) private _document: Document, private tenantService: TenantService) {
// Pass the app state to the worker from the main thread (Initially and every time it changes)
appStateService.currentUser.subscribe(async (user) => {
Expand Down Expand Up @@ -181,6 +182,7 @@ export class BuilderModule {
this.renderer = rendererFactory.createRenderer(null, null);
this.registerAndTrackAnalyticsProvider(true);
}
this.appBuilderUpgradeService.loadUpgradeBanner();
}
});

Expand Down
10 changes: 8 additions & 2 deletions builder/home/home.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,23 @@ <h2 class="text-light home-margin-top-20" translate="">Welcome to Application Bu
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<div class="row card home-wdiget-card" >
<div class="card-header">
<!-- <div class="card-header">
Upgrade Application Builder
</div> -->
<div class="card-header">
Install Demo Catalog
</div>
<div class="card-block">
<div class="row home-widget-body">
<div class="col-xs-4 col-sm-2 col-md-2">
<img (click)="openLink('source')" src="./images/c8y-app-upgrade.png">
</div>
<div class="col-xs-8 col-sm-10 col-md-10">
Find the latest release of Application Builder on <a [href]="getURL('source')" target="_blank" >Software AG Open Source</a>.
Discover how you can leverage other demos for your Iot use case <a href="#" (click)="installDemoCatalog()" target="_self" >Install Demo Catalog</a>
</div>
<!-- <div class="col-xs-8 col-sm-10 col-md-10">
Find the latest release of Application Builder on <a [href]="getURL('source')" target="_blank" >Software AG Open Source</a>.
</div> -->
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 08a4b16

Please sign in to comment.