From 784c4f7ae7e978c717aaad017b31adfff641937c Mon Sep 17 00:00:00 2001 From: Henry Taeschner Date: Wed, 21 Feb 2024 16:54:06 +0100 Subject: [PATCH 1/4] feat: added management of mfes and ms --- package-lock.json | 41 +- package.json | 12 +- .../app-delete/app-delete.component.html | 6 +- .../app-delete/app-delete.component.ts | 38 +- .../app-detail/app-detail.component.html | 330 ++++++++--- .../app-detail/app-detail.component.spec.ts | 4 +- .../app-detail/app-detail.component.ts | 223 ++++++-- .../app-search/app-search.component.html | 167 +++--- .../app-search/app-search.component.spec.ts | 4 +- .../app-search/app-search.component.ts | 140 ++++- .../product-apps/product-apps.component.html | 13 +- .../product-apps/product-apps.component.ts | 49 +- .../product.detail.component.spec.ts | 4 +- .../product-search.component.ts | 6 +- .../shared/generated/.openapi-generator/FILES | 7 +- src/app/shared/generated/api/api.ts | 4 +- .../generated/api/microfrontends.service.ts | 14 +- .../generated/api/microservices.service.ts | 517 ++++++++++++++++++ .../model/createMicroserviceRequest.ts | 22 + ...hCriteria.ts => mfeAndMsSearchCriteria.ts} | 2 +- .../shared/generated/model/microservice.ts | 27 + .../generated/model/microservicePageResult.ts | 25 + src/app/shared/generated/model/models.ts | 6 +- .../model/updateMicroserviceRequest.ts | 22 + ...ct-store-bff-api.yaml => openapi-bff.yaml} | 301 +++++++++- src/assets/i18n/de.json | 37 +- src/assets/i18n/en.json | 35 +- 27 files changed, 1704 insertions(+), 352 deletions(-) create mode 100644 src/app/shared/generated/api/microservices.service.ts create mode 100644 src/app/shared/generated/model/createMicroserviceRequest.ts rename src/app/shared/generated/model/{microfrontendSearchCriteria.ts => mfeAndMsSearchCriteria.ts} (94%) create mode 100644 src/app/shared/generated/model/microservice.ts create mode 100644 src/app/shared/generated/model/microservicePageResult.ts create mode 100644 src/app/shared/generated/model/updateMicroserviceRequest.ts rename src/assets/api/{product-store-bff-api.yaml => openapi-bff.yaml} (74%) diff --git a/package-lock.json b/package-lock.json index 7b98278..969b429 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,11 +23,11 @@ "@ngneat/falso": "^6.4.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", - "@onecx/accelerator": "^4.1.2", - "@onecx/integration-interface": "^4.1.2", - "@onecx/keycloak-auth": "^4.1.2", - "@onecx/portal-integration-angular": "^4.1.2", - "@onecx/portal-layout-styles": "^4.1.2", + "@onecx/accelerator": "^4.5.1", + "@onecx/integration-interface": "^4.5.1", + "@onecx/keycloak-auth": "^4.5.1", + "@onecx/portal-integration-angular": "^4.5.1", + "@onecx/portal-layout-styles": "^4.5.1", "file-saver": "^2.0.5", "i18n-iso-countries": "^7.6.0", "ngx-color": "^8.0.3", @@ -7204,18 +7204,18 @@ } }, "node_modules/@onecx/accelerator": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@onecx/accelerator/-/accelerator-4.2.0.tgz", - "integrity": "sha512-/CXlDCJ45gpaGsJuAIeD4j8j//i18qeCJ3v614R9hszGLA4/OAwUk65wyh+uEhehiy+y1KNFBDs6xdUmYgwm0g==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@onecx/accelerator/-/accelerator-4.6.0.tgz", + "integrity": "sha512-6k+KIYugoEMeVv7525JibVHbIWN3uit3WsioYzeq9/aTG9Nv5ykkGDn0WpZ98nfm+VssuA+ykgNXCrkpjHJwgw==", "peerDependencies": { "rxjs": "7.8.1", "tslib": "^2.3.0" } }, "node_modules/@onecx/integration-interface": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@onecx/integration-interface/-/integration-interface-4.2.0.tgz", - "integrity": "sha512-UNXf+rOZe/dee1B+wkQ593D1hEfB3YEzgY43E7yDcr+L0u9eYFTrrSGkz/bmMKC1sAy/Ls0BMUObwRqHCVWYJQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@onecx/integration-interface/-/integration-interface-4.6.0.tgz", + "integrity": "sha512-MMS86UHQIj6WiqRlHiEyIQMdjOiEFEOLKQvc+EzkizdDsqGgFOQg26WXbwp5V0wwPpOMia5KN9M/ZnqWVy2Dkw==", "peerDependencies": { "@onecx/accelerator": "~4", "rxjs": "7.8.1", @@ -7223,9 +7223,9 @@ } }, "node_modules/@onecx/keycloak-auth": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@onecx/keycloak-auth/-/keycloak-auth-4.2.0.tgz", - "integrity": "sha512-rrbMsNyEBHMr74sKghF1hKkbqaBDTN1SBP9B5fCJX2KSpKn5GlVCFfnw12EfXdlqDmQhiIttPxb8jKfWllm3Yw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@onecx/keycloak-auth/-/keycloak-auth-4.6.0.tgz", + "integrity": "sha512-dDYxcVyzYA12CmdVHDE1ODn+v0jeHjDG7ZGpHJmcFFk1XnhXHxDlP0ADvwVNmKLnrVAes8IOQehvX9hhVsqq8Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -7239,9 +7239,9 @@ } }, "node_modules/@onecx/portal-integration-angular": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@onecx/portal-integration-angular/-/portal-integration-angular-4.2.0.tgz", - "integrity": "sha512-y3EzIOwIGr4G5w63Ipxw8AEqnRrH+0pTUTmNhHHgZNCRbxurgO+aP1xk2YA+bLhBc1vgoTwKJ1ctgkbVTP6GnQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@onecx/portal-integration-angular/-/portal-integration-angular-4.6.0.tgz", + "integrity": "sha512-8F4q/BhwM1Jfh+i0YJClE0NyQanaN4UO49BIwBx/fwTpOdRfbGeVlcWMF9EFVAnU5ha0peSVTUre7S+bXr9oiQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -7260,6 +7260,7 @@ "@ngrx/store": "^15.4.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", + "@onecx/accelerator": "~4", "@onecx/integration-interface": "~4", "chart.js": "^4.4.0", "d3-scale-chromatic": "^3.0.0", @@ -7271,9 +7272,9 @@ } }, "node_modules/@onecx/portal-layout-styles": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@onecx/portal-layout-styles/-/portal-layout-styles-4.2.0.tgz", - "integrity": "sha512-ccuV1iJ4pTVk9tM8s/36zoSJ89INXMftqheJy6Suv6vYaKu5Cn6FZCtcCarVEGb+hKikVzdL408z7JZcOTdw0A==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@onecx/portal-layout-styles/-/portal-layout-styles-4.6.0.tgz", + "integrity": "sha512-MSJ4hLxuk7nEUGYH5dBurhI9hrVtAC7d/xS5y4gZRBlqHBBUQL+eC+lio+EIEzDh3eaoFIP8t8ZieQUtTmdY6g==", "peerDependencies": { "tslib": "^2.5.0" } diff --git a/package.json b/package.json index b818267..428057f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "OneCX Development Team " ], "config": { - "openapiYaml": "src/assets/api/product-store-bff-api.yaml", + "openapiYaml": "src/assets/api/openapi-bff.yaml", "openapiOutput": "src/app/shared/generated" }, "scripts": { @@ -49,11 +49,11 @@ "@ngneat/falso": "^6.4.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", - "@onecx/accelerator": "^4.1.2", - "@onecx/integration-interface": "^4.1.2", - "@onecx/keycloak-auth": "^4.1.2", - "@onecx/portal-integration-angular": "^4.1.2", - "@onecx/portal-layout-styles": "^4.1.2", + "@onecx/accelerator": "^4.5.1", + "@onecx/integration-interface": "^4.5.1", + "@onecx/keycloak-auth": "^4.5.1", + "@onecx/portal-integration-angular": "^4.5.1", + "@onecx/portal-layout-styles": "^4.5.1", "file-saver": "^2.0.5", "i18n-iso-countries": "^7.6.0", "ngx-color": "^8.0.3", diff --git a/src/app/product-store/app-delete/app-delete.component.html b/src/app/product-store/app-delete/app-delete.component.html index 15ee6ad..69176fa 100644 --- a/src/app/product-store/app-delete/app-delete.component.html +++ b/src/app/product-store/app-delete/app-delete.component.html @@ -1,6 +1,6 @@
-
{{ ('ACTIONS.DELETE.MESSAGE' | translate).replace("{{ITEM}}", appAbstract ? appAbstract.appId : '') }}
+
{{ ('ACTIONS.DELETE.MESSAGE' | translate).replace("{{ITEM}}", appAbstract?.appId ?? '') }}
{{ 'ACTIONS.DELETE.MESSAGE_INFO' | translate }}
@@ -34,7 +34,7 @@ iconPos="left" [label]="'ACTIONS.CONFIRMATION.YES' | translate" [title]="'ACTIONS.CONFIRMATION.YES.TOOLTIP' | translate" - (click)="confirmDeletion()" + (click)="onConfirmDeletion()" > diff --git a/src/app/product-store/app-delete/app-delete.component.ts b/src/app/product-store/app-delete/app-delete.component.ts index 84f7fbd..6427195 100644 --- a/src/app/product-store/app-delete/app-delete.component.ts +++ b/src/app/product-store/app-delete/app-delete.component.ts @@ -2,29 +2,47 @@ import { Component, EventEmitter, Input, Output } from '@angular/core' import { TranslateService } from '@ngx-translate/core' import { PortalMessageService } from '@onecx/portal-integration-angular' -import { MicrofrontendsAPIService, MicrofrontendAbstract } from 'src/app/shared/generated' +import { AppAbstract } from '../app-search/app-search.component' +import { MicrofrontendsAPIService, MicroservicesAPIService } from 'src/app/shared/generated' @Component({ selector: 'app-app-delete', templateUrl: './app-delete.component.html' }) export class AppDeleteComponent { - @Input() appAbstract: MicrofrontendAbstract | undefined + @Input() appAbstract: AppAbstract | undefined @Input() displayDialog = false - @Output() displayDialogChange = new EventEmitter() - - public deleteMessage = '' + @Output() appDeleted = new EventEmitter() constructor( - private appApi: MicrofrontendsAPIService, + private msApi: MicroservicesAPIService, + private mfeApi: MicrofrontendsAPIService, private msgService: PortalMessageService, private translate: TranslateService ) {} - public onDialogHide() { - this.displayDialogChange.emit(false) + public onDialogHide(): void { + this.appDeleted.emit(false) } - public confirmDeletion() { - console.log('confirmDeletion') + + public onConfirmDeletion(): void { + if (this.appAbstract?.id) { + if (this.appAbstract?.appType === 'MFE') + this.mfeApi.deleteMicrofrontend({ id: this.appAbstract?.id }).subscribe({ + next: () => { + this.msgService.success({ summaryKey: 'ACTIONS.DELETE.APP.OK' }) + this.appDeleted.emit(true) + }, + error: () => this.msgService.error({ summaryKey: 'ACTIONS.DELETE.APP.NOK' }) + }) + if (this.appAbstract?.appType === 'MS') + this.msApi.deleteMicroservice({ id: this.appAbstract?.id }).subscribe({ + next: () => { + this.msgService.success({ summaryKey: 'ACTIONS.DELETE.APP.OK' }) + this.appDeleted.emit(true) + }, + error: () => this.msgService.error({ summaryKey: 'ACTIONS.DELETE.APP.NOK' }) + }) + } } } diff --git a/src/app/product-store/app-detail/app-detail.component.html b/src/app/product-store/app-detail/app-detail.component.html index 605738d..c6af714 100644 --- a/src/app/product-store/app-detail/app-detail.component.html +++ b/src/app/product-store/app-detail/app-detail.component.html @@ -1,6 +1,11 @@ -
+ - {{ 'ACTIONS.' + this.changeMode + '.APP.HEADER' | translate }} + {{ 'ACTIONS.' + this.changeMode + '.MFE.HEADER' | translate }} - - {{ 'MFE.GROUP.REMOTE_MODULE' | translate }} + + {{ 'APP.GROUP.REMOTE_MODULE' | translate }}
-
- +
+ -
-
- +
+ -
-
- +
+ -
-
- +
+ -
@@ -100,139 +105,295 @@ - {{ 'MFE.GROUP.LOCAL_MODULE' | translate }} + {{ 'APP.GROUP.LOCAL_MODULE' | translate }}
-
- +
+ - +
-
- +
+ - +
-
- +
+ -
-
- +
+ -
-
- +
+ - +
-
- +
+ - +
-
- +
+ - +
-
- +
+ + + +
+
+ + + + + {{ 'APP.GROUP.INTERNALS' | translate }} + +
+
+ + + + +
+
+ + + + +
+
+
+ + +
+ + {{ 'ACTIONS.' + this.changeMode + '.MS.HEADER' | translate }} + + + + {{ 'APP.GROUP.LOCAL_MODULE' | translate }} + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + - +
- + - {{ 'MFE.GROUP.INTERNALS' | translate }} + {{ 'APP.GROUP.INTERNALS' | translate }}
- + - +
diff --git a/src/app/product-store/app-detail/app-detail.component.spec.ts b/src/app/product-store/app-detail/app-detail.component.spec.ts index 299f1b8..b3f1270 100644 --- a/src/app/product-store/app-detail/app-detail.component.spec.ts +++ b/src/app/product-store/app-detail/app-detail.component.spec.ts @@ -68,9 +68,9 @@ describe('AppDetailComponent', () => { getValue: jasmine.createSpy('getValue').and.returnValue('en') }, hasPermission: jasmine.createSpy('hasPermission').and.callFake((permissionName) => { - if (permissionName === 'MICROFRONTEND#CREATE') { + if (permissionName === 'APP#CREATE') { return true - } else if (permissionName === 'MICROFRONTEND#EDIT') { + } else if (permissionName === 'APP#EDIT') { return true } else { return false diff --git a/src/app/product-store/app-detail/app-detail.component.ts b/src/app/product-store/app-detail/app-detail.component.ts index 625f286..abf50f1 100644 --- a/src/app/product-store/app-detail/app-detail.component.ts +++ b/src/app/product-store/app-detail/app-detail.component.ts @@ -7,16 +7,21 @@ import { TranslateService } from '@ngx-translate/core' import { PortalMessageService, UserService } from '@onecx/portal-integration-angular' import { CreateMicrofrontendRequest, + CreateMicroserviceRequest, GetMicrofrontendByAppIdRequestParams, MicrofrontendsAPIService, - MicrofrontendAbstract, + Microservice, Microfrontend, - UpdateMicrofrontendRequest + UpdateMicrofrontendRequest, + UpdateMicroserviceRequest, + MicroservicesAPIService, + GetMicroserviceByAppIdRequestParams } from 'src/app/shared/generated' +import { AppAbstract } from '../app-search/app-search.component' export type ChangeMode = 'VIEW' | 'CREATE' | 'EDIT' | 'COPY' -export interface AppDetailForm { +export interface MfeForm { appId: FormControl appName: FormControl appVersion: FormControl @@ -31,6 +36,13 @@ export interface AppDetailForm { note?: FormControl exposedModule?: FormControl } +export interface MsForm { + appId: FormControl + appName: FormControl + appVersion: FormControl + productName: FormControl + description: FormControl +} @Component({ selector: 'app-app-detail', @@ -38,29 +50,33 @@ export interface AppDetailForm { styleUrls: ['./app-detail.component.scss'] }) export class AppDetailComponent implements OnChanges { - @Input() appAbstract: MicrofrontendAbstract | undefined + @Input() appAbstract: AppAbstract | undefined @Input() dateFormat = 'medium' @Input() changeMode: ChangeMode = 'VIEW' - @Input() displayDetailDialog = false - @Output() displayDetailDialogChange = new EventEmitter() + @Input() displayDialog = false + @Output() displayDialogChange = new EventEmitter() + @Output() appChanged = new EventEmitter() - public app: Microfrontend | undefined - public formGroup: FormGroup + public mfe: Microfrontend | undefined + public ms: Microservice | undefined + public formGroupMfe: FormGroup + public formGroupMs: FormGroup public loading = false public hasCreatePermission = false public hasEditPermission = false constructor( private user: UserService, - private appApi: MicrofrontendsAPIService, + private msApi: MicroservicesAPIService, + private mfeApi: MicrofrontendsAPIService, private msgService: PortalMessageService, private translate: TranslateService ) { - this.hasCreatePermission = this.user.hasPermission('MICROFRONTEND#CREATE') - this.hasEditPermission = this.user.hasPermission('MICROFRONTEND#EDIT') + this.hasCreatePermission = this.user.hasPermission('APP#CREATE') + this.hasEditPermission = this.user.hasPermission('APP#EDIT') this.dateFormat = this.user.lang$.getValue() === 'de' ? 'dd.MM.yyyy HH:mm:ss' : 'medium' - this.formGroup = new FormGroup({ + this.formGroupMfe = new FormGroup({ appId: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), appName: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), appVersion: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), @@ -75,21 +91,32 @@ export class AppDetailComponent implements OnChanges { iconName: new FormControl(null, [Validators.maxLength(255)]), note: new FormControl(null, [Validators.maxLength(255)]) }) + this.formGroupMs = new FormGroup({ + appId: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), + appName: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), + appVersion: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), + productName: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), + description: new FormControl(null, [Validators.maxLength(255)]) + }) } ngOnChanges() { - if (this.appAbstract && this.displayDetailDialog) { + if (this.appAbstract?.id && this.displayDialog) { if (this.changeMode !== 'CREATE') { - this.getApp() + if (this.appAbstract.appType === 'MFE') this.getMfe() + if (this.appAbstract.appType === 'MS') this.getMs() } else if (this.changeMode === 'CREATE') { - this.app = undefined + this.ms = undefined + this.mfe = undefined + this.formGroupMs.reset() + this.formGroupMfe.reset() } } } - public getApp() { + public getMfe() { this.loading = true - this.appApi + this.mfeApi .getMicrofrontendByAppId({ appId: this.appAbstract?.appId } as GetMicrofrontendByAppIdRequestParams) .pipe( finalize(() => { @@ -99,13 +126,13 @@ export class AppDetailComponent implements OnChanges { .subscribe({ next: (data: any) => { if (data) { - this.app = data - this.viewForm(this.app) + this.mfe = data + if (this.mfe) this.viewFormMfe(this.mfe) if (this.changeMode === 'COPY') { - if (this.app?.id) { - this.app.id = undefined - this.app.creationDate = undefined - this.app.modificationDate = undefined + if (this.mfe?.id) { + this.mfe.id = undefined + this.mfe.creationDate = undefined + this.mfe.modificationDate = undefined } this.changeMode = 'CREATE' } else this.changeMode = 'EDIT' @@ -113,47 +140,109 @@ export class AppDetailComponent implements OnChanges { } }) } - - public viewForm(app?: Microfrontend): void { - if (app) - this.formGroup.setValue({ - appId: app['appId'], - appName: app['appName'], - appVersion: app['appVersion'], - productName: app['productName'], - description: app['description'], - technology: app['technology'], - remoteBaseUrl: app['remoteBaseUrl'], - remoteEntry: app['remoteEntry'], - exposedModule: app['exposedModule'], - classifications: app['classifications'], - contact: app['contact'], - iconName: app['iconName'], - note: app['note'] + public getMs() { + this.loading = true + this.msApi + .getMicroserviceByAppId({ appId: this.appAbstract?.appId } as GetMicroserviceByAppIdRequestParams) + .pipe( + finalize(() => { + this.loading = false + }) + ) + .subscribe({ + next: (data: any) => { + if (data) { + this.ms = data + if (this.ms) this.viewFormMs(this.ms) + if (this.changeMode === 'COPY') { + if (this.ms?.id) { + this.ms.id = undefined + this.ms.creationDate = undefined + this.ms.modificationDate = undefined + } + this.changeMode = 'CREATE' + } else this.changeMode = 'EDIT' + } + } }) } + public viewFormMfe(mfe: Microfrontend): void { + this.formGroupMfe.setValue({ + appId: mfe['appId'], + appName: mfe['appName'], + appVersion: mfe['appVersion'], + productName: mfe['productName'], + description: mfe['description'], + technology: mfe['technology'], + remoteBaseUrl: mfe['remoteBaseUrl'], + remoteEntry: mfe['remoteEntry'], + exposedModule: mfe['exposedModule'], + classifications: mfe['classifications'], + contact: mfe['contact'], + iconName: mfe['iconName'], + note: mfe['note'] + }) + } + public viewFormMs(ms: Microservice): void { + this.formGroupMs.setValue({ + appId: ms['appId'], + appName: ms['appName'], + appVersion: ms['appVersion'], + productName: ms['productName'], + description: ms['description'] + }) + } + public onDialogHide() { - this.displayDetailDialogChange.emit(false) + this.displayDialogChange.emit(false) + this.appChanged.emit(false) } + public onSave() { - if (!this.formGroup.valid) { - this.msgService.error({ summaryKey: 'VALIDATION.FORM_INVALID' }) - return - } - this.app = { ...this.formGroup.value, id: this.app?.id } - if (this.changeMode === 'CREATE') { - this.createApp() - } else if (this.changeMode === 'EDIT') { - this.updateApp() + if (this.appAbstract?.appType === 'MFE') { + if (!this.formGroupMfe.valid) { + this.msgService.error({ summaryKey: 'VALIDATION.FORM_INVALID' }) + return + } + this.mfe = { ...this.formGroupMfe.value, id: this.mfe?.id } + if (this.changeMode === 'CREATE') { + this.createMfe() + } else if (this.changeMode === 'EDIT') { + this.updateMfe() + } + } else if (this.appAbstract?.appType === 'MS') { + if (!this.formGroupMs.valid) { + this.msgService.error({ summaryKey: 'VALIDATION.FORM_INVALID' }) + return + } + this.ms = { ...this.formGroupMs.value, id: this.ms?.id } + if (this.changeMode === 'CREATE') { + this.createMs() + } else if (this.changeMode === 'EDIT') { + this.updateMs() + } } } - private createApp() { - this.appApi.createMicrofrontend({ createMicrofrontendRequest: this.app as CreateMicrofrontendRequest }).subscribe({ + private createMfe() { + this.mfeApi.createMicrofrontend({ createMicrofrontendRequest: this.mfe as CreateMicrofrontendRequest }).subscribe({ + next: () => { + this.msgService.success({ summaryKey: 'ACTIONS.CREATE.APP.OK' }) + this.displayDialogChange.emit(true) + this.appChanged.emit(true) + }, + error: (err) => { + this.displaySaveError(err) + } + }) + } + private createMs() { + this.msApi.createMicroservice({ createMicroserviceRequest: this.ms as CreateMicroserviceRequest }).subscribe({ next: () => { this.msgService.success({ summaryKey: 'ACTIONS.CREATE.APP.OK' }) - this.displayDetailDialogChange.emit(false) + this.displayDialogChange.emit(true) + this.appChanged.emit(true) }, error: (err) => { this.displaySaveError(err) @@ -161,16 +250,34 @@ export class AppDetailComponent implements OnChanges { }) } - private updateApp() { - this.appApi + private updateMfe() { + this.mfeApi .updateMicrofrontend({ - id: this.app?.id ?? '', - updateMicrofrontendRequest: this.app as UpdateMicrofrontendRequest + id: this.mfe?.id ?? '', + updateMicrofrontendRequest: this.mfe as UpdateMicrofrontendRequest + }) + .subscribe({ + next: () => { + this.msgService.success({ summaryKey: 'ACTIONS.EDIT.APP.OK' }) + //this.displayDialogChange.emit(false) + this.appChanged.emit(true) + }, + error: (err) => { + this.displaySaveError(err) + } + }) + } + private updateMs() { + this.msApi + .updateMicroservice({ + id: this.ms?.id ?? '', + updateMicroserviceRequest: this.ms as UpdateMicroserviceRequest }) .subscribe({ next: () => { this.msgService.success({ summaryKey: 'ACTIONS.EDIT.APP.OK' }) - this.displayDetailDialogChange.emit(false) + this.displayDialogChange.emit(true) + this.appChanged.emit(true) }, error: (err) => { this.displaySaveError(err) diff --git a/src/app/product-store/app-search/app-search.component.html b/src/app/product-store/app-search/app-search.component.html index 323c28d..32b4bd1 100644 --- a/src/app/product-store/app-search/app-search.component.html +++ b/src/app/product-store/app-search/app-search.component.html @@ -1,4 +1,4 @@ - + - +
- + - +
-
- -
- - - - + + + + +
+
- - -
-
- - - -
-
{{ limitText(app.appId, 25) }}
-
{{ limitText(app.appName, 30) }}
-
{{ limitText(app.productName, 25) }}
-
+ + + + +
+
{{ limitText(app.appId, 25) }}
+
{{ app.appType }}
+
{{ limitText(app.productName, 25) }}
- - - +
+
+ - - + diff --git a/src/app/product-store/app-search/app-search.component.spec.ts b/src/app/product-store/app-search/app-search.component.spec.ts index cf174db..68960d2 100644 --- a/src/app/product-store/app-search/app-search.component.spec.ts +++ b/src/app/product-store/app-search/app-search.component.spec.ts @@ -32,9 +32,9 @@ describe('AppSearchComponent', () => { getValue: jasmine.createSpy('getValue').and.returnValue('en') }, hasPermission: jasmine.createSpy('hasPermission').and.callFake((permissionName) => { - if (permissionName === 'MICROFRONTEND#CREATE') { + if (permissionName === 'APP#CREATE') { return true - } else if (permissionName === 'MICROFRONTEND#EDIT') { + } else if (permissionName === 'APP#EDIT') { return true } else { return false diff --git a/src/app/product-store/app-search/app-search.component.ts b/src/app/product-store/app-search/app-search.component.ts index 1960d29..5bcbc0c 100644 --- a/src/app/product-store/app-search/app-search.component.ts +++ b/src/app/product-store/app-search/app-search.component.ts @@ -1,30 +1,49 @@ -import { Component, OnInit, ViewChild } from '@angular/core' +import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core' +import { HttpErrorResponse } from '@angular/common/http' import { FormControl, FormGroup } from '@angular/forms' import { ActivatedRoute, Router } from '@angular/router' import { TranslateService } from '@ngx-translate/core' import { DataView } from 'primeng/dataview' -import { Observable, finalize, map } from 'rxjs' +import { combineLatest, finalize, map, Observable, Subject, takeUntil } from 'rxjs' import { Action, DataViewControlTranslations, UserService } from '@onecx/portal-integration-angular' -import { MicrofrontendAbstract, MicrofrontendPageResult, MicrofrontendsAPIService } from 'src/app/shared/generated' +import { + MicrofrontendAbstract, + MicrofrontendPageResult, + MicrofrontendsAPIService, + Microservice, + MicroservicePageResult, + MicroservicesAPIService +} from 'src/app/shared/generated' import { limitText } from 'src/app/shared/utils' import { ChangeMode } from '../app-detail/app-detail.component' -export interface MicrofrontendSearchCriteria { +export interface AppSearchCriteria { appId: FormControl appName: FormControl productName: FormControl } +export type AppType = 'MS' | 'MFE' +export type AppAbstract = MicrofrontendAbstract & Microservice & { appType: AppType } @Component({ templateUrl: './app-search.component.html', styleUrls: ['./app-search.component.scss'] }) -export class AppSearchComponent implements OnInit { - public apps$!: Observable - public app: MicrofrontendAbstract | undefined - public appSearchCriteriaGroup!: FormGroup +export class AppSearchComponent implements OnInit, OnDestroy { + private readonly destroy$ = new Subject() + private readonly debug = true // to be removed after finalization + public dataAccessIssue = false + public exceptionKey = '' + public loading = true public actions$: Observable | undefined + + public apps$!: Observable + public mfes$!: Observable + public mss$!: Observable + public apps: AppAbstract[] = [] + public app: AppAbstract | undefined + public appSearchCriteriaGroup!: FormGroup public viewMode = 'grid' public changeMode: ChangeMode = 'VIEW' public filter: string | undefined @@ -44,12 +63,13 @@ export class AppSearchComponent implements OnInit { private route: ActivatedRoute, private router: Router, private user: UserService, - private appApi: MicrofrontendsAPIService, + private mfeApi: MicrofrontendsAPIService, + private msApi: MicroservicesAPIService, private translate: TranslateService ) { - this.hasCreatePermission = this.user.hasPermission('MICROFRONTEND#CREATE') - this.hasDeletePermission = this.user.hasPermission('MICROFRONTEND#DELETE') - this.appSearchCriteriaGroup = new FormGroup({ + this.hasCreatePermission = this.user.hasPermission('APP#CREATE') + this.hasDeletePermission = this.user.hasPermission('APP#DELETE') + this.appSearchCriteriaGroup = new FormGroup({ appId: new FormControl(null), appName: new FormControl(null), productName: new FormControl(null) @@ -58,27 +78,80 @@ export class AppSearchComponent implements OnInit { ngOnInit(): void { this.prepareActionButtons() + this.apps = [{ id: 'id', appId: '123', appType: 'MS', productName: 'test', description: 'bla' } as AppAbstract] this.searchApps() } + public ngOnDestroy(): void { + this.destroy$.next(undefined) + this.destroy$.complete() + } + private log(text: string, obj?: object): void { + if (this.debug) { + if (obj) console.log('app search: ' + text, obj) + else console.log('app search: ' + text) + } + } public searchApps(): void { this.searchInProgress = true - this.apps$ = this.appApi + this.mfes$ = this.mfeApi .searchMicrofrontends({ - microfrontendSearchCriteria: { + mfeAndMsSearchCriteria: { + appId: this.appSearchCriteriaGroup.controls['appId'].value, + appName: this.appSearchCriteriaGroup.controls['appName'].value, + productName: this.appSearchCriteriaGroup.controls['productName'].value, + pageSize: 100 + } + }) + .pipe(finalize(() => (this.searchInProgress = false))) + + this.mss$ = this.msApi + .searchMicroservice({ + mfeAndMsSearchCriteria: { appId: this.appSearchCriteriaGroup.controls['appId'].value, appName: this.appSearchCriteriaGroup.controls['appName'].value, productName: this.appSearchCriteriaGroup.controls['productName'].value, - pageSize: 1000 + pageSize: 100 } }) .pipe(finalize(() => (this.searchInProgress = false))) + + this.apps = [] + combineLatest(this.mfes$, this.mss$) + .pipe(takeUntil(this.destroy$)) + .subscribe(([microfrontends, microservices]) => { + // mfe + if (microfrontends instanceof HttpErrorResponse) { + this.dataAccessIssue = true + this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + microfrontends.status + '.WORKSPACES' + console.error('searchMicrofrontends():', microfrontends) + } else if (microfrontends instanceof Object) { + if (microfrontends?.stream) + for (let app of microfrontends.stream) this.apps?.push({ ...app, appType: 'MFE' } as AppAbstract) + this.log('searchMicrofrontends():', microfrontends.stream) + } else console.error('searchMicrofrontends() => unknown response:', microfrontends) + // ms + if (!this.dataAccessIssue) { + if (microservices instanceof HttpErrorResponse) { + this.dataAccessIssue = true + this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + microservices.status + '.WORKSPACES' + console.error('searchMicroservice():', microservices) + } else if (microservices instanceof Object) { + if (microservices?.stream) + for (let app of microservices.stream) this.apps?.push({ ...app, appType: 'MS' } as AppAbstract) + this.log('searchMicroservice():', microservices?.stream) + } else console.error('searchMicroservice() => unknown response:', microservices) + } + this.log('searchApps():', this.apps) + this.loading = false + }) } private prepareActionButtons(): void { this.actions$ = this.translate .get([ - 'ACTIONS.CREATE.LABEL', + 'ACTIONS.CREATE.MFE.LABEL', + 'ACTIONS.CREATE.MS.LABEL', 'ACTIONS.CREATE.APP.TOOLTIP', 'ACTIONS.NAVIGATION.BACK', 'ACTIONS.NAVIGATION.BACK.TOOLTIP' @@ -94,10 +167,18 @@ export class AppSearchComponent implements OnInit { show: 'always' }, { - label: data['ACTIONS.CREATE.LABEL'], + label: data['ACTIONS.CREATE.MFE.LABEL'], + title: data['ACTIONS.CREATE.APP.TOOLTIP'], + actionCallback: () => this.onCreate('MFE'), + permission: 'APP#CREATE', + icon: 'pi pi-plus', + show: 'always' + }, + { + label: data['ACTIONS.CREATE.MS.LABEL'], title: data['ACTIONS.CREATE.APP.TOOLTIP'], - actionCallback: () => this.onCreate(), - permission: 'MICROFRONTEND#CREATE', + actionCallback: () => this.onCreate('MS'), + permission: 'APP#CREATE', icon: 'pi pi-plus', show: 'always' } @@ -133,26 +214,37 @@ export class AppSearchComponent implements OnInit { this.router.navigate(['../', product], { relativeTo: this.route }) } - public onDetail(ev: any, app: MicrofrontendAbstract) { + public onDetail(ev: any, app: AppAbstract) { ev.stopPropagation() this.app = app this.changeMode = 'EDIT' this.displayDetailDialog = true } - public onCopy(ev: any, app: MicrofrontendAbstract) { + public onCopy(ev: any, app: AppAbstract) { ev.stopPropagation() this.app = app this.changeMode = 'COPY' this.displayDetailDialog = true } - public onCreate() { + public onCreate(type: AppType) { this.changeMode = 'CREATE' - this.app = undefined + this.app = { appType: type } as AppAbstract this.displayDetailDialog = true } - public onDelete(ev: any, app: MicrofrontendAbstract) { + public onDelete(ev: any, app: AppAbstract) { ev.stopPropagation() this.app = app this.displayDeleteDialog = true } + + public appChanged(changed: any) { + this.displayDetailDialog = false + if (changed) this.searchApps() + } + public appDeleted(deleted: any) { + this.displayDeleteDialog = false + if (deleted && this.app?.id) { + this.apps = this.apps.filter((app) => app.id !== this.app?.id) + } + } } diff --git a/src/app/product-store/product-detail/product-apps/product-apps.component.html b/src/app/product-store/product-detail/product-apps/product-apps.component.html index 5191ef1..662f695 100644 --- a/src/app/product-store/product-detail/product-apps/product-apps.component.html +++ b/src/app/product-store/product-detail/product-apps/product-apps.component.html @@ -23,12 +23,12 @@ [enableSorting]="true" [supportedViews]="['grid']" [initialViewMode]="viewMode" - [sortingOptions]="[{ label: 'MFE.APP_NAME' | translate, value: 'app_name' }]" + [sortingOptions]="[{ label: 'APP.APP_NAME' | translate, value: 'app_name' }]" [defaultSortOption]="sortField" [defaultSortDirection]="sortOrder === 1" (dataViewChange)="onLayoutChange($event)" (filterChange)="onFilterChange($event)" - [filterColumns]="['MFE.APP_NAME' | translate]" + [filterColumns]="['APP.APP_NAME' | translate]" (sortChange)="onSortChange($event)" (sortDirectionChange)="onSortDirChange($event)" [translations]="dataViewControlsTranslations" @@ -64,9 +64,14 @@ - + diff --git a/src/app/product-store/product-detail/product-apps/product-apps.component.ts b/src/app/product-store/product-detail/product-apps/product-apps.component.ts index 1b79fee..60f7d6b 100644 --- a/src/app/product-store/product-detail/product-apps/product-apps.component.ts +++ b/src/app/product-store/product-detail/product-apps/product-apps.component.ts @@ -7,13 +7,17 @@ import { Product, MicrofrontendsAPIService, MicrofrontendPageResult, - MicrofrontendAbstract + MicrofrontendAbstract, + Microservice } from 'src/app/shared/generated' import { dropDownSortItemsByLabel, limitText } from 'src/app/shared/utils' import { IconService } from 'src/app/shared/iconservice' import { ChangeMode } from '../../app-detail/app-detail.component' +export type AppType = 'MS' | 'MFE' +export type AppAbstract = MicrofrontendAbstract & Microservice & { appType: AppType } + @Component({ selector: 'app-product-apps', templateUrl: './product-apps.component.html', @@ -23,8 +27,10 @@ export class ProductAppsComponent implements OnChanges { @Input() product: Product | undefined @Input() dateFormat = 'medium' @Input() changeMode: ChangeMode = 'VIEW' + + private readonly debug = true // to be removed after finalization public apps$!: Observable - public app: MicrofrontendAbstract | undefined + public app: AppAbstract | undefined public iconItems: SelectItem[] = [{ label: '', value: null }] public filter: string | undefined public viewMode = 'grid' @@ -41,21 +47,28 @@ export class ProductAppsComponent implements OnChanges { public limitText = limitText constructor(private icon: IconService, private user: UserService, private appApi: MicrofrontendsAPIService) { - this.hasCreatePermission = this.user.hasPermission('MICROFRONTEND#CREATE') - this.hasDeletePermission = this.user.hasPermission('MICROFRONTEND#DELETE') + this.hasCreatePermission = this.user.hasPermission('APP#CREATE') + this.hasDeletePermission = this.user.hasPermission('APP#DELETE') this.iconItems.push(...this.icon.icons.map((i) => ({ label: i, value: i }))) this.iconItems.sort(dropDownSortItemsByLabel) } ngOnChanges(): void { - if (this.product) this.loadApps() + if (this.product) this.searchApps() + } + + private log(text: string, obj?: object): void { + if (this.debug) { + if (obj) console.log('app search: ' + text, obj) + else console.log('app search: ' + text) + } } - public loadApps(): void { + public searchApps(): void { this.searchInProgress = true this.apps$ = this.appApi .searchMicrofrontends({ - microfrontendSearchCriteria: { productName: this.product?.name, pageSize: 1000 } + mfeAndMsSearchCriteria: { productName: this.product?.name, pageSize: 100 } }) .pipe(finalize(() => (this.searchInProgress = false))) } @@ -74,26 +87,34 @@ export class ProductAppsComponent implements OnChanges { this.sortOrder = asc ? -1 : 1 } - public onDetail(ev: any, app: MicrofrontendAbstract) { + public onDetail(ev: any, app: AppAbstract) { ev.stopPropagation() - this.app = app + //this.app = app this.changeMode = 'EDIT' this.displayDetailDialog = true } - public onCopy(ev: any, app: MicrofrontendAbstract) { + public onCopy(ev: any, app: AppAbstract) { ev.stopPropagation() - this.app = app + //this.app = app this.changeMode = 'COPY' this.displayDetailDialog = true } public onCreate() { this.changeMode = 'CREATE' - this.app = undefined + //this.app = undefined this.displayDetailDialog = true } - public onDelete(ev: any, app: MicrofrontendAbstract) { + public onDelete(ev: any, app: AppAbstract) { ev.stopPropagation() - this.app = app + // this.app = app this.displayDeleteDialog = true } + public appChanged(changed: any) { + this.displayDetailDialog = false + if (changed) this.searchApps() + } + public appDeleted(deleted: any) { + this.displayDeleteDialog = false + if (deleted) this.searchApps() + } } diff --git a/src/app/product-store/product-detail/product.detail.component.spec.ts b/src/app/product-store/product-detail/product.detail.component.spec.ts index b0a6384..feee0a1 100644 --- a/src/app/product-store/product-detail/product.detail.component.spec.ts +++ b/src/app/product-store/product-detail/product.detail.component.spec.ts @@ -48,9 +48,9 @@ describe('ProductDetailComponent', () => { getValue: jasmine.createSpy('getValue').and.returnValue('en') }, hasPermission: jasmine.createSpy('hasPermission').and.callFake((permissionName) => { - if (permissionName === 'MICROFRONTEND#CREATE') { + if (permissionName === 'APP#CREATE') { return true - } else if (permissionName === 'MICROFRONTEND#EDIT') { + } else if (permissionName === 'APP#EDIT') { return true } else { return false diff --git a/src/app/product-store/product-search/product-search.component.ts b/src/app/product-store/product-search/product-search.component.ts index c64dba6..3c8c3f8 100644 --- a/src/app/product-store/product-search/product-search.component.ts +++ b/src/app/product-store/product-search/product-search.component.ts @@ -94,7 +94,7 @@ export class ProductSearchComponent implements OnInit { private prepareActionButtons(): void { this.actions$ = this.translate .get([ - 'ACTIONS.CREATE.LABEL', + 'ACTIONS.CREATE.PRODUCT.LABEL', 'ACTIONS.CREATE.PRODUCT.TOOLTIP', 'DIALOG.SEARCH.APPS.LABEL', 'DIALOG.SEARCH.APPS.TOOLTIP' @@ -106,12 +106,12 @@ export class ProductSearchComponent implements OnInit { label: data['DIALOG.SEARCH.APPS.LABEL'], title: data['DIALOG.SEARCH.APPS.TOOLTIP'], actionCallback: () => this.onAppSearch(), - permission: 'MICROFRONTEND#SEARCH', + permission: 'APP#SEARCH', icon: 'pi pi-cog', show: 'always' }, { - label: data['ACTIONS.CREATE.LABEL'], + label: data['ACTIONS.CREATE.PRODUCT.LABEL'], title: data['ACTIONS.CREATE.PRODUCT.TOOLTIP'], actionCallback: () => this.onNewProduct(), permission: 'PRODUCT#CREATE', diff --git a/src/app/shared/generated/.openapi-generator/FILES b/src/app/shared/generated/.openapi-generator/FILES index d227640..63e82d8 100644 --- a/src/app/shared/generated/.openapi-generator/FILES +++ b/src/app/shared/generated/.openapi-generator/FILES @@ -4,18 +4,22 @@ README.md api.module.ts api/api.ts api/microfrontends.service.ts +api/microservices.service.ts api/products.service.ts configuration.ts encoder.ts git_push.sh index.ts model/createMicrofrontendRequest.ts +model/createMicroserviceRequest.ts model/createProductRequest.ts model/createUIEndpoint.ts +model/mfeAndMsSearchCriteria.ts model/microfrontend.ts model/microfrontendAbstract.ts model/microfrontendPageResult.ts -model/microfrontendSearchCriteria.ts +model/microservice.ts +model/microservicePageResult.ts model/models.ts model/problemDetailInvalidParam.ts model/problemDetailParam.ts @@ -27,6 +31,7 @@ model/productPageResult.ts model/productSearchCriteria.ts model/uIEndpoint.ts model/updateMicrofrontendRequest.ts +model/updateMicroserviceRequest.ts model/updateProductRequest.ts model/updateUIEndpoint.ts param.ts diff --git a/src/app/shared/generated/api/api.ts b/src/app/shared/generated/api/api.ts index cb8fcc6..46b1228 100644 --- a/src/app/shared/generated/api/api.ts +++ b/src/app/shared/generated/api/api.ts @@ -1,5 +1,7 @@ export * from './microfrontends.service'; import { MicrofrontendsAPIService } from './microfrontends.service'; +export * from './microservices.service'; +import { MicroservicesAPIService } from './microservices.service'; export * from './products.service'; import { ProductsAPIService } from './products.service'; -export const APIS = [MicrofrontendsAPIService, ProductsAPIService]; +export const APIS = [MicrofrontendsAPIService, MicroservicesAPIService, ProductsAPIService]; diff --git a/src/app/shared/generated/api/microfrontends.service.ts b/src/app/shared/generated/api/microfrontends.service.ts index 886b31e..17fc29d 100644 --- a/src/app/shared/generated/api/microfrontends.service.ts +++ b/src/app/shared/generated/api/microfrontends.service.ts @@ -21,12 +21,12 @@ import { Observable } from 'rxjs'; // @ts-ignore import { CreateMicrofrontendRequest } from '../model/createMicrofrontendRequest'; // @ts-ignore +import { MfeAndMsSearchCriteria } from '../model/mfeAndMsSearchCriteria'; +// @ts-ignore import { Microfrontend } from '../model/microfrontend'; // @ts-ignore import { MicrofrontendPageResult } from '../model/microfrontendPageResult'; // @ts-ignore -import { MicrofrontendSearchCriteria } from '../model/microfrontendSearchCriteria'; -// @ts-ignore import { ProblemDetailResponse } from '../model/problemDetailResponse'; // @ts-ignore import { UpdateMicrofrontendRequest } from '../model/updateMicrofrontendRequest'; @@ -53,7 +53,7 @@ export interface GetMicrofrontendByAppIdRequestParams { } export interface SearchMicrofrontendsRequestParams { - microfrontendSearchCriteria: MicrofrontendSearchCriteria; + mfeAndMsSearchCriteria: MfeAndMsSearchCriteria; } export interface UpdateMicrofrontendRequestParams { @@ -382,9 +382,9 @@ export class MicrofrontendsAPIService { public searchMicrofrontends(requestParameters: SearchMicrofrontendsRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; public searchMicrofrontends(requestParameters: SearchMicrofrontendsRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; public searchMicrofrontends(requestParameters: SearchMicrofrontendsRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { - const microfrontendSearchCriteria = requestParameters.microfrontendSearchCriteria; - if (microfrontendSearchCriteria === null || microfrontendSearchCriteria === undefined) { - throw new Error('Required parameter microfrontendSearchCriteria was null or undefined when calling searchMicrofrontends.'); + const mfeAndMsSearchCriteria = requestParameters.mfeAndMsSearchCriteria; + if (mfeAndMsSearchCriteria === null || mfeAndMsSearchCriteria === undefined) { + throw new Error('Required parameter mfeAndMsSearchCriteria was null or undefined when calling searchMicrofrontends.'); } let localVarHeaders = this.defaultHeaders; @@ -431,7 +431,7 @@ export class MicrofrontendsAPIService { return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, { context: localVarHttpContext, - body: microfrontendSearchCriteria, + body: mfeAndMsSearchCriteria, responseType: responseType_, withCredentials: this.configuration.withCredentials, headers: localVarHeaders, diff --git a/src/app/shared/generated/api/microservices.service.ts b/src/app/shared/generated/api/microservices.service.ts new file mode 100644 index 0000000..da7c3ff --- /dev/null +++ b/src/app/shared/generated/api/microservices.service.ts @@ -0,0 +1,517 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +// @ts-ignore +import { CreateMicroserviceRequest } from '../model/createMicroserviceRequest'; +// @ts-ignore +import { MfeAndMsSearchCriteria } from '../model/mfeAndMsSearchCriteria'; +// @ts-ignore +import { Microservice } from '../model/microservice'; +// @ts-ignore +import { MicroservicePageResult } from '../model/microservicePageResult'; +// @ts-ignore +import { ProblemDetailResponse } from '../model/problemDetailResponse'; +// @ts-ignore +import { UpdateMicroserviceRequest } from '../model/updateMicroserviceRequest'; + +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; + + +export interface CreateMicroserviceRequestParams { + createMicroserviceRequest: CreateMicroserviceRequest; +} + +export interface DeleteMicroserviceRequestParams { + id: string; +} + +export interface GetMicroserviceRequestParams { + id: string; +} + +export interface GetMicroserviceByAppIdRequestParams { + appId: string; +} + +export interface SearchMicroserviceRequestParams { + mfeAndMsSearchCriteria: MfeAndMsSearchCriteria; +} + +export interface UpdateMicroserviceRequestParams { + id: string; + updateMicroserviceRequest: UpdateMicroserviceRequest; +} + + +@Injectable({ + providedIn: 'any' +}) +export class MicroservicesAPIService { + + protected basePath = 'http://onecx-product-store-bff:8080'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string|string[], @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + if (Array.isArray(basePath) && basePath.length > 0) { + basePath = basePath[0]; + } + + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + // @ts-ignore + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, (value as Date).toISOString().substring(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * Create microservice + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public createMicroservice(requestParameters: CreateMicroserviceRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public createMicroservice(requestParameters: CreateMicroserviceRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public createMicroservice(requestParameters: CreateMicroserviceRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public createMicroservice(requestParameters: CreateMicroserviceRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const createMicroserviceRequest = requestParameters.createMicroserviceRequest; + if (createMicroserviceRequest === null || createMicroserviceRequest === undefined) { + throw new Error('Required parameter createMicroserviceRequest was null or undefined when calling createMicroservice.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + // to determine the Content-Type header + const consumes: string[] = [ + 'application/json' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); + } + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/microservices`; + return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + body: createMicroserviceRequest, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Delete microservice by ID + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public deleteMicroservice(requestParameters: DeleteMicroserviceRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public deleteMicroservice(requestParameters: DeleteMicroserviceRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public deleteMicroservice(requestParameters: DeleteMicroserviceRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public deleteMicroservice(requestParameters: DeleteMicroserviceRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const id = requestParameters.id; + if (id === null || id === undefined) { + throw new Error('Required parameter id was null or undefined when calling deleteMicroservice.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/microservices/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`; + return this.httpClient.request('delete', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Return microservices by ID + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getMicroservice(requestParameters: GetMicroserviceRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public getMicroservice(requestParameters: GetMicroserviceRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public getMicroservice(requestParameters: GetMicroserviceRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public getMicroservice(requestParameters: GetMicroserviceRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const id = requestParameters.id; + if (id === null || id === undefined) { + throw new Error('Required parameter id was null or undefined when calling getMicroservice.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/microservices/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`; + return this.httpClient.request('get', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Return microservice by its appId + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getMicroserviceByAppId(requestParameters: GetMicroserviceByAppIdRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public getMicroserviceByAppId(requestParameters: GetMicroserviceByAppIdRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public getMicroserviceByAppId(requestParameters: GetMicroserviceByAppIdRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public getMicroserviceByAppId(requestParameters: GetMicroserviceByAppIdRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const appId = requestParameters.appId; + if (appId === null || appId === undefined) { + throw new Error('Required parameter appId was null or undefined when calling getMicroserviceByAppId.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/microservices/appId/${this.configuration.encodeParam({name: "appId", value: appId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`; + return this.httpClient.request('get', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Search for microservices by search criteria + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public searchMicroservice(requestParameters: SearchMicroserviceRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public searchMicroservice(requestParameters: SearchMicroserviceRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public searchMicroservice(requestParameters: SearchMicroserviceRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public searchMicroservice(requestParameters: SearchMicroserviceRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const mfeAndMsSearchCriteria = requestParameters.mfeAndMsSearchCriteria; + if (mfeAndMsSearchCriteria === null || mfeAndMsSearchCriteria === undefined) { + throw new Error('Required parameter mfeAndMsSearchCriteria was null or undefined when calling searchMicroservice.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + // to determine the Content-Type header + const consumes: string[] = [ + 'application/json' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); + } + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/microservices/search`; + return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + body: mfeAndMsSearchCriteria, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Update microservice by ID + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public updateMicroservice(requestParameters: UpdateMicroserviceRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public updateMicroservice(requestParameters: UpdateMicroserviceRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public updateMicroservice(requestParameters: UpdateMicroserviceRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public updateMicroservice(requestParameters: UpdateMicroserviceRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const id = requestParameters.id; + if (id === null || id === undefined) { + throw new Error('Required parameter id was null or undefined when calling updateMicroservice.'); + } + const updateMicroserviceRequest = requestParameters.updateMicroserviceRequest; + if (updateMicroserviceRequest === null || updateMicroserviceRequest === undefined) { + throw new Error('Required parameter updateMicroserviceRequest was null or undefined when calling updateMicroservice.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + // to determine the Content-Type header + const consumes: string[] = [ + 'application/json' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); + } + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/microservices/${this.configuration.encodeParam({name: "id", value: id, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`; + return this.httpClient.request('put', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + body: updateMicroserviceRequest, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + +} diff --git a/src/app/shared/generated/model/createMicroserviceRequest.ts b/src/app/shared/generated/model/createMicroserviceRequest.ts new file mode 100644 index 0000000..438ead1 --- /dev/null +++ b/src/app/shared/generated/model/createMicroserviceRequest.ts @@ -0,0 +1,22 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface CreateMicroserviceRequest { + appId: string; + appVersion?: string; + appName?: string; + description?: string; + productName: string; + type?: string; +} + diff --git a/src/app/shared/generated/model/microfrontendSearchCriteria.ts b/src/app/shared/generated/model/mfeAndMsSearchCriteria.ts similarity index 94% rename from src/app/shared/generated/model/microfrontendSearchCriteria.ts rename to src/app/shared/generated/model/mfeAndMsSearchCriteria.ts index dd4211f..76cf741 100644 --- a/src/app/shared/generated/model/microfrontendSearchCriteria.ts +++ b/src/app/shared/generated/model/mfeAndMsSearchCriteria.ts @@ -11,7 +11,7 @@ */ -export interface MicrofrontendSearchCriteria { +export interface MfeAndMsSearchCriteria { appId?: string | null; appName?: string | null; productName?: string | null; diff --git a/src/app/shared/generated/model/microservice.ts b/src/app/shared/generated/model/microservice.ts new file mode 100644 index 0000000..23add62 --- /dev/null +++ b/src/app/shared/generated/model/microservice.ts @@ -0,0 +1,27 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface Microservice { + id?: string; + creationDate?: string; + creationUser?: string; + modificationDate?: string; + modificationUser?: string; + modificationCount?: number; + appId?: string; + appVersion?: string; + appName?: string; + description?: string; + productName?: string; +} + diff --git a/src/app/shared/generated/model/microservicePageResult.ts b/src/app/shared/generated/model/microservicePageResult.ts new file mode 100644 index 0000000..09b2b6a --- /dev/null +++ b/src/app/shared/generated/model/microservicePageResult.ts @@ -0,0 +1,25 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { Microservice } from './microservice'; + + +export interface MicroservicePageResult { + /** + * The total elements in the resource. + */ + totalElements?: number; + number?: number; + size?: number; + totalPages?: number; + stream?: Array; +} + diff --git a/src/app/shared/generated/model/models.ts b/src/app/shared/generated/model/models.ts index 6edd837..c638d8e 100644 --- a/src/app/shared/generated/model/models.ts +++ b/src/app/shared/generated/model/models.ts @@ -1,10 +1,13 @@ export * from './createMicrofrontendRequest'; +export * from './createMicroserviceRequest'; export * from './createProductRequest'; export * from './createUIEndpoint'; +export * from './mfeAndMsSearchCriteria'; export * from './microfrontend'; export * from './microfrontendAbstract'; export * from './microfrontendPageResult'; -export * from './microfrontendSearchCriteria'; +export * from './microservice'; +export * from './microservicePageResult'; export * from './problemDetailInvalidParam'; export * from './problemDetailParam'; export * from './problemDetailResponse'; @@ -15,5 +18,6 @@ export * from './productPageResult'; export * from './productSearchCriteria'; export * from './uIEndpoint'; export * from './updateMicrofrontendRequest'; +export * from './updateMicroserviceRequest'; export * from './updateProductRequest'; export * from './updateUIEndpoint'; diff --git a/src/app/shared/generated/model/updateMicroserviceRequest.ts b/src/app/shared/generated/model/updateMicroserviceRequest.ts new file mode 100644 index 0000000..a08863b --- /dev/null +++ b/src/app/shared/generated/model/updateMicroserviceRequest.ts @@ -0,0 +1,22 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface UpdateMicroserviceRequest { + appId?: string; + appVersion?: string; + appName?: string; + description?: string; + productName?: string; + type?: string; +} + diff --git a/src/assets/api/product-store-bff-api.yaml b/src/assets/api/openapi-bff.yaml similarity index 74% rename from src/assets/api/product-store-bff-api.yaml rename to src/assets/api/openapi-bff.yaml index 8e30b1a..150fedd 100644 --- a/src/assets/api/product-store-bff-api.yaml +++ b/src/assets/api/openapi-bff.yaml @@ -16,6 +16,10 @@ tags: paths: /microfrontends: post: + x-onecx: + permissions: + product-store: + - write tags: - Microfrontends description: Create microfrontend @@ -47,6 +51,10 @@ paths: $ref: '#/components/schemas/ProblemDetailResponse' /microfrontends/search: post: + x-onecx: + permissions: + product-store: + - read tags: - Microfrontends description: Search for microfrontend(s) by search criteria @@ -56,7 +64,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/MicrofrontendSearchCriteria' + $ref: '#/components/schemas/MfeAndMsSearchCriteria' responses: '200': description: Corresponding microfrontends @@ -72,6 +80,10 @@ paths: $ref: '#/components/schemas/ProblemDetailResponse' /microfrontends/appId/{appId}: get: + x-onecx: + permissions: + product-store: + - read tags: - Microfrontends description: Return micro-frontend by its appId @@ -93,6 +105,10 @@ paths: description: Not Found /microfrontends/{id}: get: + x-onecx: + permissions: + product-store: + - read tags: - Microfrontends description: Return microfrontend by ID @@ -113,6 +129,10 @@ paths: '404': description: Not Found put: + x-onecx: + permissions: + product-store: + - write tags: - Microfrontends description: Update microfrontend by ID @@ -141,6 +161,10 @@ paths: '404': description: Microfrontend not found delete: + x-onecx: + permissions: + product-store: + - delete tags: - Microfrontends description: Delete microfrontend by ID @@ -162,6 +186,10 @@ paths: $ref: '#/components/schemas/ProblemDetailResponse' /products: post: + x-onecx: + permissions: + product-store: + - write tags: - Products description: Create new product @@ -193,6 +221,10 @@ paths: $ref: '#/components/schemas/ProblemDetailResponse' /products/search: post: + x-onecx: + permissions: + product-store: + - read tags: - Products description: Search for products by search criteria @@ -218,6 +250,10 @@ paths: $ref: '#/components/schemas/ProblemDetailResponse' /products/{id}: get: + x-onecx: + permissions: + product-store: + - read tags: - Products description: Return product by ID @@ -248,6 +284,10 @@ paths: schema: $ref: '#/components/schemas/ProblemDetailResponse' put: + x-onecx: + permissions: + product-store: + - write tags: - Products description: Update product by ID @@ -280,6 +320,10 @@ paths: schema: $ref: '#/components/schemas/ProblemDetailResponse' delete: + x-onecx: + permissions: + product-store: + - delete tags: - Products description: Delete product by ID @@ -301,6 +345,10 @@ paths: $ref: '#/components/schemas/ProblemDetailResponse' /products/name/{name}: get: + x-onecx: + permissions: + product-store: + - read tags: - Products description: Return product by (unique) name @@ -330,6 +378,176 @@ paths: application/json: schema: $ref: '#/components/schemas/ProblemDetailResponse' + /microservices: + post: + x-onecx: + permissions: + product-store: + - write + tags: + - Microservices + description: Create microservice + operationId: createMicroservice + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateMicroserviceRequest' + responses: + '201': + description: New microservice created + headers: + Location: + required: true + schema: + type: string + format: url + content: + application/json: + schema: + $ref: '#/components/schemas/Microservice' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetailResponse' + /microservices/search: + post: + x-onecx: + permissions: + product-store: + - read + tags: + - Microservices + description: Search for microservices by search criteria + operationId: searchMicroservice + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MfeAndMsSearchCriteria' + responses: + '200': + description: Corresponding micro-services + content: + application/json: + schema: + $ref: '#/components/schemas/MicroservicePageResult' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetailResponse' + /microservices/{id}: + x-onecx: + permissions: + product-store: + - read + get: + tags: + - Microservices + description: Return microservices by ID + operationId: getMicroservice + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Microservice' + '404': + description: Not found + put: + x-onecx: + permissions: + product-store: + - write + tags: + - Microservices + description: Update microservice by ID + operationId: updateMicroservice + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateMicroserviceRequest' + responses: + '204': + description: microservice updated + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetailResponse' + '404': + description: microservice not found + delete: + x-onecx: + permissions: + product-store: + - delete + tags: + - Microservices + description: Delete microservice by ID + operationId: deleteMicroservice + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '204': + description: No Content + '400': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetailResponse' + /microservices/appId/{appId}: + x-onecx: + permissions: + product-store: + - read + get: + tags: + - Microservices + description: Return microservice by its appId + operationId: getMicroserviceByAppId + parameters: + - name: appId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Microservice' + '404': + description: Not found components: schemas: ProductPageResult: @@ -601,7 +819,7 @@ components: description: available endpoints which are exposed by MFE items: $ref: '#/components/schemas/UIEndpoint' - MicrofrontendSearchCriteria: + MfeAndMsSearchCriteria: type: object properties: appId: @@ -811,3 +1029,82 @@ components: type: string name: type: string + Microservice: + type: object + properties: + id: + type: string + creationDate: + $ref: '#/components/schemas/OffsetDateTime' + creationUser: + type: string + modificationDate: + $ref: '#/components/schemas/OffsetDateTime' + modificationUser: + type: string + modificationCount: + format: int32 + type: integer + appId: + type: string + appVersion: + type: string + appName: + type: string + description: + type: string + productName: + type: string + UpdateMicroserviceRequest: + type: object + properties: + appId: + type: string + appVersion: + type: string + appName: + type: string + description: + type: string + productName: + type: string + type: + type: string + CreateMicroserviceRequest: + required: + - productName + - appId + type: object + properties: + appId: + type: string + appVersion: + type: string + appName: + type: string + description: + type: string + productName: + type: string + type: + type: string + MicroservicePageResult: + type: object + properties: + totalElements: + format: int64 + description: The total elements in the resource. + type: integer + number: + format: int32 + type: integer + size: + format: int32 + type: integer + totalPages: + format: int64 + type: integer + stream: + type: array + items: + $ref: '#/components/schemas/Microservice' diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 7f3cd62..d43b316 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -17,13 +17,17 @@ }, "CREATE": { "LABEL": "Erstellen", + "MFE.LABEL": "Microfrontend erstellen", + "MS.LABEL": "Microservice erstellen", + "MFE.HEADER": "Microfrontend erstellen", + "MS.HEADER": "Microservice erstellen", "APP": { - "HEADER": "App erstellen", "TOOLTIP": "Eine neue App erstellen", "OK": "Die App wurde erfolgreich erstellt", "NOK": "Die App konnte nicht erstellt werden." }, "PRODUCT": { + "LABEL": "Produkt erstellen", "HEADER": "Produkt erstellen", "TOOLTIP": "Ein neues Produkt erstellen", "ALREADY_EXISTS": "Ein Produkt mit diesem Namen existert schon", @@ -33,9 +37,10 @@ }, "DELETE": { "LABEL": "Löschen", + "MFE.HEADER": "Microfrontend löschen", + "MS.HEADER": "Microservice löschen", "APP": { "TOOLTIP": "App löschen", - "HEADER": "App löschen", "NOK": "App konnte nicht gelöscht werden", "OK": "App erfolgreich gelöscht" }, @@ -50,8 +55,9 @@ }, "EDIT": { "LABEL": "Bearbeiten", + "MFE.HEADER": "Microfrontend Details", + "MS.HEADER": "Microservice Details", "APP": { - "HEADER": "App Details", "TOOLTIP": "Die App-Eigenschaften bearbeiten", "OK": "Die App wurde erfolgreich geändert", "NOK": "Die App konnte nicht geändert werden." @@ -102,9 +108,8 @@ }, "VIEW": { "LABEL": "Ansehen", - "APP": { - "HEADER": "App Details" - }, + "MFE.HEADER": "Microfrontend Details", + "MS.HEADER": "Microservice Details", "PRODUCT": { "HEADER": "Produkt bearbeiten" } @@ -149,19 +154,20 @@ "SEARCH.HEADER": "Product Store", "SEARCH.SUBHEADER": "Produkte und deren Apps verwalten", "DETAIL.SUBHEADER": "Produkt Details", - "SEARCH.APPS.LABEL": "Microfrontends", - "SEARCH.APPS.TOOLTIP": "Microfrontends suchen und anzeigen", - "SEARCH.APPS.HEADER": "Microfrontends", - "SEARCH.APPS.SUBHEADER": "Suchen und verwalten", + "SEARCH.APPS.LABEL": "Apps", + "SEARCH.APPS.TOOLTIP": "Apps suchen und anzeigen", + "SEARCH.APPS.HEADER": "Apps", + "SEARCH.APPS.SUBHEADER": "Suchen und verwalten von Microfrontends und Microservices", "TABS": { "APPS": "Apps", "INTERN": "Intern", "PROPERTIES": "Eigenschaften" } }, - "MFE": { + "APP": { "APP_ID": "App ID", - "APP_NAME": "App-Name", + "APP_NAME": "Name", + "APP_TYPE": "Typ", "APP_VERSION": "Version", "DESCRIPTION": "Beschreibung", "PRODUCT_NAME": "Produkt", @@ -178,11 +184,12 @@ "GROUP.LOCAL_MODULE": "Modul Management Details", "GROUP.INTERNALS": "Weitere Details", "TOOLTIPS": { - "APP_ID": "Eindeutige ID - Basis Bezeichner des Microfrontends", - "APP_NAME": "Name des Microfrontends", + "APP_ID": "Eindeutige ID - Basis Bezeichner der App", + "APP_NAME": "Name der App", + "APP_TYPE": "Typ der App", "APP_VERSION": "Version", "DESCRIPTION": "Beschreibung", - "PRODUCT_NAME": "Name des Produkts dem das Microfrontend zugeordnet ist", + "PRODUCT_NAME": "Name des Produkts dem die App zugeordnet ist", "TECHNOLOGY": "Technologie", "REMOTE_BASE_URL": "Remote Base URL", "REMOTE_ENTRY": "Remote Entry", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index c2c4c9f..25c41fb 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -17,14 +17,18 @@ }, "CREATE": { "LABEL": "Create", + "MFE.LABEL": "Create Microfrontend", + "MS.LABEL": "Create Microservice", + "MFE.HEADER": "Create Microfrontend", + "MS.HEADER": "Create Microservice", "APP": { - "HEADER": "Create App", "TOOLTIP": "Create a new App", "ALREADY_EXISTS": "App with that name already exists", "OK": "The App was created successfully", "NOK": "An error has occurred. The App was not created." }, "PRODUCT": { + "LABEL": "Create Product", "HEADER": "Create Product", "TOOLTIP": "Create a new Product", "ALREADY_EXISTS": "A Product with that name already exists", @@ -34,9 +38,10 @@ }, "DELETE": { "LABEL": "Delete", + "MFE.HEADER": "Delete Microfrontend", + "MS.HEADER": "Delete Microservice", "APP": { "TOOLTIP": "Delete App", - "HEADER": "Delete App", "NOK": "App could not be deleted", "OK": "App deleted successfully" }, @@ -51,8 +56,9 @@ }, "EDIT": { "LABEL": "Edit", + "MFE.HEADER": "Edit Microfrontend", + "MS.HEADER": "Edit Microservice", "APP": { - "HEADER": "Edit App", "TOOLTIP": "Edit App properties", "OK": "Das App wurde erfolgreich geändert", "NOK": "Ein Fehler ist aufgetreten. Das App wurde nicht geändert." @@ -103,9 +109,8 @@ }, "VIEW": { "LABEL": "View", - "APP": { - "HEADER": "App Details" - }, + "MFE.HEADER": "Microfrontend Details", + "MS.HEADER": "Microservice Details", "PRODUCT": { "HEADER": "Edit Product" } @@ -150,19 +155,20 @@ "SEARCH.HEADER": "Product Store", "SEARCH.SUBHEADER": "Viewing and Managing Products and their Apps", "DETAIL.SUBHEADER": "Product Details", - "SEARCH.APPS.LABEL": "Microfrontends", - "SEARCH.APPS.TOOLTIP": "Search and Display Microfrontends", - "SEARCH.APPS.HEADER": "Microfrontends", - "SEARCH.APPS.SUBHEADER": "Searching and Viewing Microfrontends", + "SEARCH.APPS.LABEL": "Aps", + "SEARCH.APPS.TOOLTIP": "Search and Display Apps", + "SEARCH.APPS.HEADER": "Apps", + "SEARCH.APPS.SUBHEADER": "Searching and Viewing of Microfrontends and Microservices", "TABS": { "APPS": "Apps", "INTERN": "Internal", "PROPERTIES": "Properties" } }, - "MFE": { + "APP": { "APP_ID": "App ID", "APP_NAME": "App Name", + "APP_TYPE": "Type", "APP_VERSION": "Version", "DESCRIPTION": "Description", "PRODUCT_NAME": "Product", @@ -179,11 +185,12 @@ "GROUP.LOCAL_MODULE": "Module Management Details", "GROUP.INTERNALS": "More Details", "TOOLTIPS": { - "APP_ID": "Unique ID - base identifier for this Microfrontend", - "APP_NAME": "Business name of the Microfrontend", + "APP_ID": "Unique ID - base identifier for this App", + "APP_NAME": "Business name of the App", + "APP_TYPE": "Type of the App", "APP_VERSION": "Version", "DESCRIPTION": "Description", - "PRODUCT_NAME": "Name of the Produkt the Microfrontend is assigned to", + "PRODUCT_NAME": "Name of the Produkt the App is assigned to", "TECHNOLOGY": "Technology", "REMOTE_BASE_URL": "Remote Base URL", "REMOTE_ENTRY": "Remote Entry", From 72c3c2f6a0ff27544657a401ed389177ae48ed12 Mon Sep 17 00:00:00 2001 From: Henry Taeschner Date: Thu, 22 Feb 2024 10:45:59 +0100 Subject: [PATCH 2/4] feat: fix search of apps --- .../app-detail/app-detail.component.html | 4 +- .../app-detail/app-detail.component.ts | 29 ++++--- .../app-search/app-search.component.html | 2 +- .../app-search/app-search.component.scss | 6 ++ .../app-search/app-search.component.ts | 83 ++++++++++--------- .../product-search.component.ts | 13 +-- 6 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/app/product-store/app-detail/app-detail.component.html b/src/app/product-store/app-detail/app-detail.component.html index c6af714..66dc226 100644 --- a/src/app/product-store/app-detail/app-detail.component.html +++ b/src/app/product-store/app-detail/app-detail.component.html @@ -21,7 +21,7 @@ '600px': '100vw', }" > - + {{ 'ACTIONS.' + this.changeMode + '.MFE.HEADER' | translate }} @@ -293,7 +293,7 @@ -
+ {{ 'ACTIONS.' + this.changeMode + '.MS.HEADER' | translate }} diff --git a/src/app/product-store/app-detail/app-detail.component.ts b/src/app/product-store/app-detail/app-detail.component.ts index abf50f1..d8a2169 100644 --- a/src/app/product-store/app-detail/app-detail.component.ts +++ b/src/app/product-store/app-detail/app-detail.component.ts @@ -17,9 +17,7 @@ import { MicroservicesAPIService, GetMicroserviceByAppIdRequestParams } from 'src/app/shared/generated' -import { AppAbstract } from '../app-search/app-search.component' - -export type ChangeMode = 'VIEW' | 'CREATE' | 'EDIT' | 'COPY' +import { AppAbstract, ChangeMode } from '../app-search/app-search.component' export interface MfeForm { appId: FormControl @@ -57,6 +55,7 @@ export class AppDetailComponent implements OnChanges { @Output() displayDialogChange = new EventEmitter() @Output() appChanged = new EventEmitter() + private debug = true public mfe: Microfrontend | undefined public ms: Microservice | undefined public formGroupMfe: FormGroup @@ -101,16 +100,20 @@ export class AppDetailComponent implements OnChanges { } ngOnChanges() { - if (this.appAbstract?.id && this.displayDialog) { - if (this.changeMode !== 'CREATE') { - if (this.appAbstract.appType === 'MFE') this.getMfe() - if (this.appAbstract.appType === 'MS') this.getMs() - } else if (this.changeMode === 'CREATE') { - this.ms = undefined - this.mfe = undefined - this.formGroupMs.reset() - this.formGroupMfe.reset() - } + if (this.changeMode === 'CREATE') { + this.ms = undefined + this.mfe = undefined + this.formGroupMs.reset() + this.formGroupMfe.reset() + } else if (this.appAbstract?.id) { + if (this.appAbstract.appType === 'MFE') this.getMfe() + if (this.appAbstract.appType === 'MS') this.getMs() + } + } + private log(text: string, obj?: object): void { + if (this.debug) { + if (obj) console.log('app detail: ' + text, obj) + else console.log('app detail: ' + text) } } diff --git a/src/app/product-store/app-search/app-search.component.html b/src/app/product-store/app-search/app-search.component.html index 32b4bd1..317b659 100644 --- a/src/app/product-store/app-search/app-search.component.html +++ b/src/app/product-store/app-search/app-search.component.html @@ -53,7 +53,7 @@ div { min-height: 60px; &.listview-row { diff --git a/src/app/product-store/app-search/app-search.component.ts b/src/app/product-store/app-search/app-search.component.ts index 5bcbc0c..0e151d7 100644 --- a/src/app/product-store/app-search/app-search.component.ts +++ b/src/app/product-store/app-search/app-search.component.ts @@ -1,10 +1,9 @@ import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core' -import { HttpErrorResponse } from '@angular/common/http' import { FormControl, FormGroup } from '@angular/forms' import { ActivatedRoute, Router } from '@angular/router' import { TranslateService } from '@ngx-translate/core' import { DataView } from 'primeng/dataview' -import { combineLatest, finalize, map, Observable, Subject, takeUntil } from 'rxjs' +import { combineLatest, finalize, map, of, Observable, Subject, startWith, catchError } from 'rxjs' import { Action, DataViewControlTranslations, UserService } from '@onecx/portal-integration-angular' import { @@ -16,15 +15,16 @@ import { MicroservicesAPIService } from 'src/app/shared/generated' import { limitText } from 'src/app/shared/utils' -import { ChangeMode } from '../app-detail/app-detail.component' export interface AppSearchCriteria { appId: FormControl appName: FormControl + // appType: FormControl productName: FormControl } export type AppType = 'MS' | 'MFE' export type AppAbstract = MicrofrontendAbstract & Microservice & { appType: AppType } +export type ChangeMode = 'VIEW' | 'CREATE' | 'EDIT' | 'COPY' @Component({ templateUrl: './app-search.component.html', @@ -33,7 +33,6 @@ export type AppAbstract = MicrofrontendAbstract & Microservice & { appType: AppT export class AppSearchComponent implements OnInit, OnDestroy { private readonly destroy$ = new Subject() private readonly debug = true // to be removed after finalization - public dataAccessIssue = false public exceptionKey = '' public loading = true public actions$: Observable | undefined @@ -103,7 +102,15 @@ export class AppSearchComponent implements OnInit, OnDestroy { pageSize: 100 } }) - .pipe(finalize(() => (this.searchInProgress = false))) + .pipe( + startWith({} as MicrofrontendPageResult), + catchError((err) => { + this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + err.status + '.APPS' + console.error('searchMicrofrontends():', err) + return of({} as MicrofrontendPageResult) + }), + finalize(() => (this.searchInProgress = false)) + ) this.mss$ = this.msApi .searchMicroservice({ @@ -114,37 +121,36 @@ export class AppSearchComponent implements OnInit, OnDestroy { pageSize: 100 } }) - .pipe(finalize(() => (this.searchInProgress = false))) + .pipe( + startWith({} as MicroservicePageResult), + catchError((err) => { + this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + err.status + '.APPS' + console.error('searchMicroservice():', err) + return of({} as MicroservicePageResult) + }), + finalize(() => (this.searchInProgress = false)) + ) - this.apps = [] - combineLatest(this.mfes$, this.mss$) - .pipe(takeUntil(this.destroy$)) - .subscribe(([microfrontends, microservices]) => { - // mfe - if (microfrontends instanceof HttpErrorResponse) { - this.dataAccessIssue = true - this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + microfrontends.status + '.WORKSPACES' - console.error('searchMicrofrontends():', microfrontends) - } else if (microfrontends instanceof Object) { - if (microfrontends?.stream) - for (let app of microfrontends.stream) this.apps?.push({ ...app, appType: 'MFE' } as AppAbstract) - this.log('searchMicrofrontends():', microfrontends.stream) - } else console.error('searchMicrofrontends() => unknown response:', microfrontends) - // ms - if (!this.dataAccessIssue) { - if (microservices instanceof HttpErrorResponse) { - this.dataAccessIssue = true - this.exceptionKey = 'EXCEPTIONS.HTTP_STATUS_' + microservices.status + '.WORKSPACES' - console.error('searchMicroservice():', microservices) - } else if (microservices instanceof Object) { - if (microservices?.stream) - for (let app of microservices.stream) this.apps?.push({ ...app, appType: 'MS' } as AppAbstract) - this.log('searchMicroservice():', microservices?.stream) - } else console.error('searchMicroservice() => unknown response:', microservices) - } - this.log('searchApps():', this.apps) - this.loading = false - }) + this.apps$ = combineLatest([ + this.mfes$.pipe( + map((a) => { + return a.stream + ? a.stream?.map((mfe) => { + return { ...mfe, appType: 'MFE' } as AppAbstract + }) + : [] + }) + ), + this.mss$.pipe( + map((a) => { + return a.stream + ? a.stream?.map((ms) => { + return { ...ms, appType: 'MS' } as AppAbstract + }) + : [] + }) + ) + ]).pipe(map(([mfes, mss]) => mfes.concat(mss))) } private prepareActionButtons(): void { @@ -237,14 +243,15 @@ export class AppSearchComponent implements OnInit, OnDestroy { this.displayDeleteDialog = true } + /** + * trigger search after any change on detail level + */ public appChanged(changed: any) { this.displayDetailDialog = false if (changed) this.searchApps() } public appDeleted(deleted: any) { this.displayDeleteDialog = false - if (deleted && this.app?.id) { - this.apps = this.apps.filter((app) => app.id !== this.app?.id) - } + if (deleted) this.searchApps() } } diff --git a/src/app/product-store/product-search/product-search.component.ts b/src/app/product-store/product-search/product-search.component.ts index 3c8c3f8..3df1703 100644 --- a/src/app/product-store/product-search/product-search.component.ts +++ b/src/app/product-store/product-search/product-search.component.ts @@ -46,10 +46,10 @@ export class ProductSearchComponent implements OnInit { ngOnInit(): void { this.prepareDialogTranslations() this.prepareActionButtons() - this.searchData() + this.searchProducts() } - public searchData(): void { + public searchProducts(): void { this.searchInProgress = true this.products$ = this.productApi .searchProducts({ @@ -136,16 +136,17 @@ export class ProductSearchComponent implements OnInit { public onSortDirChange(asc: boolean): void { this.sortOrder = asc ? -1 : 1 } + public onSearch() { - this.searchData() + this.searchProducts() } public onSearchReset() { this.productSearchCriteriaGroup.reset() } - public onNewProduct() { - this.router.navigate(['./new'], { relativeTo: this.route }) - } public onAppSearch() { this.router.navigate(['./apps'], { relativeTo: this.route }) } + public onNewProduct() { + this.router.navigate(['./new'], { relativeTo: this.route }) + } } From 9913650a6e85a8f1baee2a520548377bbd3b1879 Mon Sep 17 00:00:00 2001 From: Henry Taeschner Date: Thu, 22 Feb 2024 10:52:36 +0100 Subject: [PATCH 3/4] feat: fix search of apps --- .../product-detail/product-apps/product-apps.component.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/product-store/product-detail/product-apps/product-apps.component.ts b/src/app/product-store/product-detail/product-apps/product-apps.component.ts index 60f7d6b..39cb459 100644 --- a/src/app/product-store/product-detail/product-apps/product-apps.component.ts +++ b/src/app/product-store/product-detail/product-apps/product-apps.component.ts @@ -13,10 +13,7 @@ import { import { dropDownSortItemsByLabel, limitText } from 'src/app/shared/utils' import { IconService } from 'src/app/shared/iconservice' -import { ChangeMode } from '../../app-detail/app-detail.component' - -export type AppType = 'MS' | 'MFE' -export type AppAbstract = MicrofrontendAbstract & Microservice & { appType: AppType } +import { AppAbstract, ChangeMode } from '../../app-search/app-search.component' @Component({ selector: 'app-product-apps', From ac27cd1b826d38ec7e8e0eb73b3d9c7c696927fc Mon Sep 17 00:00:00 2001 From: Henry Taeschner Date: Thu, 22 Feb 2024 15:08:26 +0100 Subject: [PATCH 4/4] feat: update BFF api --- .../app-detail/app-detail.component.html | 23 +- .../app-detail/app-detail.component.ts | 20 ++ .../app-search/app-search.component.html | 17 +- .../app-search/app-search.component.ts | 2 +- .../product-apps/product-apps.component.ts | 8 +- .../shared/generated/.openapi-generator/FILES | 3 + src/app/shared/generated/api/api.ts | 4 +- .../generated/api/imagesInternal.service.ts | 335 ++++++++++++++++++ src/app/shared/generated/model/imageInfo.ts | 17 + src/app/shared/generated/model/models.ts | 2 + src/app/shared/generated/model/refType.ts | 18 + src/assets/api/openapi-bff.yaml | 132 +++++++ 12 files changed, 565 insertions(+), 16 deletions(-) create mode 100644 src/app/shared/generated/api/imagesInternal.service.ts create mode 100644 src/app/shared/generated/model/imageInfo.ts create mode 100644 src/app/shared/generated/model/refType.ts diff --git a/src/app/product-store/app-detail/app-detail.component.html b/src/app/product-store/app-detail/app-detail.component.html index 66dc226..17f3832 100644 --- a/src/app/product-store/app-detail/app-detail.component.html +++ b/src/app/product-store/app-detail/app-detail.component.html @@ -191,7 +191,26 @@
- + +
+ + {{ ico.label || 'dummy-for-renderer' }} +
+
+ +
+ + {{ ico.label }} +
+
+ +
diff --git a/src/app/product-store/app-detail/app-detail.component.ts b/src/app/product-store/app-detail/app-detail.component.ts index d8a2169..f213f7f 100644 --- a/src/app/product-store/app-detail/app-detail.component.ts +++ b/src/app/product-store/app-detail/app-detail.component.ts @@ -3,8 +3,11 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core import { FormControl, FormGroup, Validators } from '@angular/forms' import { finalize } from 'rxjs' import { TranslateService } from '@ngx-translate/core' +import { SelectItem } from 'primeng/api' import { PortalMessageService, UserService } from '@onecx/portal-integration-angular' +import { IconService } from 'src/app/shared/iconservice' +import { dropDownSortItemsByLabel } from 'src/app/shared/utils' import { CreateMicrofrontendRequest, CreateMicroserviceRequest, @@ -63,9 +66,11 @@ export class AppDetailComponent implements OnChanges { public loading = false public hasCreatePermission = false public hasEditPermission = false + public iconItems: SelectItem[] = [{ label: '', value: null }] // default value is empty constructor( private user: UserService, + private icon: IconService, private msApi: MicroservicesAPIService, private mfeApi: MicrofrontendsAPIService, private msgService: PortalMessageService, @@ -74,6 +79,8 @@ export class AppDetailComponent implements OnChanges { this.hasCreatePermission = this.user.hasPermission('APP#CREATE') this.hasEditPermission = this.user.hasPermission('APP#EDIT') this.dateFormat = this.user.lang$.getValue() === 'de' ? 'dd.MM.yyyy HH:mm:ss' : 'medium' + this.iconItems.push(...this.icon.icons.map((i) => ({ label: i, value: i }))) + this.iconItems.sort(dropDownSortItemsByLabel) this.formGroupMfe = new FormGroup({ appId: new FormControl(null, [Validators.required, Validators.minLength(2), Validators.maxLength(255)]), @@ -209,6 +216,19 @@ export class AppDetailComponent implements OnChanges { return } this.mfe = { ...this.formGroupMfe.value, id: this.mfe?.id } + // manage classifications => is string array + if (this.mfe?.classifications) { + const a: string = this.formGroupMfe.controls['classifications'].value + let ar: Array | undefined = [] + if (ar && a?.length > 0) { + a.toString() + .split(',') + .map((a) => ar?.push(a.trim())) + } else ar = undefined + this.mfe.classifications = ar?.sort() + } else if (this.mfe) { + this.mfe.classifications = undefined + } if (this.changeMode === 'CREATE') { this.createMfe() } else if (this.changeMode === 'EDIT') { diff --git a/src/app/product-store/app-search/app-search.component.html b/src/app/product-store/app-search/app-search.component.html index 317b659..da51079 100644 --- a/src/app/product-store/app-search/app-search.component.html +++ b/src/app/product-store/app-search/app-search.component.html @@ -60,7 +60,7 @@ [rows]="viewMode === 'grid' ? 24 : 10" [layout]="viewMode" [emptyMessage]="'ACTIONS.SEARCH.NOT_FOUND' | translate" - filterBy="appId,appName,productName" + filterBy="appId,appName,appType,productName,classifications" [sortField]="sortField" [sortOrder]="sortOrder" > @@ -84,7 +84,8 @@ 'APP.APP_ID' | translate, 'APP.APP_NAME' | translate, 'APP.APP_TYPE' | translate, - 'APP.PRODUCT_NAME' | translate + 'APP.PRODUCT_NAME' | translate, + 'APP.CLASSIFICATIONS' | translate ]" (sortChange)="onSortChange($event)" (sortDirectionChange)="onSortDirChange($event)" @@ -99,14 +100,14 @@ [class.bg-primary]="app.appType === 'MFE'" > - +
{{ limitText(app.appId, 25) }}
{{ app.appType }}
diff --git a/src/app/product-store/app-search/app-search.component.ts b/src/app/product-store/app-search/app-search.component.ts index 0e151d7..4faa8cb 100644 --- a/src/app/product-store/app-search/app-search.component.ts +++ b/src/app/product-store/app-search/app-search.component.ts @@ -46,7 +46,7 @@ export class AppSearchComponent implements OnInit, OnDestroy { public viewMode = 'grid' public changeMode: ChangeMode = 'VIEW' public filter: string | undefined - public sortField = 'appName' + public sortField = 'appId' public sortOrder = 1 public searchInProgress = false public displayDetailDialog = false diff --git a/src/app/product-store/product-detail/product-apps/product-apps.component.ts b/src/app/product-store/product-detail/product-apps/product-apps.component.ts index 39cb459..84ceefd 100644 --- a/src/app/product-store/product-detail/product-apps/product-apps.component.ts +++ b/src/app/product-store/product-detail/product-apps/product-apps.component.ts @@ -3,13 +3,7 @@ import { SelectItem } from 'primeng/api' import { Observable, finalize } from 'rxjs' import { DataViewControlTranslations, UserService } from '@onecx/portal-integration-angular' -import { - Product, - MicrofrontendsAPIService, - MicrofrontendPageResult, - MicrofrontendAbstract, - Microservice -} from 'src/app/shared/generated' +import { Product, MicrofrontendsAPIService, MicrofrontendPageResult } from 'src/app/shared/generated' import { dropDownSortItemsByLabel, limitText } from 'src/app/shared/utils' import { IconService } from 'src/app/shared/iconservice' diff --git a/src/app/shared/generated/.openapi-generator/FILES b/src/app/shared/generated/.openapi-generator/FILES index 63e82d8..8bf5900 100644 --- a/src/app/shared/generated/.openapi-generator/FILES +++ b/src/app/shared/generated/.openapi-generator/FILES @@ -3,6 +3,7 @@ README.md api.module.ts api/api.ts +api/imagesInternal.service.ts api/microfrontends.service.ts api/microservices.service.ts api/products.service.ts @@ -14,6 +15,7 @@ model/createMicrofrontendRequest.ts model/createMicroserviceRequest.ts model/createProductRequest.ts model/createUIEndpoint.ts +model/imageInfo.ts model/mfeAndMsSearchCriteria.ts model/microfrontend.ts model/microfrontendAbstract.ts @@ -29,6 +31,7 @@ model/productAbstract.ts model/productAndWorkspaces.ts model/productPageResult.ts model/productSearchCriteria.ts +model/refType.ts model/uIEndpoint.ts model/updateMicrofrontendRequest.ts model/updateMicroserviceRequest.ts diff --git a/src/app/shared/generated/api/api.ts b/src/app/shared/generated/api/api.ts index 46b1228..29c608b 100644 --- a/src/app/shared/generated/api/api.ts +++ b/src/app/shared/generated/api/api.ts @@ -1,7 +1,9 @@ +export * from './imagesInternal.service'; +import { ImagesInternalAPIService } from './imagesInternal.service'; export * from './microfrontends.service'; import { MicrofrontendsAPIService } from './microfrontends.service'; export * from './microservices.service'; import { MicroservicesAPIService } from './microservices.service'; export * from './products.service'; import { ProductsAPIService } from './products.service'; -export const APIS = [MicrofrontendsAPIService, MicroservicesAPIService, ProductsAPIService]; +export const APIS = [ImagesInternalAPIService, MicrofrontendsAPIService, MicroservicesAPIService, ProductsAPIService]; diff --git a/src/app/shared/generated/api/imagesInternal.service.ts b/src/app/shared/generated/api/imagesInternal.service.ts new file mode 100644 index 0000000..5526992 --- /dev/null +++ b/src/app/shared/generated/api/imagesInternal.service.ts @@ -0,0 +1,335 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +// @ts-ignore +import { ImageInfo } from '../model/imageInfo'; +// @ts-ignore +import { ProblemDetailResponse } from '../model/problemDetailResponse'; +// @ts-ignore +import { RefType } from '../model/refType'; + +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; + + +export interface GetImageRequestParams { + refId: string; + refType: RefType; +} + +export interface UpdateImageRequestParams { + refId: string; + refType: RefType; + body: Blob; + contentLength?: number; +} + +export interface UploadImageRequestParams { + contentLength: number; + refId: string; + refType: RefType; + body: Blob; +} + + +@Injectable({ + providedIn: 'any' +}) +export class ImagesInternalAPIService { + + protected basePath = 'http://onecx-product-store-bff:8080'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string|string[], @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + if (Array.isArray(basePath) && basePath.length > 0) { + basePath = basePath[0]; + } + + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + // @ts-ignore + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, (value as Date).toISOString().substring(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * Get Image by id + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getImage(requestParameters: GetImageRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'image/*' | 'application/json', context?: HttpContext}): Observable; + public getImage(requestParameters: GetImageRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'image/*' | 'application/json', context?: HttpContext}): Observable>; + public getImage(requestParameters: GetImageRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'image/*' | 'application/json', context?: HttpContext}): Observable>; + public getImage(requestParameters: GetImageRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'image/*' | 'application/json', context?: HttpContext}): Observable { + const refId = requestParameters.refId; + if (refId === null || refId === undefined) { + throw new Error('Required parameter refId was null or undefined when calling getImage.'); + } + const refType = requestParameters.refType; + if (refType === null || refType === undefined) { + throw new Error('Required parameter refType was null or undefined when calling getImage.'); + } + + let localVarHeaders = this.defaultHeaders; + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'image/*', + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + let localVarPath = `/images/${this.configuration.encodeParam({name: "refId", value: refId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/${this.configuration.encodeParam({name: "refType", value: refType, in: "path", style: "simple", explode: false, dataType: "RefType", dataFormat: undefined})}`; + return this.httpClient.request('get', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: "blob", + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * update Images + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public updateImage(requestParameters: UpdateImageRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public updateImage(requestParameters: UpdateImageRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public updateImage(requestParameters: UpdateImageRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public updateImage(requestParameters: UpdateImageRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const refId = requestParameters.refId; + if (refId === null || refId === undefined) { + throw new Error('Required parameter refId was null or undefined when calling updateImage.'); + } + const refType = requestParameters.refType; + if (refType === null || refType === undefined) { + throw new Error('Required parameter refType was null or undefined when calling updateImage.'); + } + const body = requestParameters.body; + if (body === null || body === undefined) { + throw new Error('Required parameter body was null or undefined when calling updateImage.'); + } + const contentLength = requestParameters.contentLength; + + let localVarHeaders = this.defaultHeaders; + if (contentLength !== undefined && contentLength !== null) { + localVarHeaders = localVarHeaders.set('Content-Length', String(contentLength)); + } + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + // to determine the Content-Type header + const consumes: string[] = [ + 'image/*' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); + } + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/images/${this.configuration.encodeParam({name: "refId", value: refId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/${this.configuration.encodeParam({name: "refType", value: refType, in: "path", style: "simple", explode: false, dataType: "RefType", dataFormat: undefined})}`; + return this.httpClient.request('put', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + body: body, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Upload Images + * @param requestParameters + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public uploadImage(requestParameters: UploadImageRequestParams, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable; + public uploadImage(requestParameters: UploadImageRequestParams, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public uploadImage(requestParameters: UploadImageRequestParams, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>; + public uploadImage(requestParameters: UploadImageRequestParams, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable { + const contentLength = requestParameters.contentLength; + if (contentLength === null || contentLength === undefined) { + throw new Error('Required parameter contentLength was null or undefined when calling uploadImage.'); + } + const refId = requestParameters.refId; + if (refId === null || refId === undefined) { + throw new Error('Required parameter refId was null or undefined when calling uploadImage.'); + } + const refType = requestParameters.refType; + if (refType === null || refType === undefined) { + throw new Error('Required parameter refType was null or undefined when calling uploadImage.'); + } + const body = requestParameters.body; + if (body === null || body === undefined) { + throw new Error('Required parameter body was null or undefined when calling uploadImage.'); + } + + let localVarHeaders = this.defaultHeaders; + if (contentLength !== undefined && contentLength !== null) { + localVarHeaders = localVarHeaders.set('Content-Length', String(contentLength)); + } + + let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (localVarHttpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + let localVarHttpContext: HttpContext | undefined = options && options.context; + if (localVarHttpContext === undefined) { + localVarHttpContext = new HttpContext(); + } + + + // to determine the Content-Type header + const consumes: string[] = [ + 'image/*' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); + } + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/images/${this.configuration.encodeParam({name: "refId", value: refId, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/${this.configuration.encodeParam({name: "refType", value: refType, in: "path", style: "simple", explode: false, dataType: "RefType", dataFormat: undefined})}`; + return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + body: body, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + reportProgress: reportProgress + } + ); + } + +} diff --git a/src/app/shared/generated/model/imageInfo.ts b/src/app/shared/generated/model/imageInfo.ts new file mode 100644 index 0000000..c460e8a --- /dev/null +++ b/src/app/shared/generated/model/imageInfo.ts @@ -0,0 +1,17 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ImageInfo { + id?: string; +} + diff --git a/src/app/shared/generated/model/models.ts b/src/app/shared/generated/model/models.ts index c638d8e..fd54bca 100644 --- a/src/app/shared/generated/model/models.ts +++ b/src/app/shared/generated/model/models.ts @@ -2,6 +2,7 @@ export * from './createMicrofrontendRequest'; export * from './createMicroserviceRequest'; export * from './createProductRequest'; export * from './createUIEndpoint'; +export * from './imageInfo'; export * from './mfeAndMsSearchCriteria'; export * from './microfrontend'; export * from './microfrontendAbstract'; @@ -16,6 +17,7 @@ export * from './productAbstract'; export * from './productAndWorkspaces'; export * from './productPageResult'; export * from './productSearchCriteria'; +export * from './refType'; export * from './uIEndpoint'; export * from './updateMicrofrontendRequest'; export * from './updateMicroserviceRequest'; diff --git a/src/app/shared/generated/model/refType.ts b/src/app/shared/generated/model/refType.ts new file mode 100644 index 0000000..d407e81 --- /dev/null +++ b/src/app/shared/generated/model/refType.ts @@ -0,0 +1,18 @@ +/** + * onecx-product-store-bff + * Backend-For-Frontend (BFF) service for onecx product store. With this API you can manage applications (technical microfrontend(s)) and product(s) as logical abstraction. A Product is a versioned cover for a collection of applications (versioned) to be used within workspaces. Microfrontends (applications) which have reference to exposed/registered modules + * + * The version of the OpenAPI document: 1.0.0 + * Contact: tkit_dev@1000kit.org + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export enum RefType { + Logo = 'logo', + Favicon = 'favicon' +} + diff --git a/src/assets/api/openapi-bff.yaml b/src/assets/api/openapi-bff.yaml index 150fedd..a058764 100644 --- a/src/assets/api/openapi-bff.yaml +++ b/src/assets/api/openapi-bff.yaml @@ -548,8 +548,140 @@ paths: $ref: '#/components/schemas/Microservice' '404': description: Not found + /images/{refId}/{refType}: + post: + x-onecx: + permissions: + product-store: + - write + tags: + - imagesInternal + description: Upload Images + parameters: + - in: header + name: Content-Length + required: true + schema: + minimum: 1 + maximum: 110000 + type: integer + - name: refId + in: path + required: true + schema: + type: string + - name: refType + in: path + required: true + schema: + $ref: '#/components/schemas/RefType' + operationId: uploadImage + requestBody: + required: true + content: + image/*: + schema: + type: string + format: binary + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: '#/components/schemas/ImageInfo' + '400': + description: Bad Request + get: + x-onecx: + permissions: + product-store: + - read + tags: + - imagesInternal + description: Get Image by id + operationId: getImage + parameters: + - name: refId + in: path + required: true + schema: + type: string + - name: refType + in: path + required: true + schema: + $ref: '#/components/schemas/RefType' + responses: + '200': + description: OK + content: + image/*: + schema: + type: string + format: binary + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetailResponse' + put: + x-onecx: + permissions: + product-store: + - write + tags: + - imagesInternal + description: update Images + operationId: updateImage + parameters: + - in: header + name: Content-Length + schema: + type: integer + minimum: 1 + maximum: 110000 + - name: refId + in: path + required: true + schema: + type: string + - name: refType + in: path + required: true + schema: + $ref: '#/components/schemas/RefType' + requestBody: + required: true + content: + image/*: + schema: + type: string + format: binary + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ImageInfo' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetailResponse' components: schemas: + RefType: + type: string + enum: [logo, favicon] + ImageInfo: + type: object + properties: + id: + type: string ProductPageResult: type: object properties: