From 2e0a10c9d50488255d394d4087bad46eed01e848 Mon Sep 17 00:00:00 2001 From: jsteenke <146953549+jsteenke@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:56:53 +0100 Subject: [PATCH] feat: images added (#46) --- .../product-props.component.html | 17 +- .../product-props/product-props.component.ts | 127 ++++++- .../product-search.component.html | 4 +- .../product-search.component.ts | 13 +- .../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/product-store-bff-api.yaml | 180 ++++++++++ src/assets/i18n/de.json | 5 +- src/assets/i18n/en.json | 5 +- 13 files changed, 707 insertions(+), 23 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/product-detail/product-props/product-props.component.html b/src/app/product-store/product-detail/product-props/product-props.component.html index 541ef2c..897b283 100644 --- a/src/app/product-store/product-detail/product-props/product-props.component.html +++ b/src/app/product-store/product-detail/product-props/product-props.component.html @@ -53,16 +53,29 @@ [small]="true" [title]="'LOGO.TOOLTIPS.' + (fetchingLogoUrl ? 'IMAGE' : 'PLACEHOLDER') | translate" > +
- + diff --git a/src/app/product-store/product-detail/product-props/product-props.component.ts b/src/app/product-store/product-detail/product-props/product-props.component.ts index 10d6b9c..b7e4bbf 100644 --- a/src/app/product-store/product-detail/product-props/product-props.component.ts +++ b/src/app/product-store/product-detail/product-props/product-props.component.ts @@ -1,9 +1,18 @@ -import { Component, ElementRef, EventEmitter, Input, OnChanges, Output } from '@angular/core' +import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core' import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms' import { SelectItem } from 'primeng/api' import { PortalMessageService } from '@onecx/portal-integration-angular' -import { CreateProductRequest, Product, ProductsAPIService, UpdateProductRequest } from 'src/app/shared/generated' +import { + CreateProductRequest, + GetImageRequestParams, + ImagesInternalAPIService, + Product, + ProductsAPIService, + RefType, + UpdateProductRequest, + UploadImageRequestParams +} from 'src/app/shared/generated' import { IconService } from 'src/app/shared/iconservice' import { dropDownSortItemsByLabel } from 'src/app/shared/utils' import { ChangeMode } from '../product-detail.component' @@ -34,7 +43,7 @@ export function productNameValidator(): ValidatorFn { templateUrl: './product-props.component.html', styleUrls: ['./product-props.component.scss'] }) -export class ProductPropertyComponent implements OnChanges { +export class ProductPropertyComponent implements OnChanges, OnInit { @Input() product: Product | undefined @Input() dateFormat = 'medium' @Input() changeMode: ChangeMode = 'VIEW' @@ -46,12 +55,15 @@ export class ProductPropertyComponent implements OnChanges { public productName: string | null | undefined public fetchingLogoUrl: string | undefined public iconItems: SelectItem[] = [{ label: '', value: null }] + public logoImageWasUploaded: boolean | undefined + //private productNamePattern = '^(?!new$)(.*)$' // matching for valid product names constructor( private icon: IconService, private elements: ElementRef, private productApi: ProductsAPIService, + private imageApi: ImagesInternalAPIService, private msgService: PortalMessageService ) { this.formGroup = new FormGroup({ @@ -75,6 +87,26 @@ export class ProductPropertyComponent implements OnChanges { this.iconItems.sort(dropDownSortItemsByLabel) } + ngOnInit(): void { + let productName = this.formGroup.controls['name'].value! + let requestParametersGet: GetImageRequestParams = { + refId: productName, + refType: RefType.Logo + } + if ( + requestParametersGet.refId === undefined || + requestParametersGet.refId === '' || + requestParametersGet.refId === null + ) { + this.logoImageWasUploaded = false + } else { + this.imageApi.getImage(requestParametersGet).subscribe(() => { + this.logoImageWasUploaded = true + }) + } + this.fetchingLogoUrl = this.getImageUrl() + } + ngOnChanges(): void { if (this.product) { this.formGroup.patchValue({ @@ -104,13 +136,17 @@ export class ProductPropertyComponent implements OnChanges { } private createProduct() { + let imgUrl = this.formGroup.controls['imageUrl'].value + if ((imgUrl == '' || imgUrl == null) && !this.logoImageWasUploaded) { + imgUrl = 'http://pragmaticscrum.info/wp-content/uploads/2016/06/t1.jpg' + } this.productApi .createProduct({ createProductRequest: { name: this.formGroup.value['name'], version: this.formGroup.value['version'], description: this.formGroup.value['description'], - imageUrl: this.formGroup.value['imageUrl'], + imageUrl: imgUrl, basePath: this.formGroup.value['basePath'], displayName: this.formGroup.value['displayName'], iconName: this.formGroup.value['iconName'], @@ -127,6 +163,10 @@ export class ProductPropertyComponent implements OnChanges { } private updateProduct() { + let imgUrl = this.formGroup.controls['imageUrl'].value + if ((imgUrl == '' || imgUrl == null) && !this.logoImageWasUploaded) { + imgUrl = 'http://pragmaticscrum.info/wp-content/uploads/2016/06/t1.jpg' + } this.productApi .updateProduct({ id: this.productId!, @@ -134,7 +174,7 @@ export class ProductPropertyComponent implements OnChanges { name: this.formGroup.value['name'], version: this.formGroup.value['version'], description: this.formGroup.value['description'], - imageUrl: this.formGroup.value['imageUrl'], + imageUrl: imgUrl, basePath: this.formGroup.value['basePath'], displayName: this.formGroup.value['displayName'], iconName: this.formGroup.value['iconName'], @@ -164,19 +204,78 @@ export class ProductPropertyComponent implements OnChanges { /** File Handling */ public onFileUpload(ev: Event, fieldType: 'logo'): void { + let productName = this.formGroup.controls['name'].value if (ev.target && (ev.target as HTMLInputElement).files) { const files = (ev.target as HTMLInputElement).files if (files) { - Array.from(files).forEach((file) => { - /* - this.imageApi.uploadImage({ image: file }).subscribe((data) => { - this.formGroup.controls[fieldType + 'Url'].setValue(data.imageUrl) - this.fetchingLogoUrl = setFetchUrls(this.apiPrefix, this.formGroup.controls[fieldType + 'Url'].value) - this.msgService.info({ summaryKey: 'LOGO.UPLOADED', detailKey: 'LOGO.LOGO_URL' }) - }) - */ - }) + if (productName == undefined || productName == '' || productName == null) { + this.msgService.error({ summaryKey: 'LOGO.UPLOAD_FAILED_NAME' }) + } else if (files[0].size > 110000) { + this.msgService.error({ summaryKey: 'LOGO.UPLOAD_FAILED_SIZE' }) + } else { + let requestParametersGet: GetImageRequestParams + requestParametersGet = { + refId: productName, + refType: RefType.Logo + } + let requestParameters: UploadImageRequestParams + const blob = new Blob([files[0]], { type: files[0].type }) + let imageType: RefType = RefType.Logo + + requestParameters = { + contentLength: files.length, + refId: this.formGroup.controls['name'].value!, + refType: imageType, + body: blob + } + + this.fetchingLogoUrl = undefined + + this.imageApi.getImage(requestParametersGet).subscribe( + (res) => { + if (RegExp(/^.*.(jpg|jpeg|png)$/).exec(files[0].name)) { + this.imageApi.updateImage(requestParameters).subscribe(() => { + this.fetchingLogoUrl = + this.imageApi.configuration.basePath + '/images/' + productName + '/' + fieldType + this.msgService.info({ summaryKey: 'LOGO.UPLOADED' }) + this.formGroup.controls['imageUrl'].setValue('') + this.logoImageWasUploaded = true + }) + } + }, + (err) => { + if (RegExp(/^.*.(jpg|jpeg|png)$/).exec(files[0].name)) { + this.imageApi.uploadImage(requestParameters).subscribe(() => { + this.fetchingLogoUrl = + this.imageApi.configuration.basePath + '/images/' + productName + '/' + fieldType + this.msgService.info({ summaryKey: 'LOGO.UPLOADED' }) + this.formGroup.controls['imageUrl'].setValue('') + this.logoImageWasUploaded = true + }) + } + } + ) + } } } } + + getImageUrl(): string { + let imgUrl = this.formGroup.controls['imageUrl'].value + if (imgUrl == '' || imgUrl == null) { + return this.imageApi.configuration.basePath + '/images/' + this.formGroup.controls['name'].value + '/logo' + } else { + return imgUrl + } + } + + inputChange(event: Event) { + setTimeout(() => { + this.fetchingLogoUrl = (event.target as HTMLInputElement).value + if ((event.target as HTMLInputElement).value == undefined || (event.target as HTMLInputElement).value == '') { + this.fetchingLogoUrl = + this.imageApi.configuration.basePath + '/images/' + this.formGroup.controls['name'].value + '/logo' + } + }, 1000) + } } diff --git a/src/app/product-store/product-search/product-search.component.html b/src/app/product-store/product-search/product-search.component.html index 04d635c..1a31120 100644 --- a/src/app/product-store/product-search/product-search.component.html +++ b/src/app/product-store/product-search/product-search.component.html @@ -65,7 +65,7 @@
@@ -83,7 +83,7 @@
- +
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..605f165 100644 --- a/src/app/product-store/product-search/product-search.component.ts +++ b/src/app/product-store/product-search/product-search.component.ts @@ -7,7 +7,7 @@ import { DataView } from 'primeng/dataview' import { Action, DataViewControlTranslations } from '@onecx/portal-integration-angular' -import { ProductPageResult, ProductsAPIService } from 'src/app/shared/generated' +import { ImagesInternalAPIService, ProductPageResult, ProductsAPIService } from 'src/app/shared/generated' import { limitText } from 'src/app/shared/utils' export interface ProductSearchCriteria { @@ -36,7 +36,8 @@ export class ProductSearchComponent implements OnInit { private route: ActivatedRoute, private router: Router, private productApi: ProductsAPIService, - private translate: TranslateService + private translate: TranslateService, + private imageApi: ImagesInternalAPIService ) { this.productSearchCriteriaGroup = new FormGroup({ productName: new FormControl(null) @@ -148,4 +149,12 @@ export class ProductSearchComponent implements OnInit { public onAppSearch() { this.router.navigate(['./apps'], { relativeTo: this.route }) } + + getImageUrl(product: any) { + if (product.imageUrl) { + return product.imageUrl + } else { + return this.imageApi.configuration.basePath + '/images/' + product.name + '/logo' + } + } } diff --git a/src/app/shared/generated/.openapi-generator/FILES b/src/app/shared/generated/.openapi-generator/FILES index d227640..6efc8c3 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/products.service.ts configuration.ts @@ -12,6 +13,7 @@ index.ts model/createMicrofrontendRequest.ts model/createProductRequest.ts model/createUIEndpoint.ts +model/imageInfo.ts model/microfrontend.ts model/microfrontendAbstract.ts model/microfrontendPageResult.ts @@ -25,6 +27,7 @@ model/productAbstract.ts model/productAndWorkspaces.ts model/productPageResult.ts model/productSearchCriteria.ts +model/refType.ts model/uIEndpoint.ts model/updateMicrofrontendRequest.ts model/updateProductRequest.ts diff --git a/src/app/shared/generated/api/api.ts b/src/app/shared/generated/api/api.ts index cb8fcc6..8c1ff58 100644 --- a/src/app/shared/generated/api/api.ts +++ b/src/app/shared/generated/api/api.ts @@ -1,5 +1,7 @@ +export * from './imagesInternal.service'; +import { ImagesInternalAPIService } from './imagesInternal.service'; export * from './microfrontends.service'; import { MicrofrontendsAPIService } from './microfrontends.service'; export * from './products.service'; import { ProductsAPIService } from './products.service'; -export const APIS = [MicrofrontendsAPIService, ProductsAPIService]; +export const APIS = [ImagesInternalAPIService, MicrofrontendsAPIService, 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 6edd837..56c769d 100644 --- a/src/app/shared/generated/model/models.ts +++ b/src/app/shared/generated/model/models.ts @@ -1,6 +1,7 @@ export * from './createMicrofrontendRequest'; export * from './createProductRequest'; export * from './createUIEndpoint'; +export * from './imageInfo'; export * from './microfrontend'; export * from './microfrontendAbstract'; export * from './microfrontendPageResult'; @@ -13,6 +14,7 @@ export * from './productAbstract'; export * from './productAndWorkspaces'; export * from './productPageResult'; export * from './productSearchCriteria'; +export * from './refType'; export * from './uIEndpoint'; export * from './updateMicrofrontendRequest'; export * from './updateProductRequest'; 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/product-store-bff-api.yaml b/src/assets/api/product-store-bff-api.yaml index 8e30b1a..caec06b 100644 --- a/src/assets/api/product-store-bff-api.yaml +++ b/src/assets/api/product-store-bff-api.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 @@ -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,8 +378,140 @@ paths: application/json: schema: $ref: '#/components/schemas/ProblemDetailResponse' + /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: diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 7f3cd62..285fceb 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -143,7 +143,10 @@ "PLACEHOLDER": "Bild Platzhalter", "UPLOAD": "Bild auswählen und hochladen" }, - "UPLOADED": "Datei hochgeladen" + "UPLOADED": "Datei hochgeladen", + "UPLOAD_FAILED": "Upload fehlgeschlagen", + "UPLOAD_FAILED_SIZE": "Upload fehlgeschlagen. Das Bild ist zu groß", + "UPLOAD_FAILED_NAME": "Upload fehlgeschlagen. Bitte geben Sie einen Namen an." }, "DIALOG": { "SEARCH.HEADER": "Product Store", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index c2c4c9f..92794a5 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -144,7 +144,10 @@ "PLACEHOLDER": "Image Placeholder", "UPLOAD": "Select and upload image" }, - "UPLOADED": "File uploaded" + "UPLOADED": "File uploaded", + "UPLOAD_FAILED": "Upload failed", + "UPLOAD_FAILED_SIZE": "Upload failed. The image is too large", + "UPLOAD_FAILED_NAME": "Upload failed. Please provide a name first" }, "DIALOG": { "SEARCH.HEADER": "Product Store",