Merged PR 1134: Merge ISA 2.0 into Develop

Merge ISA 2.0 into Develop

Related work items: #1098, #2592, #2630, #2633, #2635, #2639, #2706, #2707, #2813, #2818, #2825, #2843, #2846, #2847, #2848, #2851, #2852, #2853, #2897, #2900
This commit is contained in:
Nino Righi
2022-03-28 10:12:12 +00:00
committed by Andreas Schickinger
parent 91b3f44c1e
commit f0cc76f180
998 changed files with 25284 additions and 12719 deletions

1
.gitignore vendored
View File

@@ -34,6 +34,7 @@ speed-measure-plugin.json
/.sass-cache
/connect.lock
/coverage
/testresults
/libpeerconnection.log
npm-debug.log
yarn-error.log

View File

@@ -3,7 +3,6 @@
"johnpapa.angular2",
"esbenp.prettier-vscode",
"angular.ng-template",
"ms-vscode.vscode-typescript-tslint-plugin",
"eg2.vscode-npm-script"
]
}
}

View File

@@ -1,5 +1,5 @@
#stage 1
FROM node:14 as node
FROM node:14 as base
ARG IS_PRODUCTION=false
ARG SEMVERSION=1.0.0
ARG BuildUniqueID
@@ -11,9 +11,19 @@ RUN npm version ${SEMVERSION}
RUN npm install --always-auth=false
RUN if [ "${IS_PRODUCTION}" = "true" ] ; then npm run-script build-prod ; else npm run-script build ; fi
# stage 2
FROM nginx:alpine
# stage final
FROM nginx:alpine as publish
ARG BuildUniqueID
LABEL build.uniqueid="${BuildUniqueID:-1}"
COPY --from=node /app/dist/sales /usr/share/nginx/html
COPY --from=node /app/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=base /app/dist/isa-app /usr/share/nginx/html
COPY --from=base /app/nginx.conf /etc/nginx/conf.d/default.conf
# stage npm test
FROM base as test
ARG BuildUniqueID
LABEL build.uniqueid="${BuildUniqueID:-1}"
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -q -O /tmp/chrome.deb && apt update && apt install -y /tmp/chrome.deb
# ignore exitcode, sonst gibts keinen container
RUN npm test || true
ENTRYPOINT [ "/bin/sleep", "60000" ]

View File

@@ -10,7 +10,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "libs/ui/tsconfig.lib.json",
"project": "libs/ui/ng-package.json"
@@ -55,7 +55,6 @@
"options": {
"aot": true,
"outputPath": "dist/sales",
"outputHashing": "all",
"index": "apps/sales/src/index.html",
"main": "apps/sales/src/main.ts",
"polyfills": "apps/sales/src/polyfills.ts",
@@ -123,10 +122,7 @@
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "sales:build",
"customWebpackConfig": {
"path": "apps/sales/webpack.config.js"
}
"browserTarget": "sales:build"
},
"configurations": {
"test": {
@@ -224,7 +220,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "libs/sso/tsconfig.lib.json",
"project": "libs/sso/ng-package.json"
@@ -264,7 +260,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/availability/tsconfig.lib.json",
"project": "apps/swagger/availability/ng-package.json"
@@ -304,7 +300,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/checkout/tsconfig.lib.json",
"project": "apps/swagger/checkout/ng-package.json"
@@ -344,7 +340,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/crm/tsconfig.lib.json",
"project": "apps/swagger/crm/ng-package.json"
@@ -384,7 +380,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/isa/tsconfig.lib.json",
"project": "apps/swagger/isa/ng-package.json"
@@ -424,7 +420,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/oms/tsconfig.lib.json",
"project": "apps/swagger/oms/ng-package.json"
@@ -464,7 +460,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/print/tsconfig.lib.json",
"project": "apps/swagger/print/ng-package.json"
@@ -504,7 +500,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/cat/tsconfig.lib.json",
"project": "apps/swagger/cat/ng-package.json"
@@ -544,7 +540,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/swagger/eis/tsconfig.lib.json",
"project": "apps/swagger/eis/ng-package.json"
@@ -584,7 +580,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/native-container/tsconfig.lib.json",
"project": "apps/native-container/ng-package.json"
@@ -624,7 +620,7 @@
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/isa/remission/tsconfig.lib.json",
"project": "apps/isa/remission/ng-package.json"
@@ -664,7 +660,7 @@
"prefix": "crm",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/domain/crm/tsconfig.lib.json",
"project": "apps/domain/crm/ng-package.json"
@@ -704,7 +700,7 @@
"prefix": "checkout",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "apps/domain/checkout/tsconfig.lib.json",
"project": "apps/domain/checkout/ng-package.json"
@@ -3297,6 +3293,487 @@
}
}
},
"isa-app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
},
"@schematics/angular:application": {
"strict": true
}
},
"root": "apps/isa-app",
"sourceRoot": "apps/isa-app/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/isa-app",
"index": "apps/isa-app/src/index.html",
"main": "apps/isa-app/src/main.ts",
"polyfills": "apps/isa-app/src/polyfills.ts",
"tsConfig": "apps/isa-app/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"apps/isa-app/src/favicon.ico",
"apps/isa-app/src/assets",
"apps/isa-app/src/config",
"apps/isa-app/src/silent-refresh.html",
"apps/isa-app/src/manifest.webmanifest"
],
"styles": [
"apps/isa-app/src/styles.scss"
],
"scripts": [],
"serviceWorker": true,
"ngswConfigPath": "apps/isa-app/ngsw-config.json"
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "apps/isa-app/src/environments/environment.ts",
"with": "apps/isa-app/src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "isa-app:build:production"
},
"development": {
"browserTarget": "isa-app:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "isa-app:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/isa-app/src/test.ts",
"polyfills": "apps/isa-app/src/polyfills.ts",
"tsConfig": "apps/isa-app/tsconfig.spec.json",
"karmaConfig": "apps/isa-app/karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"apps/isa-app/src/favicon.ico",
"apps/isa-app/src/assets",
"apps/isa-app/src/manifest.webmanifest"
],
"styles": [
"apps/isa-app/src/styles.scss"
],
"scripts": []
}
}
}
},
"@core/config": {
"projectType": "library",
"root": "apps/core/config",
"sourceRoot": "apps/core/config/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/core/config/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/core/config/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/core/config/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/core/config/src/test.ts",
"tsConfig": "apps/core/config/tsconfig.spec.json",
"karmaConfig": "apps/core/config/karma.conf.js"
}
}
}
},
"@core/auth": {
"projectType": "library",
"root": "apps/core/auth",
"sourceRoot": "apps/core/auth/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/core/auth/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/core/auth/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/core/auth/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/core/auth/src/test.ts",
"tsConfig": "apps/core/auth/tsconfig.spec.json",
"karmaConfig": "apps/core/auth/karma.conf.js"
}
}
}
},
"@shell/footer": {
"projectType": "library",
"root": "apps/shell/footer",
"sourceRoot": "apps/shell/footer/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/shell/footer/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/shell/footer/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/shell/footer/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/shell/footer/src/test.ts",
"tsConfig": "apps/shell/footer/tsconfig.spec.json",
"karmaConfig": "apps/shell/footer/karma.conf.js"
}
}
}
},
"@shell/process": {
"projectType": "library",
"root": "apps/shell/process",
"sourceRoot": "apps/shell/process/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/shell/process/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/shell/process/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/shell/process/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/shell/process/src/test.ts",
"tsConfig": "apps/shell/process/tsconfig.spec.json",
"karmaConfig": "apps/shell/process/karma.conf.js"
}
}
}
},
"@page/dashboard": {
"projectType": "library",
"root": "apps/page/dashboard",
"sourceRoot": "apps/page/dashboard/src",
"prefix": "page",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/page/dashboard/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/page/dashboard/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/page/dashboard/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/page/dashboard/src/test.ts",
"tsConfig": "apps/page/dashboard/tsconfig.spec.json",
"karmaConfig": "apps/page/dashboard/karma.conf.js"
}
}
}
},
"@domain/isa": {
"projectType": "library",
"root": "apps/domain/isa",
"sourceRoot": "apps/domain/isa/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/domain/isa/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/domain/isa/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/domain/isa/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/domain/isa/src/test.ts",
"tsConfig": "apps/domain/isa/tsconfig.spec.json",
"karmaConfig": "apps/domain/isa/karma.conf.js"
}
}
}
},
"@core/logger": {
"projectType": "library",
"root": "apps/core/logger",
"sourceRoot": "apps/core/logger/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/core/logger/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/core/logger/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/core/logger/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/core/logger/src/test.ts",
"tsConfig": "apps/core/logger/tsconfig.spec.json",
"karmaConfig": "apps/core/logger/karma.conf.js"
}
}
}
},
"@shell/filter-overlay": {
"projectType": "library",
"root": "apps/shell/filter-overlay",
"sourceRoot": "apps/shell/filter-overlay/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/shell/filter-overlay/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/shell/filter-overlay/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/shell/filter-overlay/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/shell/filter-overlay/src/test.ts",
"tsConfig": "apps/shell/filter-overlay/tsconfig.spec.json",
"karmaConfig": "apps/shell/filter-overlay/karma.conf.js"
}
}
}
},
"@store/search-component-store": {
"projectType": "library",
"root": "apps/store/search-component-store",
"sourceRoot": "apps/store/search-component-store/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/store/search-component-store/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/store/search-component-store/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/store/search-component-store/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/store/search-component-store/src/test.ts",
"tsConfig": "apps/store/search-component-store/tsconfig.spec.json",
"karmaConfig": "apps/store/search-component-store/karma.conf.js"
}
}
}
},
"@domain/remission": {
"projectType": "library",
"root": "apps/domain/remission",
"sourceRoot": "apps/domain/remission/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/domain/remission/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/domain/remission/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/domain/remission/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/domain/remission/src/test.ts",
"tsConfig": "apps/domain/remission/tsconfig.spec.json",
"karmaConfig": "apps/domain/remission/karma.conf.js"
}
}
}
},
"@page/remission": {
"projectType": "library",
"root": "apps/page/remission",
"sourceRoot": "apps/page/remission/src",
"prefix": "page",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/page/remission/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/page/remission/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/page/remission/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/page/remission/src/test.ts",
"tsConfig": "apps/page/remission/tsconfig.spec.json",
"karmaConfig": "apps/page/remission/karma.conf.js"
}
}
}
},
"@modal/notifications": {
"projectType": "library",
"root": "apps/modal/notifications",
"sourceRoot": "apps/modal/notifications/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/modal/notifications/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/modal/notifications/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/modal/notifications/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/modal/notifications/src/test.ts",
"tsConfig": "apps/modal/notifications/tsconfig.spec.json",
"karmaConfig": "apps/modal/notifications/karma.conf.js"
}
}
}
},
"@ui/branch-dropdown": {
"projectType": "library",
"root": "apps/ui/branch-dropdown",
@@ -3336,7 +3813,38 @@
}
}
}
},
"@adapter/scan": {
"projectType": "library",
"root": "apps/adapter/scan",
"sourceRoot": "apps/adapter/scan/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/adapter/scan/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/adapter/scan/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/adapter/scan/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/adapter/scan/src/test.ts",
"tsConfig": "apps/adapter/scan/tsconfig.spec.json",
"karmaConfig": "apps/adapter/scan/karma.conf.js"
}
}
}
}
},
"defaultProject": "sales"
},
"defaultProject": "isa-app"
}

View File

@@ -0,0 +1,25 @@
# Scan
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
## Code scaffolding
Run `ng generate component component-name --project scan` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project scan`.
> Note: Don't forget to add `--project scan` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build scan` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build scan`, go to the dist folder `cd dist/scan` and run `npm publish`.
## Running unit tests
Run `ng test scan` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@@ -0,0 +1,41 @@
// 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-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, '../../../coverage/adapter/scan'),
subdir: '.',
reporters: [{ type: 'html' }, { type: 'text-summary' }],
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true,
});
};

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/adapter/scan",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "@adapter/scan",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^12.2.0",
"@angular/core": "^12.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,46 @@
import { Injectable } from '@angular/core';
import { PromptModalData, UiModalService, UiPromptModalComponent } from '@ui/modal';
import { Observable } from 'rxjs';
import { ScanAdapter } from './scan-adapter';
@Injectable()
export class DevScanAdapter implements ScanAdapter {
constructor(private _modal: UiModalService) {}
getName(): string {
return 'Dev Scanner';
}
isPrimary(): boolean {
return true;
}
isReady(): boolean {
return true;
}
scan(): Observable<string> {
return new Observable((observer) => {
const modalRef = this._modal.open({
content: UiPromptModalComponent,
title: 'Anmeldung',
data: {
message: 'Bitte geben Sie Ihren Login Code ein',
placeholder: 'Login Code',
confirmText: 'weiter',
cancelText: 'Anmeldung mit Benutzername und Passwort',
} as PromptModalData,
});
const sub = modalRef.afterClosed$.subscribe((result) => {
observer.next(result.data);
observer.complete();
});
return () => {
modalRef.close();
sub.unsubscribe();
};
});
}
}

View File

@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { NativeContainerService } from 'native-container';
import { Observable } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
import { ScanAdapter } from './scan-adapter';
@Injectable()
export class NativeScanAdapter implements ScanAdapter {
constructor(private readonly nativeContainerService: NativeContainerService) {}
getName(): string {
return 'Native Scanner';
}
isPrimary(): boolean {
return true;
}
isReady(): boolean {
return this.nativeContainerService.isUiWebview().isNative;
}
scan(): Observable<string> {
return this.nativeContainerService.openScanner('remissionContainer').pipe(
filter((result) => result.status === 'SUCCESS'),
map((result) => result.data),
take(1)
);
}
}

View File

@@ -0,0 +1,11 @@
import { Observable } from 'rxjs';
export interface ScanAdapter {
getName(): string;
isPrimary(): boolean;
isReady(): boolean;
scan(): Observable<string>;
}

View File

@@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { DevScanAdapter } from './dev.scan-adapter';
import { NativeScanAdapter } from './native.scan-adapter';
import { SCAN_ADAPTER } from './tokens';
@NgModule({})
export class ScanAdapterModule {
static forRoot(dev?: boolean) {
return {
ngModule: ScanAdapterModule,
providers: [{ provide: SCAN_ADAPTER, useClass: dev ? DevScanAdapter : NativeScanAdapter, multi: true }],
};
}
}

View File

@@ -0,0 +1,26 @@
import { Inject, Injectable } from '@angular/core';
import { ScanAdapter } from './scan-adapter';
import { SCAN_ADAPTER } from './tokens';
@Injectable({
providedIn: 'root',
})
export class ScanAdapterService {
constructor(@Inject(SCAN_ADAPTER) private readonly scanAdapters: ScanAdapter[]) {}
scanners() {
return this.scanAdapters.filter((adapter) => adapter.isReady());
}
scanner() {
return this.scanners().find((scanner) => scanner.isPrimary()) || this.scanners().find(() => true);
}
scan() {
const primaryScanner = this.scanner();
if (primaryScanner) {
return primaryScanner.scan();
}
return null;
}
}

View File

@@ -0,0 +1,4 @@
import { InjectionToken } from '@angular/core';
import { ScanAdapter } from './scan-adapter';
export const SCAN_ADAPTER = new InjectionToken<ScanAdapter>('SCAN_ADAPTER');

View File

@@ -0,0 +1,6 @@
/*
* Public API Surface of scan
*/
export * from './lib/scan.service';
export * from './lib/scan.module';

View File

@@ -0,0 +1,25 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp
): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } });
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,20 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -0,0 +1,17 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@@ -1,11 +1,12 @@
import { Inject, Injectable } from '@angular/core';
import { Config } from '@core/config';
import { CDN_PRODUCT_IMAGE } from './tokens';
@Injectable({
providedIn: 'root',
})
export class ProductImageService {
constructor(@Inject(CDN_PRODUCT_IMAGE) private imageUrl: string) {}
constructor(private readonly _config: Config) {}
getImageUrl({
imageId,
@@ -18,6 +19,6 @@ export class ProductImageService {
height?: number;
showDummy?: boolean;
}): string {
return `${this.imageUrl}/${imageId}_${width}x${height}.jpg?showDummy=${showDummy}`;
return `${this._config.get('@cdn/product-image.url')}/${imageId}_${width}x${height}.jpg?showDummy=${showDummy}`;
}
}

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -1,5 +1,8 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const customLaunchers = require('../../../karma/custom-launchers');
const junitReporter = require('../../../karma/junit-reporter')('core-application');
const coverageReporter = require('../../../karma/coverage-reporter')('core-application');
module.exports = function (config) {
config.set({
@@ -9,23 +12,31 @@ module.exports = function (config) {
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('karma-coverage'),
require('karma-junit-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../../coverage/core/application'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
coverageReporter,
junitReporter,
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers,
singleRun: false,
restartOnFileChange: true,
});

View File

@@ -11,8 +11,13 @@ import { ApplicationService } from './application.service';
export class CoreApplicationModule {
static forRoot(): ModuleWithProviders<CoreApplicationModule> {
return {
ngModule: CoreApplicationModule,
providers: [ApplicationService, StoreModule.forFeature('core-application', applicationReducer).providers],
ngModule: RootCoreApplicationModule,
};
}
}
@NgModule({
imports: [StoreModule.forFeature('core-application', applicationReducer)],
providers: [ApplicationService],
})
export class RootCoreApplicationModule {}

View File

@@ -1,32 +1,232 @@
import { TestBed } from '@angular/core/testing';
import { isObservable } from 'rxjs';
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator';
import { Store } from '@ngrx/store';
import { Observable, of } from 'rxjs';
import { first } from 'rxjs/operators';
import { ApplicationProcess } from './defs';
import { ApplicationService } from './application.service';
import * as actions from './store/application.actions';
describe('ApplicationService', () => {
let service: ApplicationService;
let spectator: SpectatorService<ApplicationService>;
let store: SpyObject<Store>;
const createService = createServiceFactory({
service: ApplicationService,
mocks: [Store],
});
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ApplicationService],
});
service = TestBed.inject(ApplicationService);
spectator = createService({});
store = spectator.inject(Store);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
describe('activatedProcessId', () => {
it('should return the processId', () => {
service.setActivatedProcessId(100);
expect(service.activatedProcessId).toEqual(100);
});
expect(spectator.service).toBeTruthy();
});
describe('activatedProcessId$', () => {
it('should return an observable', () => {
expect(isObservable(service.activatedProcessId$)).toBeTruthy();
expect(spectator.service.activatedProcessId$).toBeInstanceOf(Observable);
});
});
describe('activatedProcessId', () => {
it('should return the process id as a number', () => {
spyOnProperty(spectator.service['activatedProcessIdSubject'] as any, 'value').and.returnValue(2);
expect(spectator.service.activatedProcessId).toBe(2);
});
});
describe('getProcesses$()', () => {
it('should call select on store and return all selected processes', async () => {
const processes: ApplicationProcess[] = [
{ id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
{ id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
];
store.select.and.returnValue(of(processes));
const result = await spectator.service.getProcesses$().pipe(first()).toPromise();
expect(result).toEqual(processes);
expect(store.select).toHaveBeenCalled();
});
it('should call select on store and return all section customer processes', async () => {
const processes: ApplicationProcess[] = [
{ id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
{ id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
];
store.select.and.returnValue(of(processes));
const result = await spectator.service.getProcesses$('customer').pipe(first()).toPromise();
expect(result).toEqual([processes[0]]);
expect(store.select).toHaveBeenCalled();
});
it('should call select on store and return all section branch processes', async () => {
const processes: ApplicationProcess[] = [
{ id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } },
{ id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' },
];
store.select.and.returnValue(of(processes));
const result = await spectator.service.getProcesses$('branch').pipe(first()).toPromise();
expect(result).toEqual([processes[1]]);
expect(store.select).toHaveBeenCalled();
});
});
describe('getProcessById$()', () => {
it('should return the process by id', async () => {
const processes: ApplicationProcess[] = [
{ id: 1, name: 'Vorgang 1', section: 'customer' },
{ id: 2, name: 'Vorgang 2', section: 'customer' },
];
spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
const process = await spectator.service.getProcessById$(1).toPromise();
expect(process.id).toBe(1);
});
});
describe('getSection$()', () => {
it('should return the selected section branch', async () => {
const section = 'branch';
store.select.and.returnValue(of(section));
const result = await spectator.service.getSection$().pipe(first()).toPromise();
expect(result).toEqual(section);
expect(store.select).toHaveBeenCalled();
});
});
describe('getActivatedProcessId$', () => {
it('should return the current selected activated process id', async () => {
const activatedProcessId = 2;
store.select.and.returnValue(of({ id: activatedProcessId }));
const result = await spectator.service.getActivatedProcessId$().pipe(first()).toPromise();
expect(result).toEqual(activatedProcessId);
expect(store.select).toHaveBeenCalled();
});
});
describe('activateProcess()', () => {
it('should dispatch action setActivatedProcess with argument activatedProcessId and action type', () => {
const activatedProcessId = 2;
spectator.service.activateProcess(activatedProcessId);
expect(store.dispatch).toHaveBeenCalledWith({ activatedProcessId, type: actions.setActivatedProcess.type });
});
});
describe('removeProcess()', () => {
it('should dispatch action removeProcess with argument processId and action type', () => {
const processId = 2;
spectator.service.removeProcess(processId);
expect(store.dispatch).toHaveBeenCalledWith({ processId, type: actions.removeProcess.type });
});
});
describe('createProcess()', () => {
it('should dispatch action addProcess with process', async () => {
const process: ApplicationProcess = {
id: 1,
name: 'Vorgang 1',
section: 'customer',
type: 'cart',
};
const timestamp = 100;
spyOn(spectator.service as any, '_createTimestamp').and.returnValue(timestamp);
spyOn(spectator.service, 'getProcessById$').and.returnValue(of(undefined));
await spectator.service.createProcess(process);
expect(store.dispatch).toHaveBeenCalledWith({
type: actions.addProcess.type,
process: {
...process,
activated: 0,
created: timestamp,
},
});
});
it('should throw an error if the process id is already existing', async () => {
const process: ApplicationProcess = {
id: 1,
name: 'Vorgang 1',
section: 'customer',
type: 'cart',
};
spyOn(spectator.service, 'getProcessById$').and.returnValue(of(process));
await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id existiert bereits');
});
it('should throw an error if the process id is not a number', async () => {
const process: ApplicationProcess = {
id: undefined,
name: 'Vorgang 1',
section: 'customer',
type: 'cart',
};
spyOn(spectator.service, 'getProcessById$').and.returnValue(of({ id: 5, name: 'Vorgang 2', section: 'customer' }));
await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id nicht gesetzt');
});
});
describe('patchProcess', () => {
it('should dispatch action patchProcess with changes', async () => {
const process: ApplicationProcess = {
id: 1,
name: 'Vorgang 1',
section: 'customer',
type: 'cart',
};
await spectator.service.patchProcess(process.id, process);
expect(store.dispatch).toHaveBeenCalledWith({
type: actions.patchProcess.type,
process: {
...process,
},
});
});
});
describe('setSection()', () => {
it('should dispatch action setSection with argument section and action type', () => {
const section = 'customer';
spectator.service.setSection(section);
expect(store.dispatch).toHaveBeenCalledWith({ section, type: actions.setSection.type });
});
});
describe('getLastActivatedProcessWithSectionAndType()', () => {
it('should return the last activated process by section and type', async () => {
const processes: ApplicationProcess[] = [
{ id: 1, name: 'Vorgang 1', section: 'customer', type: 'cart', activated: 100 },
{ id: 2, name: 'Vorgang 2', section: 'customer', type: 'cart', activated: 200 },
{ id: 3, name: 'Vorgang 3', section: 'customer', type: 'goodsOut', activated: 300 },
];
spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
expect(await spectator.service.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()).toBe(
processes[1]
);
});
});
describe('getLastActivatedProcessWithSection()', () => {
it('should return the last activated process by section', async () => {
const processes: ApplicationProcess[] = [
{ id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 },
{ id: 2, name: 'Vorgang 2', section: 'customer', activated: 200 },
{ id: 3, name: 'Vorgang 3', section: 'customer', activated: 300 },
];
spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes));
expect(await spectator.service.getLastActivatedProcessWithSection$('customer').pipe(first()).toPromise()).toBe(processes[2]);
});
});
describe('_createTimestamp', () => {
it('should return the current timestamp in ms', () => {
expect(spectator.service['_createTimestamp']()).toBeCloseTo(Date.now());
});
});
});

View File

@@ -1,37 +1,129 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { BehaviorSubject } from 'rxjs';
import { processRemoved, selectSection, setSection } from './store';
import { isBoolean, isNumber } from '@utils/common';
import { BehaviorSubject, Observable } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
import { ApplicationProcess } from './defs';
import {
removeProcess,
selectSection,
selectProcesses,
setSection,
addProcess,
setActivatedProcess,
selectActivatedProcess,
patchProcess,
patchProcessData,
} from './store';
@Injectable()
export class ApplicationService {
/** @deprecated */
private activatedProcessIdSubject = new BehaviorSubject<number>(undefined);
/** @deprecated */
get activatedProcessId() {
return this.activatedProcessIdSubject.value;
}
/** @deprecated */
get activatedProcessId$() {
return this.activatedProcessIdSubject.asObservable();
}
readonly section$ = this.store.select(selectSection);
constructor(private store: Store) {}
setActivatedProcessId(processId: number) {
if (this.activatedProcessId !== processId) {
this.activatedProcessIdSubject.next(processId);
}
getProcesses$(section?: 'customer' | 'branch') {
const processes$ = this.store.select(selectProcesses);
return processes$.pipe(map((processes) => processes.filter((process) => (section ? process.section === section : true))));
}
getProcessById$(processId: number): Observable<ApplicationProcess> {
return this.getProcesses$().pipe(map((processes) => processes.find((process) => process.id === processId)));
}
getSection$() {
return this.store.select(selectSection);
}
/** @deprecated */
getActivatedProcessId$() {
return this.store.select(selectActivatedProcess).pipe(map((process) => process?.id));
}
activateProcess(activatedProcessId: number) {
this.store.dispatch(setActivatedProcess({ activatedProcessId }));
this.activatedProcessIdSubject.next(activatedProcessId);
}
removeProcess(processId: number) {
this.store.dispatch(processRemoved({ processId }));
this.store.dispatch(removeProcess({ processId }));
}
createProcess() {}
patchProcess(processId: number, changes: Partial<ApplicationProcess>) {
this.store.dispatch(patchProcess({ processId, changes }));
}
patchProcessData(processId: number, data: Record<string, any>) {
this.store.dispatch(patchProcessData({ processId, data }));
}
async createProcess(process: ApplicationProcess) {
const existingProcess = await this.getProcessById$(process?.id).pipe(first()).toPromise();
if (existingProcess?.id === process?.id) {
throw new Error('Process Id existiert bereits');
}
if (!isNumber(process.id)) {
throw new Error('Process Id nicht gesetzt');
}
if (!isBoolean(process.closeable)) {
process.closeable = true;
}
if (!isBoolean(process.confirmClosing)) {
process.confirmClosing = true;
}
process.created = this._createTimestamp();
process.activated = 0;
this.store.dispatch(addProcess({ process }));
}
setSection(section: 'customer' | 'branch') {
this.store.dispatch(setSection({ section }));
}
getLastActivatedProcessWithSectionAndType$(section: 'customer' | 'branch', type: string): Observable<ApplicationProcess> {
return this.getProcesses$(section).pipe(
map((processes) =>
processes
?.filter((process) => process.type === type)
?.reduce((latest, current) => {
if (!latest) {
return current;
}
return latest?.activated > current?.activated ? latest : current;
}, undefined)
)
);
}
getLastActivatedProcessWithSection$(section: 'customer' | 'branch'): Observable<ApplicationProcess> {
return this.getProcesses$(section).pipe(
map((processes) =>
processes?.reduce((latest, current) => {
if (!latest) {
return current;
}
return latest?.activated > current?.activated ? latest : current;
}, undefined)
)
);
}
private _createTimestamp() {
return Date.now();
}
}

View File

@@ -1,5 +1,11 @@
export interface ApplicationProcess {
id: number;
created?: number;
activated?: number;
name: string;
data: { [key: string]: any };
section: 'customer' | 'branch';
type?: string;
data?: { [key: string]: any };
closeable?: boolean;
confirmClosing?: boolean;
}

View File

@@ -1,7 +1,6 @@
// start:ng42.barrel
export * from './application.module';
export * from './application.service';
export * from './process.service';
export * from './defs';
export * from './store';
// end:ng42.barrel

View File

@@ -1,8 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class ProcessService {
constructor() {}
updateName(processId: number, name: string) {}
}

View File

@@ -1,10 +1,16 @@
import { createAction, props } from '@ngrx/store';
import { ApplicationProcess } from '..';
const prefix = '[CORE-APPLICATION]';
/**
* Action nach Entfernung eines Prozesses
*/
export const processRemoved = createAction(`${prefix} Process Removed`, props<{ processId: number }>());
export const setSection = createAction(`${prefix} Set Section`, props<{ section: 'customer' | 'branch' }>());
export const addProcess = createAction(`${prefix} Add Process`, props<{ process: ApplicationProcess }>());
export const removeProcess = createAction(`${prefix} Remove Process`, props<{ processId: number }>());
export const setActivatedProcess = createAction(`${prefix} Set Activated Process`, props<{ activatedProcessId: number }>());
export const patchProcess = createAction(`${prefix} Patch Process`, props<{ processId: number; changes: Partial<ApplicationProcess> }>());
export const patchProcessData = createAction(`${prefix} Patch Process Data`, props<{ processId: number; data: Record<string, any> }>());

View File

@@ -0,0 +1,200 @@
import { INITIAL_APPLICATION_STATE } from './application.state';
import * as actions from './application.actions';
import { applicationReducer } from './application.reducer';
import { ApplicationProcess } from '../defs';
import { ApplicationState } from './application.state';
describe('applicationReducer', () => {
describe('setSection()', () => {
it('should return modified state with section customer', () => {
const initialState = INITIAL_APPLICATION_STATE;
const action = actions.setSection({ section: 'customer' });
const state = applicationReducer(initialState, action);
expect(state).toEqual({
...initialState,
section: 'customer',
});
});
it('should return modified state with section branch', () => {
const initialState = INITIAL_APPLICATION_STATE;
const action = actions.setSection({ section: 'branch' });
const state = applicationReducer(initialState, action);
expect(state).toEqual({
...initialState,
section: 'branch',
});
});
});
describe('addProcess()', () => {
it('should return modified state with new process if no processes are registered in the state', () => {
const initialState = INITIAL_APPLICATION_STATE;
const process: ApplicationProcess = {
id: 1,
name: 'Vorgang',
section: 'customer',
type: 'cart',
data: {},
};
const action = actions.addProcess({ process });
const state = applicationReducer(initialState, action);
expect(state.processes[0]).toEqual(process);
});
});
describe('patchProcess()', () => {
it('should return modified state with updated process when id is found', () => {
const initialState = INITIAL_APPLICATION_STATE;
const process: ApplicationProcess = {
id: 1,
name: 'Vorgang',
section: 'customer',
type: 'cart',
};
const action = actions.patchProcess({ process: { ...process, name: 'Test' } });
const state = applicationReducer(
{
...initialState,
processes: [process],
},
action
);
expect(state.processes[0].name).toEqual('Test');
});
it('should return unmodified state when id is not existing', () => {
const initialState = INITIAL_APPLICATION_STATE;
const process: ApplicationProcess = {
id: 1,
name: 'Vorgang',
section: 'customer',
type: 'cart',
};
const action = actions.patchProcess({ process: { ...process, id: 2 } });
const state = applicationReducer(
{
...initialState,
processes: [process],
},
action
);
expect(state.processes).toEqual([process]);
});
});
describe('removeProcess()', () => {
it('should return initial state if no processes are registered in the state', () => {
const initialState = INITIAL_APPLICATION_STATE;
const action = actions.removeProcess({ processId: 2 });
const state = applicationReducer(initialState, action);
expect(state).toEqual(initialState);
});
it('should return the unmodified state if processId not found', () => {
const initialState = INITIAL_APPLICATION_STATE;
const modifiedState: ApplicationState = {
...initialState,
section: 'customer',
processes: [
{
id: 1,
name: 'Vorgang',
section: 'customer',
type: 'cart',
},
{
id: 4,
name: 'Vorgang',
section: 'customer',
type: 'goods-out',
},
] as ApplicationProcess[],
};
const action = actions.removeProcess({ processId: 2 });
const state = applicationReducer(modifiedState, action);
expect(state).toEqual(modifiedState);
});
it('should return modified state, after process gets removed', () => {
const initialState = INITIAL_APPLICATION_STATE;
const modifiedState: ApplicationState = {
...initialState,
section: 'customer',
processes: [
{
id: 1,
name: 'Vorgang',
section: 'customer',
type: 'cart',
},
{
id: 2,
name: 'Vorgang',
section: 'customer',
type: 'goods-out',
},
] as ApplicationProcess[],
};
const action = actions.removeProcess({ processId: 2 });
const state = applicationReducer(modifiedState, action);
expect(state.processes).toEqual([
{
id: 1,
name: 'Vorgang',
section: 'customer',
type: 'cart',
},
]);
});
});
describe('setActivatedProcess()', () => {
it('should return modified state with process.activated value', () => {
const process: ApplicationProcess = {
id: 3,
name: 'Vorgang 3',
section: 'customer',
};
const initialState: ApplicationState = {
...INITIAL_APPLICATION_STATE,
processes: [process],
};
const action = actions.setActivatedProcess({ activatedProcessId: 3 });
const state = applicationReducer(initialState, action);
expect(state.processes[0].activated).toBeDefined();
});
it('should return modified state without process.activated value when activatedProcessId doesnt exist', () => {
const process: ApplicationProcess = {
id: 1,
name: 'Vorgang 3',
section: 'customer',
};
const initialState: ApplicationState = {
...INITIAL_APPLICATION_STATE,
processes: [process],
};
const action = actions.setActivatedProcess({ activatedProcessId: 3 });
const state = applicationReducer(initialState, action);
expect(state.processes[0].activated).toBeUndefined();
});
});
});

View File

@@ -1,10 +1,45 @@
import { Action, createReducer, on } from '@ngrx/store';
import { setSection } from './application.actions';
import { setSection, addProcess, removeProcess, setActivatedProcess, patchProcess, patchProcessData } from './application.actions';
import { ApplicationState, INITIAL_APPLICATION_STATE } from './application.state';
const _applicationReducer = createReducer(
INITIAL_APPLICATION_STATE,
on(setSection, (state, { section }) => ({ ...state, section }))
on(setSection, (state, { section }) => ({ ...state, section })),
on(addProcess, (state, { process }) => ({ ...state, processes: [...state.processes, { data: {}, ...process }] })),
on(removeProcess, (state, { processId }) => {
const processes = state?.processes?.filter((process) => process.id !== processId) || [];
return { ...state, processes };
}),
on(setActivatedProcess, (state, { activatedProcessId }) => {
const processes = state.processes.map((process) => {
if (process.id === activatedProcessId) {
return { ...process, activated: Date.now() };
}
return process;
});
return { ...state, processes };
}),
on(patchProcess, (state, { processId, changes }) => {
const processes = state.processes.map((process) => {
if (process.id === processId) {
return { ...process, ...changes };
}
return process;
});
return { ...state, processes };
}),
on(patchProcessData, (state, { processId, data }) => {
const processes = state.processes.map((process) => {
if (process.id === processId) {
return { ...process, data: { ...(process.data || {}), ...data } };
}
return process;
});
return { ...state, processes };
})
);
export function applicationReducer(state: ApplicationState, action: Action) {

View File

@@ -0,0 +1,32 @@
import { ApplicationState } from './application.state';
import { ApplicationProcess } from '../defs';
import * as selectors from './application.selectors';
describe('applicationSelectors', () => {
it('should select the processes', () => {
const processes: ApplicationProcess[] = [{ id: 1, name: 'Vorgang 1', section: 'customer' }];
const state: Partial<ApplicationState> = {
processes,
};
expect(selectors.selectProcesses.projector(state)).toEqual(processes);
});
it('should select the section', () => {
const state: Partial<ApplicationState> = {
section: 'customer',
};
expect(selectors.selectSection.projector(state)).toEqual('customer');
});
it('should select the activatedProcess', () => {
const processes: ApplicationProcess[] = [
{ id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 },
{ id: 2, name: 'Vorgang 2', section: 'customer', activated: 300 },
{ id: 3, name: 'Vorgang 3', section: 'customer', activated: 200 },
];
const state: Partial<ApplicationState> = {
processes,
};
expect(selectors.selectActivatedProcess.projector(state)).toEqual(processes[1]);
});
});

View File

@@ -3,3 +3,14 @@ import { ApplicationState } from './application.state';
export const selectApplicationState = createFeatureSelector<ApplicationState>('core-application');
export const selectSection = createSelector(selectApplicationState, (s) => s.section);
export const selectProcesses = createSelector(selectApplicationState, (s) => s.processes);
export const selectActivatedProcess = createSelector(selectApplicationState, (s) =>
s?.processes?.reduce((process, current) => {
if (!process) {
return current;
}
return process.activated > current.activated ? process : current;
}, undefined)
);

View File

@@ -1,7 +1,11 @@
import { ApplicationProcess } from '../defs';
export interface ApplicationState {
processes: ApplicationProcess[];
section: 'customer' | 'branch';
}
export const INITIAL_APPLICATION_STATE: ApplicationState = {
processes: [],
section: 'customer',
};

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

25
apps/core/auth/README.md Normal file
View File

@@ -0,0 +1,25 @@
# Auth
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
## Code scaffolding
Run `ng generate component component-name --project auth` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project auth`.
> Note: Don't forget to add `--project auth` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build auth` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build auth`, go to the dist folder `cd dist/auth` and run `npm publish`.
## Running unit tests
Run `ng test auth` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@@ -0,0 +1,43 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const customLaunchers = require('../../../karma/custom-launchers');
const junitReporter = require('../../../karma/junit-reporter')('core-auth');
const coverageReporter = require('../../../karma/coverage-reporter')('core-auth');
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('karma-junit-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
coverageReporter,
junitReporter,
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers,
singleRun: false,
restartOnFileChange: true,
});
};

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/core/auth",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "@core/auth",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^12.2.0",
"@angular/core": "^12.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,17 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { AuthService } from './auth.service';
import { OAuthModule } from 'angular-oauth2-oidc';
@NgModule({})
export class AuthModule {
static forRoot(): ModuleWithProviders<AuthModule> {
return {
ngModule: AuthModule,
providers: [
AuthService,
OAuthModule.forRoot({
resourceServer: { sendAccessToken: true },
}).providers,
],
};
}
}

View File

@@ -0,0 +1,151 @@
import { Config } from '@core/config';
import { SpectatorService, createServiceFactory, SpyObject } from '@ngneat/spectator';
import { OAuthService } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let spectator: SpectatorService<AuthService>;
const createService = createServiceFactory({
service: AuthService,
mocks: [Config, OAuthService],
});
let config: SpyObject<Config>;
let oAuthService: SpyObject<OAuthService>;
beforeEach(() => {
spectator = createService();
config = spectator.inject(Config);
oAuthService = spectator.inject(OAuthService);
});
it('should be created', () => {
expect(spectator.service).toBeTruthy();
});
describe('init()', () => {
it('should configure the oAuthService', () => {
config.get.and.returnValue({});
spectator.service.init();
expect(oAuthService.configure).toHaveBeenCalledWith({
redirectUri: window.location.origin,
silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
useSilentRefresh: true,
});
expect(oAuthService.tokenValidationHandler).toBeInstanceOf(JwksValidationHandler);
expect(oAuthService.setupAutomaticSilentRefresh).toHaveBeenCalled();
});
it('should load the discovery document', async () => {
config.get.and.returnValue({});
oAuthService.loadDiscoveryDocumentAndTryLogin.and.returnValue(Promise.resolve(true));
spyOn(spectator.service['_initialized'], 'next');
await spectator.service.init();
expect(oAuthService.loadDiscoveryDocumentAndTryLogin).toHaveBeenCalled();
expect(spectator.service['_initialized'].next).toHaveBeenCalledWith(true);
});
it('should throw an error if its already initialized', async () => {
spyOn(spectator.service['_initialized'], 'getValue').and.returnValue(true);
await expectAsync(spectator.service.init()).toBeRejectedWithError('AuthService is already initialized');
});
});
describe('isAuthenticated()', () => {
it('should call hasValidIdToken() and return its value', () => {
oAuthService.hasValidIdToken.and.returnValue(true);
expect(spectator.service.isAuthenticated()).toBeTrue();
expect(oAuthService.hasValidIdToken).toHaveBeenCalled();
});
});
describe('getToken()', () => {
it('should call getAccessToken() and return its value', () => {
oAuthService.getAccessToken.and.returnValue('token');
expect(spectator.service.getToken()).toEqual('token');
expect(oAuthService.getAccessToken).toHaveBeenCalled();
});
});
describe('getClaims()', () => {
it('should call getAccessToken() and return its value', () => {
oAuthService.getAccessToken.and.returnValue('token');
const claims = {
claim1: 'value',
claim2: 'value2',
};
spyOn(spectator.service, 'parseJwt').and.returnValue(claims);
expect(spectator.service.getClaims()).toEqual(claims);
expect(spectator.service.parseJwt).toHaveBeenCalledWith('token');
expect(oAuthService.getAccessToken).toHaveBeenCalled();
});
});
describe('getClaimByKey()', () => {
it('should call getClaims() and return its key value', () => {
spyOn(spectator.service, 'getClaims').and.returnValue({
claim1: 'value',
claim2: 'value2',
});
expect(spectator.service.getClaimByKey('claim1')).toEqual('value');
expect(spectator.service.getClaims).toHaveBeenCalled();
});
it('should return null if getClaims() returns null or undefined', () => {
spyOn(spectator.service, 'getClaims').and.returnValue(null);
expect(spectator.service.getClaimByKey('claim1')).toBeNull();
expect(spectator.service.getClaims).toHaveBeenCalled();
});
});
describe('parseJwt()', () => {
it('should return null if the token is null or undefined', () => {
expect(spectator.service.parseJwt(null)).toBeNull();
expect(spectator.service.parseJwt(undefined)).toBeNull();
});
it('should return the value of the key', () => {
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ';
expect(spectator.service.parseJwt(token)).toEqual({
sub: '1234567890',
name: 'John Doe',
admin: true,
});
});
});
describe('login()', () => {
it('should call initLoginFlow()', () => {
spectator.service.login();
expect(oAuthService.initLoginFlow).toHaveBeenCalled();
});
});
describe('logout()', () => {
it('should call revokeTokenAndLogout()', async () => {
await spectator.service.logout();
expect(oAuthService.revokeTokenAndLogout).toHaveBeenCalled();
});
});
describe('getToken()', () => {
it('should return getAccessToken()', () => {
spectator.service.getToken();
expect(oAuthService.getAccessToken).toHaveBeenCalled();
});
});
describe('initialized', () => {
it('should return _initialized as Observable', () => {
spyOn(spectator.service['_initialized'], 'asObservable').and.callThrough();
expect(spectator.service.initialized$).toBeInstanceOf(Observable);
expect(spectator.service['_initialized'].asObservable).toHaveBeenCalled();
});
});
});

View File

@@ -0,0 +1,84 @@
import { Injectable } from '@angular/core';
import { Config } from '@core/config';
import { isNullOrUndefined } from '@utils/common';
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private readonly _initialized = new BehaviorSubject<boolean>(false);
get initialized$() {
return this._initialized.asObservable();
}
constructor(private _config: Config, private readonly _oAuthService: OAuthService) {}
async init() {
if (this._initialized.getValue()) {
throw new Error('AuthService is already initialized');
}
const authConfig: AuthConfig = this._config.get('@core/auth');
authConfig.redirectUri = window.location.origin;
authConfig.silentRefreshRedirectUri = window.location.origin + '/silent-refresh.html';
authConfig.useSilentRefresh = true;
this._oAuthService.configure(authConfig);
this._oAuthService.tokenValidationHandler = new JwksValidationHandler();
this._oAuthService.setupAutomaticSilentRefresh();
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
this._initialized.next(true);
}
isAuthenticated() {
return this._oAuthService.hasValidIdToken();
}
getToken() {
return this._oAuthService.getAccessToken();
}
getClaims() {
const token = this._oAuthService.getAccessToken();
return this.parseJwt(token);
}
getClaimByKey(key: string) {
const claims = this.getClaims();
if (isNullOrUndefined(claims)) {
return null;
}
return claims[key];
}
parseJwt(token: string) {
if (isNullOrUndefined(token)) {
return null;
}
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const encoded = window.atob(base64);
return JSON.parse(encoded);
}
login() {
this._oAuthService.initLoginFlow();
}
setKeyCardToken(token: string) {
this._oAuthService.customQueryParams = {
temp_token: token,
};
}
async logout() {
await this._oAuthService.revokeTokenAndLogout();
}
}

View File

@@ -0,0 +1,4 @@
// start:ng42.barrel
export * from './auth.module';
export * from './auth.service';
// end:ng42.barrel

View File

@@ -0,0 +1,5 @@
/*
* Public API Surface of auth
*/
export * from './lib';

View File

@@ -0,0 +1,25 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp
): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } });
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,20 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -0,0 +1,17 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@@ -3,7 +3,7 @@ import { Store } from '@ngrx/store';
import { getNumberId } from '@utils/id';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { first, map, take } from 'rxjs/operators';
import { Breadcrumb } from './defs';
import * as actions from './store/breadcrumb.actions';
@@ -29,6 +29,19 @@ export class BreadcrumbService {
return this.store.select(selectors.selectBreadcrumbsByKey, key);
}
getLastActivatedBreadcrumbByKey$(key: string | number): Observable<Breadcrumb> {
return this.getBreadcrumbByKey$(key).pipe(
map((crumbs) =>
crumbs.reduce((latest, current) => {
if (!latest) {
return current;
}
return latest.timestamp > current.timestamp ? latest : current;
}, undefined)
)
);
}
addBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb {
const newBreadcrumb: Breadcrumb = { ...breadcrumb, id: getNumberId(), timestamp: Date.now(), changed: Date.now() };
this.store.dispatch(actions.addBreadcrumb({ breadcrumb: newBreadcrumb }));
@@ -39,6 +52,11 @@ export class BreadcrumbService {
this.store.dispatch(actions.updateBreadcrumb({ id: breadcrumbId, changes: { ...changes, changed: Date.now() } }));
}
async patchBreadcrumbByKeyAndTags(key: string | number, tags: string[], changes: Partial<Breadcrumb>) {
const crumbs = await this.getBreadcrumbsByKeyAndTags$(key, tags).pipe(first()).toPromise();
crumbs.forEach((crumb) => this.patchBreadcrumb(crumb.id, changes));
}
async addBreadcrumbIfNotExists(breadcrumb: Breadcrumb) {
const crumbs = await this.getBreadcrumbsByKeyAndTags$(breadcrumb.key, breadcrumb.tags).pipe(take(1)).toPromise();
if (crumbs.length === 0) {
@@ -112,6 +130,11 @@ export class BreadcrumbService {
this.store.dispatch(actions.removeManyBreadcrumb({ ids: breadcrumbsToRemove.map((crumb) => crumb.id) }));
}
async removeBreadcrumbsByKeyAndTags(key: number | string, tags: string[]) {
const crumbs = await this.getBreadcrumbsByKeyAndTags$(key, tags).pipe(first()).toPromise();
crumbs.forEach((crumb) => this.removeBreadcrumb(crumb.id));
}
getLatestBreadcrumbForSection(section: 'customer' | 'branch') {
return this.store
.select(selectors.selectBreadcrumbsBySection, { section })

View File

@@ -42,5 +42,5 @@ export interface Breadcrumb {
/**
* Applicatiuon Section
*/
section: 'branch' | 'customer';
section: string;
}

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { processRemoved } from '@core/application';
import { removeProcess } from '@core/application';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { NEVER } from 'rxjs';
import { mergeMap, tap, first, map } from 'rxjs/operators';
@@ -10,7 +10,7 @@ export class BreadcrumbEffects {
removeProcess$ = createEffect(
() =>
this.actions$.pipe(
ofType(processRemoved),
ofType(removeProcess),
mergeMap((action) =>
this.breadcrumb.getBreadcrumbByKey$(action.processId).pipe(
first(),

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -0,0 +1,25 @@
# Config
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
## Code scaffolding
Run `ng generate component component-name --project config` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project config`.
> Note: Don't forget to add `--project config` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build config` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build config`, go to the dist folder `cd dist/config` and run `npm publish`.
## Running unit tests
Run `ng test config` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@@ -0,0 +1,43 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const customLaunchers = require('../../../karma/custom-launchers');
const junitReporter = require('../../../karma/junit-reporter')('core-config');
const coverageReporter = require('../../../karma/coverage-reporter')('core-config');
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('karma-junit-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
coverageReporter,
junitReporter,
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers,
singleRun: false,
restartOnFileChange: true,
});
};

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/core/config",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "@core/config",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^12.2.0",
"@angular/core": "^12.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,8 @@
import { Observable } from 'rxjs';
/**
* Config loader interface for loading configurations
*/
export interface ConfigLoader {
load(): Promise<any>;
}

View File

@@ -0,0 +1,4 @@
// start:ng42.barrel
export * from './config-loader';
export * from './json.config-loader';
// end:ng42.barrel

View File

@@ -0,0 +1,36 @@
// unit test JsonConfigLoader
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
import { JsonConfigLoader } from './json.config-loader';
describe('JsonConfigLoader', () => {
let spectator: SpectatorService<JsonConfigLoader>;
const createService = createServiceFactory({
imports: [HttpClientTestingModule],
service: JsonConfigLoader,
mocks: [],
providers: [{ provide: CORE_JSON_CONFIG_LOADER_URL, useValue: '/assets/config.json' }],
});
let httpTestingController: HttpTestingController;
beforeEach(() => {
spectator = createService();
httpTestingController = spectator.inject(HttpTestingController);
});
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
describe('load', () => {
it('should call the provided url', async () => {
const reqPromise = spectator.service.load();
const req = httpTestingController.expectOne('/assets/config.json');
req.flush({ unit: 'test' });
const result = await reqPromise;
httpTestingController.verify();
expect(result).toEqual({ unit: 'test' });
});
});
});

View File

@@ -0,0 +1,13 @@
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { ConfigLoader } from './config-loader';
import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
@Injectable()
export class JsonConfigLoader implements ConfigLoader {
constructor(@Inject(CORE_JSON_CONFIG_LOADER_URL) private url: string, private http: HttpClient) {}
load(): Promise<any> {
return this.http.get(this.url).toPromise();
}
}

View File

@@ -0,0 +1,7 @@
import { Type } from '@angular/core';
import { ConfigLoader } from './config-loaders';
export interface ConfigModuleOptions {
useConfigLoader: Type<ConfigLoader>;
jsonConfigLoaderUrl?: string;
}

View File

@@ -0,0 +1,28 @@
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
import { CORE_CONFIG_LOADER } from '@core/config';
import { Config } from './config';
import { ConfigModuleOptions } from './config-module-options';
import { CORE_JSON_CONFIG_LOADER_URL } from './tokens';
export function _initializeConfigFactory(config: Config) {
return () => config.init();
}
@NgModule({})
export class ConfigModule {
static forRoot(options: ConfigModuleOptions): ModuleWithProviders<ConfigModule> {
const configLoaderProvider = {
provide: CORE_CONFIG_LOADER,
useClass: options.useConfigLoader,
};
return {
ngModule: ConfigModule,
providers: [
Config,
configLoaderProvider,
options.jsonConfigLoaderUrl ? { provide: CORE_JSON_CONFIG_LOADER_URL, useValue: options.jsonConfigLoaderUrl } : null,
],
};
}
}

View File

@@ -0,0 +1,45 @@
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { Config } from './config';
import { ConfigLoader } from './config-loaders';
import { CORE_CONFIG_LOADER } from './tokens';
class TestConfigLoader implements ConfigLoader {
load() {
return Promise.resolve({});
}
}
// Unit test Config
describe('Config', () => {
let spectator: SpectatorService<Config>;
const createService = createServiceFactory({
service: Config,
providers: [{ provide: CORE_CONFIG_LOADER, useClass: TestConfigLoader }],
});
let configLoader: ConfigLoader;
beforeEach(() => {
spectator = createService();
configLoader = spectator.inject(CORE_CONFIG_LOADER);
});
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
describe('init()', () => {
it('should load config and assigns it to _config', async () => {
const config = { unit: 'test' };
spyOn(configLoader, 'load').and.returnValue(Promise.resolve(config));
await spectator.service.init();
expect(spectator.service['_config']).toEqual(config);
});
});
describe('get()', () => {
it('should return config value', () => {
spectator.service['_config'] = { test: 'test' };
expect(spectator.service.get('test')).toEqual('test');
});
});
});

View File

@@ -0,0 +1,27 @@
import { Inject, Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs';
import { ConfigLoader } from './config-loaders';
import { CORE_CONFIG_LOADER } from './tokens';
import { pick } from './utils';
@Injectable()
export class Config {
private _config: any;
private readonly _initilized = new ReplaySubject<void>(1);
get initialized() {
return this._initilized.asObservable();
}
constructor(@Inject(CORE_CONFIG_LOADER) private readonly _configLoader: ConfigLoader) {}
// load config and assign it to this._config
async init() {
this._config = await this._configLoader.load();
this._initilized.next();
}
get(path: string) {
return pick(path, this._config);
}
}

View File

@@ -0,0 +1,8 @@
// start:ng42.barrel
export * from './config-module-options';
export * from './config.module';
export * from './config';
export * from './tokens';
export * from './config-loaders';
export * from './utils';
// end:ng42.barrel

View File

@@ -0,0 +1,6 @@
import { InjectionToken } from '@angular/core';
import { ConfigLoader } from './config-loaders';
export const CORE_CONFIG_LOADER = new InjectionToken<ConfigLoader>('core.config.loader');
export const CORE_JSON_CONFIG_LOADER_URL = new InjectionToken<ConfigLoader>('core.json.config.loader.url');

View File

@@ -0,0 +1,3 @@
// start:ng42.barrel
export * from './pick';
// end:ng42.barrel

View File

@@ -0,0 +1,41 @@
import { pick } from './pick';
describe('pick', () => {
it('should pick properties from the 1st level from the object', () => {
const obj = {
foo: 'bar',
};
expect(pick('foo', obj)).toEqual('bar');
});
it('should pick properties from the 2nd level from the object', () => {
const obj = {
foo: {
bar: 'baz',
},
};
expect(pick('foo.bar', obj)).toEqual('baz');
});
it('should pick properties from the 3rd level from the object', () => {
const obj = {
foo: {
bar: {
baz: 'qux',
},
},
};
expect(pick('foo.bar.baz', obj)).toEqual('qux');
});
it('should throw an error of obj is not an object', () => {
expect(() => pick('foo', 'bar')).toThrowError(`bar is not an object`);
});
it('should return undefined if the property is not found', () => {
const obj = {
foo: 'bar',
};
expect(pick('bar', obj)).toEqual(undefined);
});
});

View File

@@ -0,0 +1,33 @@
/**
* Pick a value from an object at a given path.
* @param path path of the value to pick
* @param obj object to pick from
* @returns the value at the path or undefined
* @throws if obj is not an object
*/
export function pick<T = any>(path: string, obj: Object): T {
const paths = path.split('.');
// check if obj is null or undefined
if (obj == null) {
return undefined;
}
// check if obj is of type object and not an array
// and throw an error if not
if (typeof obj !== 'object' || Array.isArray(obj)) {
throw new Error(`${obj} is not an object`);
}
let result = obj;
// loop through the path and pick the value
// early exit if the path is empty
for (const path of paths) {
result = result[path];
if (result == null) {
return undefined;
}
}
return result as T;
}

View File

@@ -0,0 +1,5 @@
/*
* Public API Surface of config
*/
export * from './lib';

View File

@@ -0,0 +1,25 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp
): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } });
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,20 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -0,0 +1,17 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -0,0 +1,25 @@
# Logger
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
## Code scaffolding
Run `ng generate component component-name --project logger` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project logger`.
> Note: Don't forget to add `--project logger` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build logger` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build logger`, go to the dist folder `cd dist/logger` and run `npm publish`.
## Running unit tests
Run `ng test logger` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@@ -0,0 +1,43 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const customLaunchers = require('../../../karma/custom-launchers');
const junitReporter = require('../../../karma/junit-reporter')('core-logger');
const coverageReporter = require('../../../karma/coverage-reporter')('core-logger');
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('karma-junit-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
junitReporter,
coverageReporter,
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers,
singleRun: false,
restartOnFileChange: true,
});
};

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/core/logger",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "@core/logger",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^12.2.0",
"@angular/core": "^12.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,51 @@
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { ConsoleLogProvider } from './console-log.provider';
import { LogLevel } from './log-level';
describe('ConsoleLogProvider', () => {
let spectator: SpectatorService<ConsoleLogProvider>;
const createService = createServiceFactory({
service: ConsoleLogProvider,
});
beforeEach(() => {
spectator = createService();
});
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
describe('log', () => {
it('should call console.debug', () => {
spyOn(console, 'debug');
spectator.service.log(LogLevel.DEBUG, 'test');
expect(console.debug).toHaveBeenCalledWith('test');
});
it('should call console.info', () => {
spyOn(console, 'info');
spectator.service.log(LogLevel.INFO, 'test');
expect(console.info).toHaveBeenCalledWith('test');
});
it('should call console.warn', () => {
spyOn(console, 'warn');
spectator.service.log(LogLevel.WARN, 'test');
expect(console.warn).toHaveBeenCalledWith('test');
});
it('should call console.error', () => {
spyOn(console, 'error');
spectator.service.log(LogLevel.ERROR, 'test');
expect(console.error).toHaveBeenCalledWith('test');
});
it('should not call console.log', () => {
spyOn(console, 'log');
spectator.service.log(LogLevel.OFF, 'test');
expect(console.log).not.toHaveBeenCalled();
});
});
});

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { LogLevel } from './log-level';
import { LogProvider } from './log.provider';
@Injectable()
export class ConsoleLogProvider implements LogProvider {
log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void {
switch (logLevel) {
case LogLevel.DEBUG:
console.debug(message, ...optionalParams);
break;
case LogLevel.INFO:
console.info(message, ...optionalParams);
break;
case LogLevel.WARN:
console.warn(message, ...optionalParams);
break;
case LogLevel.ERROR:
console.error(message, ...optionalParams);
break;
case LogLevel.OFF:
break;
}
}
}

View File

@@ -0,0 +1,8 @@
// start:ng42.barrel
export * from './console-log.provider';
export * from './log-level';
export * from './log.provider';
export * from './logger.module';
export * from './logger.service';
export * from './tokens';
// end:ng42.barrel

View File

@@ -0,0 +1,14 @@
export enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3,
OFF = 4,
// aliases
debug = 0,
info = 1,
warn = 2,
error = 3,
off = 4,
}

View File

@@ -0,0 +1,6 @@
import { Injectable } from '@angular/core';
import { LogLevel } from './log-level';
export interface LogProvider {
log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void;
}

View File

@@ -0,0 +1,31 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { Config } from '@core/config';
import { ConsoleLogProvider } from './console-log.provider';
import { Logger } from './logger.service';
import { LOG_PROVIDER, LOG_LEVEL } from './tokens';
export function _logLevelProviderFactory(config: Config) {
return config.get('@core/logger.logLevel');
}
@NgModule({})
export class CoreLoggerModule {
static forRoot(): ModuleWithProviders<CoreLoggerModule> {
return {
ngModule: CoreLoggerModule,
providers: [
Logger,
{
provide: LOG_PROVIDER,
useClass: ConsoleLogProvider,
multi: true,
},
{
provide: LOG_LEVEL,
useFactory: _logLevelProviderFactory,
deps: [Config],
},
],
};
}
}

View File

@@ -0,0 +1,140 @@
import { SpectatorService, createServiceFactory } from '@ngneat/spectator';
import { LogLevel } from './log-level';
import { Logger } from './logger.service';
import { LOG_PROVIDER } from './tokens';
import { LOG_LEVEL } from '.';
describe('LoggerService', () => {
let spectator: SpectatorService<Logger>;
let logProviderMock1 = jasmine.createSpyObj('LogProvider', ['log']);
let logProviderMock2 = jasmine.createSpyObj('LogProvider', ['log']);
const createService = createServiceFactory({
service: Logger,
mocks: [Logger],
providers: [
{
provide: LOG_PROVIDER,
useValue: logProviderMock1,
multi: true,
},
{
provide: LOG_PROVIDER,
useValue: logProviderMock2,
multi: true,
},
{
provide: LOG_LEVEL,
useValue: LogLevel.DEBUG,
},
],
});
beforeEach(() => {
spectator = createService();
});
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
const testData: {
serviceLogLevel: LogLevel;
callLogLevel: LogLevel;
shoudCall: boolean;
}[] = [
{ serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.DEBUG, shoudCall: true },
{ serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.INFO, shoudCall: true },
{ serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.WARN, shoudCall: true },
{ serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.ERROR, shoudCall: true },
{ serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.DEBUG, shoudCall: false },
{ serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.INFO, shoudCall: true },
{ serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.WARN, shoudCall: true },
{ serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.ERROR, shoudCall: true },
{ serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.DEBUG, shoudCall: false },
{ serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.INFO, shoudCall: false },
{ serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.WARN, shoudCall: true },
{ serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.ERROR, shoudCall: true },
{ serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.DEBUG, shoudCall: false },
{ serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.INFO, shoudCall: false },
{ serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.WARN, shoudCall: false },
{ serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.ERROR, shoudCall: true },
{ serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.DEBUG, shoudCall: false },
{ serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.INFO, shoudCall: false },
{ serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.WARN, shoudCall: false },
{ serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.ERROR, shoudCall: false },
];
describe('log()', () => {
beforeEach(() => {
logProviderMock1.log.calls.reset();
logProviderMock2.log.calls.reset();
});
for (const test of testData) {
if (test.shoudCall) {
it(`should call logProvider if logLevel is ${LogLevel[test.callLogLevel]} and serviceLogLevel is ${
LogLevel[test.serviceLogLevel]
}`, () => {
spectator.service['_logLevel'] = test.serviceLogLevel;
spectator.service.log(test.callLogLevel, 'test');
expect(logProviderMock1.log).toHaveBeenCalledTimes(1);
expect(logProviderMock2.log).toHaveBeenCalledTimes(1);
});
} else {
it(`should not call logProvider if logLevel is ${LogLevel[test.callLogLevel]} and serviceLogLevel is ${
LogLevel[test.serviceLogLevel]
}`, () => {
spectator.service['_logLevel'] = test.serviceLogLevel;
spectator.service.log(test.callLogLevel, 'test');
expect(logProviderMock1.log).not.toHaveBeenCalled();
expect(logProviderMock2.log).not.toHaveBeenCalled();
});
}
}
});
describe('warn()', () => {
it('should call log and logLevel should be WARN', () => {
spyOn(spectator.service, 'log');
spectator.service.warn('test', 'data');
expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.WARN, 'test', 'data');
});
});
describe('info()', () => {
it('should call log and logLevel should be INFO', () => {
spyOn(spectator.service, 'log');
spectator.service.info('test', 'data');
expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.INFO, 'test', 'data');
});
});
describe('debug()', () => {
it('should call log and logLevel should be DEBUG', () => {
spyOn(spectator.service, 'log');
spectator.service.debug('test', 'data');
expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.DEBUG, 'test', 'data');
});
});
describe('error()', () => {
it('should call log and logLevel should be ERROR', () => {
spyOn(spectator.service, 'log');
spectator.service.error('test', 'data');
expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.ERROR, 'test', 'data');
});
});
});

View File

@@ -0,0 +1,39 @@
import { Inject, Injectable } from '@angular/core';
import { LogLevel } from './log-level';
import { LogProvider } from './log.provider';
import { LOG_LEVEL, LOG_PROVIDER } from './tokens';
@Injectable()
export class Logger {
constructor(@Inject(LOG_PROVIDER) private readonly _loggerProviders: LogProvider[], @Inject(LOG_LEVEL) private _logLevel: LogLevel) {}
log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void {
if (this._logLevel === LogLevel.OFF) {
return;
}
if (this._logLevel <= logLevel) {
console.log(this._logLevel, logLevel, this._logLevel <= logLevel);
this._loggerProviders.forEach((provider) => {
provider.log(logLevel, message, ...optionalParams);
});
}
}
warn(message: string, ...optionalParams: any[]): void {
this.log(LogLevel.WARN, message, ...optionalParams);
}
info(message: string, ...optionalParams: any[]): void {
this.log(LogLevel.INFO, message, ...optionalParams);
}
debug(message: string, ...optionalParams: any[]): void {
this.log(LogLevel.DEBUG, message, ...optionalParams);
}
error(message: string, ...optionalParams: any[]): void {
this.log(LogLevel.ERROR, message, ...optionalParams);
}
}

View File

@@ -0,0 +1,6 @@
import { LogProvider } from './log.provider';
import { InjectionToken } from '@angular/core';
import { LogLevel } from './log-level';
export const LOG_PROVIDER = new InjectionToken<LogProvider[]>('LOG_PROVIDER');
export const LOG_LEVEL = new InjectionToken<LogLevel>('LOG_LEVEL');

View File

@@ -0,0 +1,5 @@
/*
* Public API Surface of logger
*/
export * from './lib';

View File

@@ -0,0 +1,25 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp
): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } });
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,20 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -0,0 +1,17 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { ItemDTO } from '@swagger/cat';
import { ItemDTO, SearchService } from '@swagger/cat';
import { AvailabilityDTO, BranchDTO, OLAAvailabilityDTO, StoreCheckoutService, SupplierDTO } from '@swagger/checkout';
import { combineLatest, Observable, of } from 'rxjs';
import {
@@ -61,7 +61,23 @@ export class DomainAvailabilityService {
@memorize()
getCurrentBranch(): Observable<BranchDTO> {
return this._stock.StockCurrentBranch().pipe(
map((response) => response.result),
map((response) => ({
id: response.result.id,
name: response.result.name,
address: response.result.address,
branchType: response.result.branchType,
branchNumber: response.result.branchNumber,
changed: response.result.changed,
created: response.result.created,
isDefault: response.result.isDefault,
isOnline: response.result.isOnline,
key: response.result.key,
label: response.result.label,
pId: response.result.pId,
shortName: response.result.shortName,
status: response.result.status,
version: response.result.version,
})),
shareReplay()
);
}

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

View File

@@ -1,7 +1,7 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

Some files were not shown because too many files have changed in this diff Show More