From 3cf22d82431b8b7ecd3f0a2aa3320bcefe677ad0 Mon Sep 17 00:00:00 2001 From: Hanno Bunjes Date: Sat, 15 Oct 2022 10:51:54 +0200 Subject: [PATCH 1/4] custom-app build integration --- .github/workflows/npm-build.yml | 4 + imxweb/angular.json | 157 +++++++++++++++++++++++++++++++- 2 files changed, 160 insertions(+), 1 deletion(-) diff --git a/.github/workflows/npm-build.yml b/.github/workflows/npm-build.yml index c95dd14c8..591b3cd7a 100644 --- a/.github/workflows/npm-build.yml +++ b/.github/workflows/npm-build.yml @@ -107,3 +107,7 @@ jobs: working-directory: ./imxweb run: npm run build qer-app-pwdportal + - name: Build custom-app + working-directory: ./imxweb + run: npm run build custom-app + diff --git a/imxweb/angular.json b/imxweb/angular.json index bbec60e33..78ec08314 100644 --- a/imxweb/angular.json +++ b/imxweb/angular.json @@ -1634,7 +1634,162 @@ } } } - } + }, + "custom-app": { + "projectType": "application", + "root": "projects/custom-app", + "sourceRoot": "projects/custom-app/src", + "prefix": "imx", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/custom-app", + "index": "projects/custom-app/src/index.html", + "main": "projects/custom-app/src/main.ts", + "polyfills": "projects/custom-app/src/polyfills.ts", + "tsConfig": "projects/custom-app/tsconfig.app.json", + "aot": true, + "assets": [ + "projects/custom-app/src/appconfig.json", + "projects/custom-app/src/assets", + { + "glob": "**/*", + "input": "./html", + "output": "./html" + }, + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + }, + { + "glob": "**/*", + "input": "./node_modules/@elemental-ui/core/assets", + "output": "./assets" + }, + { + "glob": "**/*", + "input": "./node_modules/systemjs-plugin-babel/", + "output": "./systemjs-plugin-babel" + } + ], + "styles": [ + "projects/custom-app/src/styles.scss", + "projects/qbm/src/lib/styles/imx-page-title.scss", + "shared/assets/styles.scss", + "shared/assets/variables.scss" + ], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + }, + "scripts": [ + "node_modules/systemjs/dist/system.src.js", + "node_modules/systemjs-plugin-babel/plugin-babel.js", + "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js" + ] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "projects/custom-app/src/environments/environment.ts", + "with": "projects/custom-app/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "20mb", + "maximumError": "40mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ] + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "es5": { + "tsConfig": "./projects/custom-app/tsconfig-es5.app.json" + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "custom-app:build" + }, + "configurations": { + "production": { + "browserTarget": "custom-app:build:production" + }, + "development": { + "browserTarget": "custom-app:build:development" + }, + "es5": { + "browserTarget": "custom-app:build:es5" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "custom-app:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/custom-app/src/test.ts", + "polyfills": "projects/custom-app/src/polyfills.ts", + "tsConfig": "projects/custom-app/tsconfig.spec.json", + "karmaConfig": "projects/custom-app/karma.conf.js", + "assets": ["projects/custom-app/src/appconfig.json", "projects/custom-app/src/assets"], + "styles": ["projects/custom-app/src/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + }, + "scripts": ["node_modules/systemjs/dist/system.src.js"] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/custom-app/tsconfig.app.json", "projects/custom-app/tsconfig.spec.json"], + "exclude": ["**/node_modules/**"] + } + } + } }, "defaultProject": "qer-app-portal", "cli": { From 47127e92362d17eee94a996550763fb07b0321aa Mon Sep 17 00:00:00 2001 From: Hanno Bunjes Date: Sat, 15 Oct 2022 10:52:43 +0200 Subject: [PATCH 2/4] add custom-app --- imxweb/projects/custom-app/.browserslistrc | 12 ++ imxweb/projects/custom-app/Build.proj | 15 +++ imxweb/projects/custom-app/karma.conf.js | 47 +++++++ imxweb/projects/custom-app/package.json | 5 + imxweb/projects/custom-app/readme.md | 52 ++++++++ .../custom-app/src/app/app-routing.module.ts | 66 ++++++++++ .../custom-app/src/app/app.component.html | 10 ++ .../custom-app/src/app/app.component.scss | 14 +++ .../custom-app/src/app/app.component.ts | 109 ++++++++++++++++ .../projects/custom-app/src/app/app.module.ts | 119 ++++++++++++++++++ .../custom-app/src/app/app.service.ts | 104 +++++++++++++++ .../custom-app/src/app/start.component.html | 18 +++ .../custom-app/src/app/start.component.ts | 34 +++++ imxweb/projects/custom-app/src/appconfig.json | 11 ++ .../src/environments/environment.prod.ts | 32 +++++ .../src/environments/environment.ts | 45 +++++++ imxweb/projects/custom-app/src/index.html | 15 +++ imxweb/projects/custom-app/src/main.ts | 38 ++++++ imxweb/projects/custom-app/src/polyfills.ts | 81 ++++++++++++ imxweb/projects/custom-app/src/styles.scss | 13 ++ .../projects/custom-app/tsconfig-es5.app.json | 6 + imxweb/projects/custom-app/tsconfig.app.json | 14 +++ imxweb/projects/custom-app/tsconfig.spec.json | 18 +++ imxweb/projects/custom-app/tslint.json | 17 +++ 24 files changed, 895 insertions(+) create mode 100644 imxweb/projects/custom-app/.browserslistrc create mode 100644 imxweb/projects/custom-app/Build.proj create mode 100644 imxweb/projects/custom-app/karma.conf.js create mode 100644 imxweb/projects/custom-app/package.json create mode 100644 imxweb/projects/custom-app/readme.md create mode 100644 imxweb/projects/custom-app/src/app/app-routing.module.ts create mode 100644 imxweb/projects/custom-app/src/app/app.component.html create mode 100644 imxweb/projects/custom-app/src/app/app.component.scss create mode 100644 imxweb/projects/custom-app/src/app/app.component.ts create mode 100644 imxweb/projects/custom-app/src/app/app.module.ts create mode 100644 imxweb/projects/custom-app/src/app/app.service.ts create mode 100644 imxweb/projects/custom-app/src/app/start.component.html create mode 100644 imxweb/projects/custom-app/src/app/start.component.ts create mode 100644 imxweb/projects/custom-app/src/appconfig.json create mode 100644 imxweb/projects/custom-app/src/environments/environment.prod.ts create mode 100644 imxweb/projects/custom-app/src/environments/environment.ts create mode 100644 imxweb/projects/custom-app/src/index.html create mode 100644 imxweb/projects/custom-app/src/main.ts create mode 100644 imxweb/projects/custom-app/src/polyfills.ts create mode 100644 imxweb/projects/custom-app/src/styles.scss create mode 100644 imxweb/projects/custom-app/tsconfig-es5.app.json create mode 100644 imxweb/projects/custom-app/tsconfig.app.json create mode 100644 imxweb/projects/custom-app/tsconfig.spec.json create mode 100644 imxweb/projects/custom-app/tslint.json diff --git a/imxweb/projects/custom-app/.browserslistrc b/imxweb/projects/custom-app/.browserslistrc new file mode 100644 index 000000000..80848532e --- /dev/null +++ b/imxweb/projects/custom-app/.browserslistrc @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/imxweb/projects/custom-app/Build.proj b/imxweb/projects/custom-app/Build.proj new file mode 100644 index 000000000..dd3484c3f --- /dev/null +++ b/imxweb/projects/custom-app/Build.proj @@ -0,0 +1,15 @@ + + + imxweb\projects\custom-app + no + custom-app + + + + + + + + + + diff --git a/imxweb/projects/custom-app/karma.conf.js b/imxweb/projects/custom-app/karma.conf.js new file mode 100644 index 000000000..b454fa9d7 --- /dev/null +++ b/imxweb/projects/custom-app/karma.conf.js @@ -0,0 +1,47 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-ie-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + require('karma-junit-reporter') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, 'results'), + reports: ['html', 'lcovonly', 'text-summary'], + thresholds: { + statements: 0, + branches: 0, + functions: 0, + lines: 0, + }, + fixWebpackSourcePaths: true + }, + junitReporter: { + outputDir: require('path').join(__dirname, 'results'), + }, + reporters: ['progress', 'coverage-istanbul', 'junit'], + port: 9876, + captureTimeout: 210000, + browserDisconnectTolerance: 3, + browserDisconnectTimeout : 210000, + browserNoActivityTimeout : 210000, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/imxweb/projects/custom-app/package.json b/imxweb/projects/custom-app/package.json new file mode 100644 index 000000000..7d1519614 --- /dev/null +++ b/imxweb/projects/custom-app/package.json @@ -0,0 +1,5 @@ +{ + "name": "custom-app", + "version": "9.0.0", + "private": true +} \ No newline at end of file diff --git a/imxweb/projects/custom-app/readme.md b/imxweb/projects/custom-app/readme.md new file mode 100644 index 000000000..d10e72d05 --- /dev/null +++ b/imxweb/projects/custom-app/readme.md @@ -0,0 +1,52 @@ +# custom-app + +This package contains the base functionality that you can use as a starting point to build a custom application. + +This package has a dependency on the `qbm` package, so make sure that package is compiled using the `ng build qbm` command. + +## App service + +The [`app.service.ts`](./src/app/app.service.ts) service is responsible for initialization of the app: + +- It configures the API client with the correct URL for the API Server. +- It configures the translation service with the browser's preferred language setting. +- It shows a splash screen while the application is being loaded. + +## Routing + +The [`appconfig.json`](./src/appconfig.json) file sets two default routes: + +``` json + "routeConfig": { + "login": "", + "start": "start" + } +``` + +The `login` route is the route called when there is no authenticated session. +The `start` route is the route called when there is an authenticated session. + +These routes are defined in the [`app-routing.module.ts`](./src/app/app-routing.module.ts) file. Add any routes for your application here. + +## Login component + +The app re-uses the default [login component](../qbm/src/lib/login/login.component.ts) defined in the `qbm` library. + +This app uses a session on the `portal` API project; you can change this setting in the [`environment.ts`](./src/environments/environment.ts) file. + +## Page layout + +The [`app.component.html`](./src/app/app.component.html) file defines the basic layout of the page. This template makes use of the following base components: + +- `` shows the mast head including: + - the product logo, + - the name of the authenticated user, + - an option to log out, + - and the "about" dialog. +- `` is the container where error messages are shown. + +The app component also shows a splash screen while the application is being initialized. + +## Debugging + +Run `ng serve custom-app` to serve the custom app. diff --git a/imxweb/projects/custom-app/src/app/app-routing.module.ts b/imxweb/projects/custom-app/src/app/app-routing.module.ts new file mode 100644 index 000000000..6a35e0258 --- /dev/null +++ b/imxweb/projects/custom-app/src/app/app-routing.module.ts @@ -0,0 +1,66 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { InjectionToken, NgModule } from '@angular/core'; +import { Routes, RouterModule, ActivatedRouteSnapshot } from '@angular/router'; +import { AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm'; +import { StartComponent } from './start.component'; + +const externalUrlProvider = new InjectionToken('externalUrlRedirectResolver'); + +const routes: Routes = [ + { + path: '', + component: LoginComponent, + canActivate: [AuthenticationGuardService], + resolve: [RouteGuardService] + }, + { + path: 'start', + component: StartComponent, + canActivate: [RouteGuardService], + resolve: [RouteGuardService] + }, + { path: '**', redirectTo: 'start' } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes, { useHash: true, relativeLinkResolution: 'legacy' })], + exports: [RouterModule], + providers: [ + { + provide: externalUrlProvider, + useValue: (route: ActivatedRouteSnapshot) => { + const externalUrl = route.paramMap.get('externalUrl'); + if (externalUrl && externalUrl.toLocaleLowerCase() !== 'undefined') { + window.open(externalUrl, '_self'); + } + } + }, + ], +}) + +export class AppRoutingModule { } diff --git a/imxweb/projects/custom-app/src/app/app.component.html b/imxweb/projects/custom-app/src/app/app.component.html new file mode 100644 index 000000000..ae5e1f2c2 --- /dev/null +++ b/imxweb/projects/custom-app/src/app/app.component.html @@ -0,0 +1,10 @@ +
+ + + + +
+ + +
+
\ No newline at end of file diff --git a/imxweb/projects/custom-app/src/app/app.component.scss b/imxweb/projects/custom-app/src/app/app.component.scss new file mode 100644 index 000000000..c2f8a644d --- /dev/null +++ b/imxweb/projects/custom-app/src/app/app.component.scss @@ -0,0 +1,14 @@ +.page { + display: flex; + flex-direction: column; + overflow: hidden; + height: 100vh; +} + +.pageContent { + margin: 20px 30px; + display: flex; + flex-direction: column; + overflow: hidden; + height: inherit; +} diff --git a/imxweb/projects/custom-app/src/app/app.component.ts b/imxweb/projects/custom-app/src/app/app.component.ts new file mode 100644 index 000000000..8cb3f8abc --- /dev/null +++ b/imxweb/projects/custom-app/src/app/app.component.ts @@ -0,0 +1,109 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, RouterEvent } from '@angular/router'; +import { Subscription } from 'rxjs'; + +import { AuthenticationService, ISessionState, SplashService } from 'qbm'; + +@Component({ + selector: 'imx-root', + styleUrls: ['./app.component.scss'], + templateUrl: './app.component.html' +}) +export class AppComponent implements OnInit, OnDestroy { + public isLoggedIn = false; + public hideUserMessage = false; + public showPageContent = true; + + private readonly subscriptions: Subscription[] = []; + + constructor( + private readonly authentication: AuthenticationService, + private readonly router: Router, + private readonly splash: SplashService, + ) { + this.subscriptions.push( + this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { + + if (sessionState.hasErrorState) { + // Needs to close here when there is an error on sessionState + this.splash.close(); + } + + if (sessionState.IsLoggedOut) { + this.showPageContent = false; + } + + this.isLoggedIn = sessionState.IsLoggedIn; + if (this.isLoggedIn) { + // Close the splash screen that opened in app service initialisation + // Needs to close here when session is already logged in. + this.splash.close(); + } + }) + ); + + this.setupRouter(); + } + + public async ngOnInit(): Promise { + // Fetch the session state from the server; this will either + // - go to the login page if the user is not logged in + // - or go to the start page if the user is already logged in + this.authentication.update(); + } + + public ngOnDestroy(): void { + this.subscriptions.forEach(subscription => subscription.unsubscribe()); + } + + private setupRouter(): void { + this.router.events.subscribe(((event: RouterEvent) => { + if (event instanceof NavigationStart) { + this.hideUserMessage = true; + if (this.isLoggedIn && event.url === '/') { + // show the splash screen when the user logs out! + this.splash.init({ applicationName: 'Custom App' }); + } + } + + if (event instanceof NavigationCancel) { + this.hideUserMessage = false; + } + + if (event instanceof NavigationEnd) { + this.hideUserMessage = false; + this.showPageContent = true; + } + + if (event instanceof NavigationError) { + this.hideUserMessage = false; + } + })); + } +} diff --git a/imxweb/projects/custom-app/src/app/app.module.ts b/imxweb/projects/custom-app/src/app/app.module.ts new file mode 100644 index 000000000..4e87e2af0 --- /dev/null +++ b/imxweb/projects/custom-app/src/app/app.module.ts @@ -0,0 +1,119 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { HttpClientModule } from '@angular/common/http'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NgModule, APP_INITIALIZER, ErrorHandler } from '@angular/core'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatCardModule } from '@angular/material/card'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule, TranslateLoader, MissingTranslationHandler, TranslateService } from '@ngx-translate/core'; + +import { + ImxTranslateLoader, + ImxMissingTranslationHandler, + MastHeadModule, + UserMessageModule, + GlobalErrorHandler, + Paginator, + LdsReplacePipe, + QbmModule, + AuthenticationModule, + MenuModule, + CustomThemeModule +} from 'qbm'; +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { AppService } from './app.service'; +import { MatPaginatorIntl } from '@angular/material/paginator'; +import { environment } from '../environments/environment'; +import appConfigJson from '../appconfig.json'; +import { StartComponent } from './start.component'; + + +@NgModule({ + declarations: [ + AppComponent, + StartComponent + ], + imports: [ + AppRoutingModule, + AuthenticationModule, + BrowserAnimationsModule, + BrowserModule, + EuiCoreModule, + EuiMaterialModule, + MatButtonModule, + MatCardModule, + MatIconModule, + MatProgressSpinnerModule, + HttpClientModule, + LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }), + MastHeadModule, + MenuModule, + QbmModule, + CustomThemeModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: ImxTranslateLoader + }, + missingTranslationHandler: { + provide: MissingTranslationHandler, + useClass: ImxMissingTranslationHandler + } + }), + UserMessageModule + ], + providers: [ + { provide: 'environment', useValue: environment }, + { provide: 'appConfigJson', useValue: appConfigJson }, + { + provide: APP_INITIALIZER, + useFactory: AppService.init, + deps: [AppService], + multi: true + }, + { + provide: ErrorHandler, + useClass: GlobalErrorHandler + }, + { + provide: MatPaginatorIntl, + useFactory: Paginator.Create, + deps: [ + TranslateService, + LdsReplacePipe + ] + } + ], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/imxweb/projects/custom-app/src/app/app.service.ts b/imxweb/projects/custom-app/src/app/app.service.ts new file mode 100644 index 000000000..01827b2cb --- /dev/null +++ b/imxweb/projects/custom-app/src/app/app.service.ts @@ -0,0 +1,104 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { TranslateService } from '@ngx-translate/core'; + +import { Globals } from 'imx-qbm-dbts'; +import { + AppConfigService, + AuthenticationService, + imx_SessionService, + ImxTranslationProviderService, + SplashService +} from 'qbm'; +import { environment } from '../environments/environment'; +import { TypedClient } from 'imx-api-qbm'; + +@Injectable({ + providedIn: 'root' +}) +export class AppService { + constructor( + private readonly config: AppConfigService, + private readonly translateService: TranslateService, + private readonly session: imx_SessionService, + private readonly translationProvider: ImxTranslationProviderService, + private readonly title: Title, + private readonly authentication: AuthenticationService, + private readonly splash: SplashService, + ) { } + + public async init(): Promise { + this.showSplash(); + await this.config.init(environment.clientUrl); + + this.translateService.addLangs(this.config.Config.Translation.Langs); + const browserCulture = this.translateService.getBrowserCultureLang(); + this.translateService.setDefaultLang(browserCulture); + await this.translateService.use(browserCulture).toPromise(); + + // If the session defines another culture, update the translation provider + this.authentication.onSessionResponse.subscribe(sessionState => this.translationProvider.init(sessionState?.culture)); + + this.translateService.onLangChange.subscribe(() => { + this.setTitle(); + }); + + this.setTitle(); + + this.session.TypedClient = new TypedClient(this.config.v2client, this.translationProvider); + } + + private async setTitle(): Promise { + const imxConfig = await this.config.getImxConfig(); + const name = imxConfig.ProductName || Globals.QIM_ProductNameFull; + const title = `${name} ${this.config.Config.Title}`; + this.title.setTitle(title); + + await this.updateSplash(title); + } + + public static init(app: AppService): () => Promise { + return () => + new Promise(async (resolve: any) => { + await app.init(); + resolve(); + }); + } + + private showSplash(): void { + // open splash screen with fix values + this.splash.init({ applicationName: 'Custom App' }); + } + + private async updateSplash(title: string): Promise { + // update the splash screen and use translated texts and the title from the imxconfig + const loadingMsg = await this.translateService.get('#LDS#Loading...').toPromise(); + this.splash.update({ applicationName: title, message: loadingMsg }); + } +} diff --git a/imxweb/projects/custom-app/src/app/start.component.html b/imxweb/projects/custom-app/src/app/start.component.html new file mode 100644 index 000000000..c8aa9c84e --- /dev/null +++ b/imxweb/projects/custom-app/src/app/start.component.html @@ -0,0 +1,18 @@ + + +
Hello world!
+ + + + + +
+
There are {{ totalCount }} identities in the address book. The first identities are:
+
    +
  • + {{ person.GetEntity().GetDisplay() }} +
  • +
+
+ +
\ No newline at end of file diff --git a/imxweb/projects/custom-app/src/app/start.component.ts b/imxweb/projects/custom-app/src/app/start.component.ts new file mode 100644 index 000000000..beac2ffa6 --- /dev/null +++ b/imxweb/projects/custom-app/src/app/start.component.ts @@ -0,0 +1,34 @@ +import { Component } from "@angular/core"; +import { PortalPersonAll } from "imx-api-qer"; +import { QerApiService } from "qer"; + +@Component({ + templateUrl: './start.component.html' +}) +export class StartComponent { + + constructor(private qerApi: QerApiService) { } + + persons: PortalPersonAll[] = []; + totalCount = 0; + busy = true; + + async loadPersonData() { + + try { + this.busy = true; + + // load some address book data from the API Server. + // This call uses the default parameters, which will + // return the first 20 identities in the database. + const personData = await this.qerApi.typedClient.PortalPersonAll.Get(); + + this.persons = personData.Data; + this.totalCount = personData.totalCount; + } finally { + // Even if the call fails, reset the busy flag + this.busy = false; + } + } + +} \ No newline at end of file diff --git a/imxweb/projects/custom-app/src/appconfig.json b/imxweb/projects/custom-app/src/appconfig.json new file mode 100644 index 000000000..908bd4c3b --- /dev/null +++ b/imxweb/projects/custom-app/src/appconfig.json @@ -0,0 +1,11 @@ +{ + "Title": "Custom App", + "WebAppIndex": "portal", + "Translation": { + "Langs": ["en-US", "de-DE", "de", "en"] + }, + "routeConfig": { + "login": "", + "start": "start" + } +} diff --git a/imxweb/projects/custom-app/src/environments/environment.prod.ts b/imxweb/projects/custom-app/src/environments/environment.prod.ts new file mode 100644 index 000000000..e81d320c3 --- /dev/null +++ b/imxweb/projects/custom-app/src/environments/environment.prod.ts @@ -0,0 +1,32 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +export const environment = { + production: true, + clientUrl: '', + appName: 'custom-app', + appVersion: '1.0.0' +}; diff --git a/imxweb/projects/custom-app/src/environments/environment.ts b/imxweb/projects/custom-app/src/environments/environment.ts new file mode 100644 index 000000000..ccd664291 --- /dev/null +++ b/imxweb/projects/custom-app/src/environments/environment.ts @@ -0,0 +1,45 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, + clientUrl: 'http://localhost:8182', + appName: 'custom-app', + appVersion: '1.0.0' +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/imxweb/projects/custom-app/src/index.html b/imxweb/projects/custom-app/src/index.html new file mode 100644 index 000000000..4b785ba4e --- /dev/null +++ b/imxweb/projects/custom-app/src/index.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/imxweb/projects/custom-app/src/main.ts b/imxweb/projects/custom-app/src/main.ts new file mode 100644 index 000000000..0d6be56bc --- /dev/null +++ b/imxweb/projects/custom-app/src/main.ts @@ -0,0 +1,38 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/imxweb/projects/custom-app/src/polyfills.ts b/imxweb/projects/custom-app/src/polyfills.ts new file mode 100644 index 000000000..02b0a61d5 --- /dev/null +++ b/imxweb/projects/custom-app/src/polyfills.ts @@ -0,0 +1,81 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2022 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** +* BROWSER POLYFILLS +*/ + + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** IE10 and IE11 requires the following for the Reflect API. */ +// import 'core-js/es6/reflect'; + + +/** Evergreen browsers require these. */ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +import 'core-js/es7/reflect'; + +// Used for support array.includes +import 'core-js/es7/array'; + +/** + * Required to support Web Animations `@angular/platform-browser/animations`. + * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation + */ +import 'web-animations-js'; + + + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ +import 'whatwg-fetch'; +import 'url-polyfill'; diff --git a/imxweb/projects/custom-app/src/styles.scss b/imxweb/projects/custom-app/src/styles.scss new file mode 100644 index 000000000..b2784f9a9 --- /dev/null +++ b/imxweb/projects/custom-app/src/styles.scss @@ -0,0 +1,13 @@ +/* You can add global styles to this file, and also import other style files */ + +@use '@angular/material' as mat; + +$material_icons_font_path: "~node_modules/@elemental-ui/core/assets/MaterialIcons"; +$cadence_font_path: "~node_modules/@elemental-ui/core/assets/Cadence"; +$source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans_Pro"; + +@import '@elemental-ui/core/src/styles/core.scss'; + +.imx-dialog-panel-class > .mat-dialog-container { + background-color: mat.get-color-from-palette($asher-gray-palette, 200); +} diff --git a/imxweb/projects/custom-app/tsconfig-es5.app.json b/imxweb/projects/custom-app/tsconfig-es5.app.json new file mode 100644 index 000000000..4cba6b869 --- /dev/null +++ b/imxweb/projects/custom-app/tsconfig-es5.app.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.app.json", + "compilerOptions": { + "target": "es5" + } +} \ No newline at end of file diff --git a/imxweb/projects/custom-app/tsconfig.app.json b/imxweb/projects/custom-app/tsconfig.app.json new file mode 100644 index 000000000..d69a5c154 --- /dev/null +++ b/imxweb/projects/custom-app/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts", + "src/polyfills.ts" + ], + "include": [ + "projects/custom-app/src/**/*.d.ts" + ] +} diff --git a/imxweb/projects/custom-app/tsconfig.spec.json b/imxweb/projects/custom-app/tsconfig.spec.json new file mode 100644 index 000000000..a809b0a66 --- /dev/null +++ b/imxweb/projects/custom-app/tsconfig.spec.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/imxweb/projects/custom-app/tslint.json b/imxweb/projects/custom-app/tslint.json new file mode 100644 index 000000000..8bab15f53 --- /dev/null +++ b/imxweb/projects/custom-app/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "imx", + "camelCase" + ], + "component-selector": [ + true, + "element", + "imx", + "kebab-case" + ] + } +} From dc6948d391f6da9c77a4d015d6acaa7e97fb4c5d Mon Sep 17 00:00:00 2001 From: Hanno Bunjes Date: Sat, 15 Oct 2022 10:58:34 +0200 Subject: [PATCH 3/4] fix angular.json, add readme --- README.md | 4 ++++ imxweb/angular.json | 1 + 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index 22cc0f7d3..f52b775f6 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ ## Change log +### October 15, 2022 + +- There is a new application in the workspace called `custom-app`. This application is a template that provides the basic building blocks (such as Material integration, session handling, login, and the API client configuration) can be used as a starting point for building new applications. See [`readme.md`](./imxweb/projects/custom-app/readme.md) for more information. + ### September 27, 2022 - The repository has been updated with the source code for the Identity Manager 9.1 release in the `v91` branch. diff --git a/imxweb/angular.json b/imxweb/angular.json index 78ec08314..35f5b319d 100644 --- a/imxweb/angular.json +++ b/imxweb/angular.json @@ -1790,6 +1790,7 @@ } } } + } }, "defaultProject": "qer-app-portal", "cli": { From 897addb3029cd87c6e6f57148284774ad3360d70 Mon Sep 17 00:00:00 2001 From: Hanno Bunjes Date: Sat, 15 Oct 2022 15:19:33 +0200 Subject: [PATCH 4/4] fix --- README.md | 4 +--- imxweb/projects/custom-app/src/app/app.module.ts | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f52b775f6..05950b409 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ For more information about each project, see the `readme.md` files in each proje |`qer-app-portal`|Portal|Angular app|`qbm`, `qer`| |`qer-app-operationssupport`|Operations Support Portal|Angular app|`qbm`, `qer`| |`qer-app-pwdportal`|Password Reset Portal|Angular app|`qbm`, `qer`| -|`arc-app-certaccess`|Starling CertAccess|Angular app|various| +|`custom-app`|Template for custom applications|Angular app|`qbm`| ## Installing node.js @@ -95,8 +95,6 @@ When changing the code of a _library_, you will need to build and deploy customi When changing the code of a _plugin library_, you will need to build and deploy customized versions of the plugin library itself, and all plugin libraries depending on it. For example, changing `tsb` will require that you also compile `aad` and `o3t` because these plugins include `tsb`. -_Note_: Starling CertAccess currently does not support hosting custom HTML5 apps. - ### Debugging Running and debugging web applications is possible using the default tools of the Angular CLI toolchain. For example, you can use `ng serve qer-app-portal` to debug the Portal app. diff --git a/imxweb/projects/custom-app/src/app/app.module.ts b/imxweb/projects/custom-app/src/app/app.module.ts index 4e87e2af0..09b6019ea 100644 --- a/imxweb/projects/custom-app/src/app/app.module.ts +++ b/imxweb/projects/custom-app/src/app/app.module.ts @@ -46,8 +46,7 @@ import { LdsReplacePipe, QbmModule, AuthenticationModule, - MenuModule, - CustomThemeModule + MenuModule } from 'qbm'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @@ -79,7 +78,6 @@ import { StartComponent } from './start.component'; MastHeadModule, MenuModule, QbmModule, - CustomThemeModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader,