From 82ae792ec9391d392d1b725fa244579841c8598e Mon Sep 17 00:00:00 2001 From: Richard Treier Date: Fri, 30 Jun 2023 09:34:56 +0200 Subject: [PATCH] feat: parameterization of http data sources / data sinks --- CHANGELOG.md | 2 + fake-backend/json/contractAgreementPage.json | 8 +- package-lock.json | 14 +- package.json | 3 +- .../asset-detail-dialog.component.ts | 1 + .../asset-property-grid-group-builder.ts | 4 +- .../data-address-type-select-items.ts | 31 ++- .../data-address-type.ts | 5 +- .../services/api/broker-server-api.service.ts | 65 +++++ src/app/core/services/api/edc-api.service.ts | 25 +- .../api/contractAgreement.service.ts | 28 -- src/app/core/services/asset-entry-builder.ts | 25 +- src/app/core/services/asset-properties.ts | 46 ++- .../core/services/asset-property-mapper.ts | 49 +++- src/app/core/services/data-address-mapper.ts | 27 ++ .../services/http-params-mapper.service.ts | 122 ++++++-- src/app/core/services/models/asset.ts | 8 + .../services/models/http-request-params.ts | 19 +- src/app/core/utils/string-utils.ts | 10 + .../routes/broker-ui/broker-ui.component.scss | 6 - .../catalog-page/catalog-page.component.ts | 2 +- .../mapping/broker-catalog-mapper.ts | 7 +- .../mapping/broker-catalog-page-result.ts | 2 +- .../catalog-page/mapping/broker-data-offer.ts | 4 +- .../filter-value-select-item.ts | 2 +- .../state/catalog-page-actions.ts | 2 +- .../state/catalog-page-state-model.ts | 6 +- .../catalog-page/state/catalog-page-state.ts | 13 +- .../connector-cards.component.ts | 2 +- .../connector-page-data.service.ts | 13 +- .../connector-page/connector-page-data.ts | 2 +- .../asset-create-dialog-form.ts | 26 ++ .../asset-create-dialog.component.html | 263 +++++++++++++----- .../model/asset-datasource-form-builder.ts | 28 +- .../model/asset-datasource-form-model.ts | 10 +- .../model/asset-metadata-form-builder.ts | 4 + .../model/asset-metadata-form-model.ts | 3 + .../http-datasource-query-param-form-model.ts | 15 + .../connector-ui/connector-ui.component.scss | 6 - ...contract-agreement-transfer-dialog-data.ts | 3 + ...ct-agreement-transfer-dialog-form-model.ts | 13 + ...contract-agreement-transfer-dialog-form.ts | 37 +++ ...t-agreement-transfer-dialog.component.html | 155 ++++++++++- ...act-agreement-transfer-dialog.component.ts | 89 ++++-- src/styles.scss | 6 + 45 files changed, 964 insertions(+), 247 deletions(-) create mode 100644 src/app/core/services/api/broker-server-api.service.ts create mode 100644 src/app/core/services/data-address-mapper.ts create mode 100644 src/app/routes/connector-ui/asset-page/asset-create-dialog/model/http-datasource-query-param-form-model.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf093428..b8da5fcbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ the detailed section referring to by linking pull requests or issues. #### Added +- Parameterization of Http Data Sources. + #### Changed #### Removed diff --git a/fake-backend/json/contractAgreementPage.json b/fake-backend/json/contractAgreementPage.json index af3c17a86..893422e24 100644 --- a/fake-backend/json/contractAgreementPage.json +++ b/fake-backend/json/contractAgreementPage.json @@ -190,7 +190,13 @@ "assetId": "urn:artifact:my-test-asset-2", "createdAt": "2023-04-24T12:32:28.492Z", "properties": { - "asset:prop:id": "urn:artifact:my-test-asset-2" + "asset:prop:id": "urn:artifact:my-test-asset-2", + "asset:prop:datasource:http:hints:proxyMethod": "true", + "asset:prop:datasource:http:hints:proxyPath": "true", + "asset:prop:datasource:http:hints:proxyQueryParams": "true", + "asset:prop:datasource:http:hints:proxyBody": "true", + "asset:prop:datasource:http:hints:defaultMethod": "GET", + "asset:prop:datasource:http:hints:defaultPath": "/" } }, "contractPolicy": { diff --git a/package-lock.json b/package-lock.json index e8eaf2a5c..03011b619 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,8 @@ "@angular/router": "^14.3.0", "@ng-apimock/core": "^3.6.0", "@ngxs/store": "^3.8.1", - "@sovity.de/edc-client": "0.20230531.73811-main-e140ef56", + "@sovity.de/broker-server-client": "0.20230703.152001-main-d1ec5276", + "@sovity.de/edc-client": "0.20230629.150330-main-aaa2fa41", "clean-deep": "^3.4.0", "date-fns": "^2.29.3", "dotenv": "^16.0.3", @@ -3598,10 +3599,15 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, + "node_modules/@sovity.de/broker-server-client": { + "version": "0.20230703.152001-main-d1ec5276", + "resolved": "https://registry.npmjs.org/@sovity.de/broker-server-client/-/broker-server-client-0.20230703.152001-main-d1ec5276.tgz", + "integrity": "sha512-3azcUFMg0tLHrCqPG/vw8vIYqShLrJnAQDhtH0VyHvYyyt+waokdS6Pf0nGvL0RzWEBQZY81pFt2TvE56AS5ug==" + }, "node_modules/@sovity.de/edc-client": { - "version": "0.20230531.73811-main-e140ef56", - "resolved": "https://registry.npmjs.org/@sovity.de/edc-client/-/edc-client-0.20230531.73811-main-e140ef56.tgz", - "integrity": "sha512-VzO0sNV5rrEMbXk4OCeC/QXCadkqlkSLzOnXy7O4NVsFoU8RziICGVtX9yyUeqrWdad7dB2R3nt16kgbQ9kXaQ==" + "version": "0.20230629.150330-main-aaa2fa41", + "resolved": "https://registry.npmjs.org/@sovity.de/edc-client/-/edc-client-0.20230629.150330-main-aaa2fa41.tgz", + "integrity": "sha512-6oaHKaqeZUYMAylOUk4h0Dfl2WTpw86XqSuMhU3ItdXoWo0Z5Xz6HlYaEczut4v3au1o4inwkwvRkwLTRpB61w==" }, "node_modules/@tootallnate/once": { "version": "2.0.0", diff --git a/package.json b/package.json index 2a95181b3..80947d5d0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "@angular/router": "^14.3.0", "@ng-apimock/core": "^3.6.0", "@ngxs/store": "^3.8.1", - "@sovity.de/edc-client": "0.20230531.73811-main-e140ef56", + "@sovity.de/broker-server-client": "0.20230703.152001-main-d1ec5276", + "@sovity.de/edc-client": "0.20230629.150330-main-aaa2fa41", "clean-deep": "^3.4.0", "date-fns": "^2.29.3", "dotenv": "^16.0.3", diff --git a/src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog.component.ts b/src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog.component.ts index 570d12a01..fb6a3d83b 100644 --- a/src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog.component.ts +++ b/src/app/component-library/catalog/asset-detail-dialog/asset-detail-dialog.component.ts @@ -91,6 +91,7 @@ export class AssetDetailDialogComponent implements OnDestroy { onTransferClick() { const data: ContractAgreementTransferDialogData = { contractId: this.data.contractAgreement?.contractAgreementId!!, + asset: this.data.asset, }; this.matDialog.open(ContractAgreementTransferDialogComponent, { data, diff --git a/src/app/component-library/catalog/asset-detail-dialog/asset-property-grid-group-builder.ts b/src/app/component-library/catalog/asset-detail-dialog/asset-property-grid-group-builder.ts index c4a9ac2cd..f54df2506 100644 --- a/src/app/component-library/catalog/asset-detail-dialog/asset-property-grid-group-builder.ts +++ b/src/app/component-library/catalog/asset-detail-dialog/asset-property-grid-group-builder.ts @@ -1,6 +1,6 @@ import {Injectable} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; -import {DataOfferListEntryContractOffer} from '@sovity.de/edc-client'; +import {CatalogContractOffer} from '@sovity.de/broker-server-client'; import {ActiveFeatureSet} from '../../../core/config/active-feature-set'; import {Policy} from '../../../core/services/api/legacy-managent-api-client'; import {AssetProperties} from '../../../core/services/asset-properties'; @@ -174,7 +174,7 @@ export class AssetPropertyGridGroupBuilder { buildContractOfferGroup( asset: Asset, - contractOffer: DataOfferListEntryContractOffer, + contractOffer: CatalogContractOffer, i: number, total: number, ) { diff --git a/src/app/component-library/data-address/data-address-type-select/data-address-type-select-items.ts b/src/app/component-library/data-address/data-address-type-select/data-address-type-select-items.ts index 17c7d9b14..f98a8ffc2 100644 --- a/src/app/component-library/data-address/data-address-type-select/data-address-type-select-items.ts +++ b/src/app/component-library/data-address/data-address-type-select/data-address-type-select-items.ts @@ -3,13 +3,24 @@ import {DataAddressTypeSelectMode} from './data-address-type-select-mode'; export const dataAddressTypeSelectItems = ( type: DataAddressTypeSelectMode, -): DataAddressTypeSelectItem[] => [ - { - id: 'Http', - label: 'REST-API Endpoint', - }, - { - id: 'Custom-Data-Address-Json', - label: `Custom ${type} Config (JSON)`, - }, -]; +): DataAddressTypeSelectItem[] => { + let items: DataAddressTypeSelectItem[] = [ + { + id: 'Http', + label: 'REST-API Endpoint', + }, + { + id: 'Custom-Data-Address-Json', + label: `Custom ${type} Config (JSON)`, + }, + ]; + + if (type === 'Datasink') { + items.push({ + id: 'Custom-Transfer-Process-Request', + label: 'Custom Transfer Process Request (JSON)', + }); + } + + return items; +}; diff --git a/src/app/component-library/data-address/data-address-type-select/data-address-type.ts b/src/app/component-library/data-address/data-address-type-select/data-address-type.ts index e7c6388d4..7fe8aaf79 100644 --- a/src/app/component-library/data-address/data-address-type-select/data-address-type.ts +++ b/src/app/component-library/data-address/data-address-type-select/data-address-type.ts @@ -1 +1,4 @@ -export type DataAddressType = 'Custom-Data-Address-Json' | 'Http'; +export type DataAddressType = + | 'Custom-Data-Address-Json' + | 'Custom-Transfer-Process-Request' + | 'Http'; diff --git a/src/app/core/services/api/broker-server-api.service.ts b/src/app/core/services/api/broker-server-api.service.ts new file mode 100644 index 000000000..290c314d7 --- /dev/null +++ b/src/app/core/services/api/broker-server-api.service.ts @@ -0,0 +1,65 @@ +import {Inject, Injectable} from '@angular/core'; +import {Observable, from} from 'rxjs'; +import { + BrokerServerClient, + CatalogPageQuery, + CatalogPageResult, + ConnectorDetailPageQuery, + ConnectorDetailPageResult, + ConnectorPageQuery, + ConnectorPageResult, + DataOfferDetailPageQuery, + DataOfferDetailPageResult, + buildBrokerServerClient, +} from '@sovity.de/broker-server-client'; +import {APP_CONFIG, AppConfig} from '../../config/app-config'; + +@Injectable({providedIn: 'root'}) +export class BrokerServerApiService { + brokerServerClient: BrokerServerClient; + + constructor(@Inject(APP_CONFIG) private config: AppConfig) { + this.brokerServerClient = buildBrokerServerClient({ + managementApiUrl: this.config.managementApiUrl, + managementApiKey: this.config.managementApiKey, + }); + } + + catalogPage( + catalogPageQuery: CatalogPageQuery, + ): Observable { + return from( + this.brokerServerClient.brokerServerApi.catalogPage({catalogPageQuery}), + ); + } + + dataOfferDetailPage( + dataOfferDetailPageQuery: DataOfferDetailPageQuery, + ): Observable { + return from( + this.brokerServerClient.brokerServerApi.dataOfferDetailPage({ + dataOfferDetailPageQuery, + }), + ); + } + + connectorPage( + connectorPageQuery: ConnectorPageQuery, + ): Observable { + return from( + this.brokerServerClient.brokerServerApi.connectorPage({ + connectorPageQuery, + }), + ); + } + + connectorDetailPage( + connectorDetailPageQuery: ConnectorDetailPageQuery, + ): Observable { + return from( + this.brokerServerClient.brokerServerApi.connectorDetailPage({ + connectorDetailPageQuery, + }), + ); + } +} diff --git a/src/app/core/services/api/edc-api.service.ts b/src/app/core/services/api/edc-api.service.ts index 1536a1081..d18004ff4 100644 --- a/src/app/core/services/api/edc-api.service.ts +++ b/src/app/core/services/api/edc-api.service.ts @@ -1,13 +1,10 @@ import {Inject, Injectable} from '@angular/core'; import {Observable, from} from 'rxjs'; import { - CatalogPageQuery, - CatalogPageResult, - ConnectorPageQuery, - ConnectorPageResult, ContractAgreementPage, + ContractAgreementTransferRequest, EdcClient, - KpiResult, + IdResponseDto, buildEdcClient, } from '@sovity.de/edc-client'; import {APP_CONFIG, AppConfig} from '../../config/app-config'; @@ -27,21 +24,11 @@ export class EdcApiService { return from(this.edcClient.uiApi.contractAgreementEndpoint()); } - getKpis(): Observable { - return from(this.edcClient.useCaseApi.kpiEndpoint()); - } - - brokerCatalog( - catalogPageQuery: CatalogPageQuery, - ): Observable { - return from(this.edcClient.brokerServerApi.catalogPage({catalogPageQuery})); - } - - brokerConnectors( - connectorPageQuery: ConnectorPageQuery, - ): Observable { + initiateTranfer( + contractAgreementTransferRequest: ContractAgreementTransferRequest, + ): Observable { return from( - this.edcClient.brokerServerApi.connectorPage({connectorPageQuery}), + this.edcClient.uiApi.initiateTransfer({contractAgreementTransferRequest}), ); } } diff --git a/src/app/core/services/api/legacy-managent-api-client/api/contractAgreement.service.ts b/src/app/core/services/api/legacy-managent-api-client/api/contractAgreement.service.ts index bc0251340..ab933e004 100644 --- a/src/app/core/services/api/legacy-managent-api-client/api/contractAgreement.service.ts +++ b/src/app/core/services/api/legacy-managent-api-client/api/contractAgreement.service.ts @@ -28,8 +28,6 @@ import {Configuration} from '../configuration'; import {CustomHttpParameterCodec} from '../encoder'; // @ts-ignore import {ContractAgreementDto} from '../model/contractAgreementDto'; -import {DataAddressDto} from '../model/dataAddressDto'; -import {TransferId} from '../model/transferId'; // @ts-ignore import { API_KEY, @@ -341,30 +339,4 @@ export class ContractAgreementService { }, ); } - initiateTransfer( - id: string, - dataAddressDto: DataAddressDto, - ): Observable { - if (id == null) { - throw new Error( - 'Required parameter id was null or undefined when calling initiateTransfer.', - ); - } - - if (dataAddressDto == null) { - throw new Error( - 'Required parameter dataAddressDto was null or undefined when calling initiateTransfer.', - ); - } - - let url = `${ - this.configuration.basePath - }/contract-agreements-transfer/contractagreements/${encodeURIComponent( - id, - )}/transfer`; - return this.httpClient.post(url, dataAddressDto, { - withCredentials: this.configuration.withCredentials, - headers: this.defaultHeaders, - }); - } } diff --git a/src/app/core/services/asset-entry-builder.ts b/src/app/core/services/asset-entry-builder.ts index fe9297197..694300505 100644 --- a/src/app/core/services/asset-entry-builder.ts +++ b/src/app/core/services/asset-entry-builder.ts @@ -1,14 +1,14 @@ import {Injectable} from '@angular/core'; import {AssetEditorDialogFormValue} from '../../routes/connector-ui/asset-page/asset-create-dialog/asset-editor-dialog-form-model'; -import {AssetEntryDto, DataAddressDto} from './api/legacy-managent-api-client'; +import {AssetEntryDto} from './api/legacy-managent-api-client'; import {AssetPropertyMapper} from './asset-property-mapper'; -import {HttpRequestParamsMapper} from './http-params-mapper.service'; +import {DataAddressMapper} from './data-address-mapper'; @Injectable() export class AssetEntryBuilder { constructor( private assetPropertyMapper: AssetPropertyMapper, - private httpRequestParamsMapper: HttpRequestParamsMapper, + private dataAddressMapper: DataAddressMapper, ) {} /** @@ -19,22 +19,9 @@ export class AssetEntryBuilder { */ buildAssetEntry(formValue: AssetEditorDialogFormValue): AssetEntryDto { let properties = this.assetPropertyMapper.buildProperties(formValue); - const dataAddress = this.buildDataAddressDto(formValue.datasource); + const dataAddress = this.dataAddressMapper.buildDataAddressProperties( + formValue.datasource, + ); return {asset: {properties}, dataAddress}; } - - private buildDataAddressDto( - datasource: AssetEditorDialogFormValue['datasource'], - ): DataAddressDto { - switch (datasource?.dataAddressType) { - case 'Custom-Data-Address-Json': - return JSON.parse(datasource.dataDestination?.trim() ?? ''); - case 'Http': - return this.httpRequestParamsMapper.buildHttpDataAddressDto(datasource); - default: - throw new Error( - `Invalid data address type: ${datasource?.dataAddressType}`, - ); - } - } } diff --git a/src/app/core/services/asset-properties.ts b/src/app/core/services/asset-properties.ts index b3838a6aa..e69c31d65 100644 --- a/src/app/core/services/asset-properties.ts +++ b/src/app/core/services/asset-properties.ts @@ -25,10 +25,52 @@ export const AssetProperties = { // mds specific asset properties dataCategory: 'http://w3id.org/mds#dataCategory', dataSubcategory: 'http://w3id.org/mds#dataSubcategory', - dataModel: 'http://w3id.org/mds#dataModel', // guessed - geoReferenceMethod: 'http://w3id.org/mds#geoReferenceMethod', // guessed + dataModel: 'http://w3id.org/mds#dataModel', + geoReferenceMethod: 'http://w3id.org/mds#geoReferenceMethod', transportMode: 'http://w3id.org/mds#transportMode', + /** + * Whether this asset supports HTTP Method parameterization + * + * Example values: "true", "false" + */ + httpProxyMethod: 'asset:prop:datasource:http:hints:proxyMethod', + + /** + * Whether this asset supports HTTP Path parameterization + * + * Example values: "true", "false" + */ + httpProxyPath: 'asset:prop:datasource:http:hints:proxyPath', + + /** + * Whether this asset supports HTTP Query Param parameterization + * + * Example values: "true", "false" + */ + httpProxyQueryParams: 'asset:prop:datasource:http:hints:proxyQueryParams', + + /** + * Whether this asset supports HTTP Body parameterization + * + * Example values: "true", "false" + */ + httpProxyBody: 'asset:prop:datasource:http:hints:proxyBody', + + /** + * If this asset supports HTTP Method parameterization, this is the default method + * + * Example values: "GET", "POST", "PUT", "DELETE" + */ + httpDefaultMethod: 'asset:prop:datasource:http:hints:defaultMethod', + + /** + * If this asset supports HTTP Path parameterization, this is the default path (appended after base path) + * + * Example values: /my-endpoint + */ + httpDefaultPath: 'asset:prop:datasource:http:hints:defaultPath', + /** * @deprecated use {@link AssetProperties.curatorOrganizationName} instead */ diff --git a/src/app/core/services/asset-property-mapper.ts b/src/app/core/services/asset-property-mapper.ts index 998b2f434..0a68871ac 100644 --- a/src/app/core/services/asset-property-mapper.ts +++ b/src/app/core/services/asset-property-mapper.ts @@ -80,6 +80,16 @@ export class AssetPropertyMapper { dataModel: props[AssetProperties.dataModel], geoReferenceMethod: props[AssetProperties.geoReferenceMethod], transportMode, + httpProxyMethod: this._parseBoolean( + props[AssetProperties.httpProxyMethod], + ), + httpProxyPath: this._parseBoolean(props[AssetProperties.httpProxyPath]), + httpProxyQueryParams: this._parseBoolean( + props[AssetProperties.httpProxyQueryParams], + ), + httpProxyBody: this._parseBoolean(props[AssetProperties.httpProxyBody]), + httpDefaultPath: props[AssetProperties.httpDefaultPath], + httpDefaultMethod: props[AssetProperties.httpDefaultMethod], additionalProperties, }; } @@ -115,12 +125,12 @@ export class AssetPropertyMapper { props[AssetProperties.description] = trimmedOrNull(metadata?.description); props[AssetProperties.language] = metadata?.language?.id ?? null; - props[AssetProperties.publisher] = trimmedOrNull(datasource?.publisher); + props[AssetProperties.publisher] = trimmedOrNull(metadata?.publisher); props[AssetProperties.standardLicense] = trimmedOrNull( - datasource?.standardLicense, + metadata?.standardLicense, ); props[AssetProperties.endpointDocumentation] = trimmedOrNull( - datasource?.endpointDocumentation, + metadata?.endpointDocumentation, ); if (this.activeFeatureSet.hasMdsFields()) { @@ -134,6 +144,39 @@ export class AssetPropertyMapper { props[AssetProperties.transportMode] = advanced?.transportMode?.id ?? null; } + + if (datasource?.dataAddressType === 'Http') { + props[AssetProperties.httpProxyMethod] = this._encodeBoolean( + datasource?.httpProxyMethod, + ); + props[AssetProperties.httpProxyPath] = this._encodeBoolean( + datasource?.httpProxyPath, + ); + props[AssetProperties.httpProxyQueryParams] = this._encodeBoolean( + datasource?.httpProxyQueryParams, + ); + props[AssetProperties.httpProxyBody] = this._encodeBoolean( + datasource?.httpProxyBody, + ); + props[AssetProperties.httpDefaultMethod] = datasource?.httpProxyMethod + ? datasource?.httpMethod ?? null + : null; + props[AssetProperties.httpDefaultPath] = datasource?.httpProxyPath + ? datasource?.httpDefaultPath ?? null + : null; + } + return removeNullValues(props); } + + private _parseBoolean(value: string | null): boolean | null { + if (!value) { + return null; + } + return value === 'true'; + } + + private _encodeBoolean(value?: boolean | null): string { + return value ? 'true' : 'false'; + } } diff --git a/src/app/core/services/data-address-mapper.ts b/src/app/core/services/data-address-mapper.ts new file mode 100644 index 000000000..9e097d12e --- /dev/null +++ b/src/app/core/services/data-address-mapper.ts @@ -0,0 +1,27 @@ +import {Injectable} from '@angular/core'; +import {AssetDatasourceFormValue} from '../../routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-model'; +import {ContractAgreementTransferDialogFormValue} from '../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model'; +import {HttpRequestParamsMapper} from './http-params-mapper.service'; + +@Injectable({providedIn: 'root'}) +export class DataAddressMapper { + constructor(private httpRequestParamsMapper: HttpRequestParamsMapper) {} + + buildDataAddressProperties( + formValue: + | AssetDatasourceFormValue + | ContractAgreementTransferDialogFormValue + | undefined, + ): Record { + switch (formValue?.dataAddressType) { + case 'Custom-Data-Address-Json': + return JSON.parse(formValue.dataDestination?.trim()!!); + case 'Http': + return this.httpRequestParamsMapper.buildHttpDataAddress(formValue); + default: + throw new Error( + `Invalid Data Address Type ${formValue?.dataAddressType}`, + ); + } + } +} diff --git a/src/app/core/services/http-params-mapper.service.ts b/src/app/core/services/http-params-mapper.service.ts index 8f5f5024b..730b8e198 100644 --- a/src/app/core/services/http-params-mapper.service.ts +++ b/src/app/core/services/http-params-mapper.service.ts @@ -1,23 +1,44 @@ import {Injectable} from '@angular/core'; import {AssetDatasourceFormValue} from '../../routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-model'; import {HttpDatasourceHeaderFormValue} from '../../routes/connector-ui/asset-page/asset-create-dialog/model/http-datasource-header-form-model'; +import {HttpDatasourceQueryParamFormValue} from '../../routes/connector-ui/asset-page/asset-create-dialog/model/http-datasource-query-param-form-model'; import {ContractAgreementTransferDialogFormValue} from '../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model'; import {removeNullValues} from '../utils/record-utils'; -import {DataAddressDto} from './api/legacy-managent-api-client'; +import {everythingAfter, everythingBefore} from '../utils/string-utils'; +import {Asset} from './models/asset'; import {HttpRequestParams} from './models/http-request-params'; @Injectable({providedIn: 'root'}) export class HttpRequestParamsMapper { - buildHttpDataAddressDto( + buildHttpDataAddress( formValue: | AssetDatasourceFormValue | ContractAgreementTransferDialogFormValue | undefined, - ): DataAddressDto { + ): Record { const params = this.buildHttpRequestParams(formValue); - return { - properties: this.encodeHttpRequestParams(params), - }; + return this.encodeHttpRequestParams(params); + } + + encodeHttpProxyTransferRequestPropreties( + asset: Asset, + value: ContractAgreementTransferDialogFormValue, + ): Record { + const method = value.httpProxiedMethod?.trim() ?? ''; + const {baseUrl: pathSegments, queryParams} = this.getUrlAndQueryParams( + value.httpProxiedPath, + value.httpProxiedQueryParams, + ); + const body = value.httpProxiedBody?.trim() ?? ''; + const contentType = value.httpProxiedBodyContentType?.trim() ?? ''; + + return removeNullValues({ + method: asset.httpProxyMethod ? method : null, + pathSegments: asset.httpProxyPath ? pathSegments : null, + queryParams: asset.httpProxyQueryParams ? queryParams : null, + body: asset.httpProxyBody ? body : null, + mediaType: asset.httpProxyBody ? contentType : null, + }); } encodeHttpRequestParams( @@ -25,11 +46,17 @@ export class HttpRequestParamsMapper { ): Record { const props: Record = { type: 'HttpData', - baseUrl: httpRequestParams.url, + baseUrl: httpRequestParams.baseUrl, method: httpRequestParams.method, authKey: httpRequestParams.authHeaderName, authCode: httpRequestParams.authHeaderValue, secretName: httpRequestParams.authHeaderSecretName, + proxyMethod: httpRequestParams.proxyMethod ? 'true' : null, + proxyPath: httpRequestParams.proxyPath ? 'true' : null, + proxyQueryParams: httpRequestParams.proxyQueryParams ? 'true' : null, + proxyBody: httpRequestParams.proxyBody ? 'true' : null, + queryParams: httpRequestParams.queryParams, + path: httpRequestParams.defaultPath, ...Object.fromEntries( Object.entries(httpRequestParams.headers).map( ([headerName, headerValue]) => [`header:${headerName}`, headerValue], @@ -40,12 +67,48 @@ export class HttpRequestParamsMapper { } buildHttpRequestParams( - formValue: - | AssetDatasourceFormValue - | ContractAgreementTransferDialogFormValue - | undefined, + formValue: AssetDatasourceFormValue | undefined, ): HttpRequestParams { - // Common Values + let proxyMethod = !!formValue?.httpProxyMethod; + let proxyPath = !!formValue?.httpProxyPath; + let proxyQueryParams = !!formValue?.httpProxyQueryParams; + let proxyBody = !!formValue?.httpProxyBody; + + let {authHeaderName, authHeaderValue, authHeaderSecretName} = + this.getAuthFields(formValue); + + let defaultPath: string | null = null; + if (proxyPath) { + defaultPath = formValue?.httpDefaultPath?.trim() || null; + } + + let method = formValue?.httpMethod?.trim().toUpperCase() ?? ''; + let {baseUrl, queryParams} = this.getUrlAndQueryParams( + formValue?.httpUrl, + formValue?.httpQueryParams, + ); + + return { + baseUrl, + defaultPath, + method, + authHeaderName, + authHeaderValue, + authHeaderSecretName, + proxyMethod, + proxyPath, + proxyQueryParams, + proxyBody, + queryParams, + headers: this.buildHttpHeaders(formValue?.httpHeaders ?? []), + }; + } + + private getAuthFields(formValue: AssetDatasourceFormValue | undefined): { + authHeaderName: string | null; + authHeaderValue: string | null; + authHeaderSecretName: string | null; + } { let authHeaderName: string | null = null; if (formValue?.httpAuthHeaderType !== 'None') { authHeaderName = formValue?.httpAuthHeaderName?.trim() || null; @@ -61,15 +124,34 @@ export class HttpRequestParamsMapper { authHeaderSecretName = formValue?.httpAuthHeaderSecretName?.trim() || null; } + return {authHeaderName, authHeaderValue, authHeaderSecretName}; + } - return { - url: formValue?.httpUrl?.trim() ?? '', - method: formValue?.httpMethod?.trim().toUpperCase() ?? '', - authHeaderName, - authHeaderValue, - authHeaderSecretName, - headers: this.buildHttpHeaders(formValue?.httpHeaders ?? []), - }; + getUrlAndQueryParams( + rawUrl: string | null | undefined, + rawQueryParams: HttpDatasourceQueryParamFormValue[] | null | undefined, + ): { + baseUrl: string; + queryParams: string; + } { + let url = rawUrl?.trim() ?? ''; + + let baseUrl = everythingBefore('?', url); + + let queryParamSegments = (rawQueryParams ?? []).map((param) => + this.encodeQueryParam(param), + ); + let queryParams = [everythingAfter('?', url), ...queryParamSegments] + .filter((it) => !!it) + .join('&'); + + return {baseUrl, queryParams}; + } + + private encodeQueryParam(param: HttpDatasourceQueryParamFormValue): string { + let k = param.paramName?.trim() ?? ''; + let v = param.paramValue?.trim() ?? ''; + return `${encodeURIComponent(k)}=${encodeURIComponent(v)}`; } private buildHttpHeaders( diff --git a/src/app/core/services/models/asset.ts b/src/app/core/services/models/asset.ts index fcdd155b5..fd9aa65ef 100644 --- a/src/app/core/services/models/asset.ts +++ b/src/app/core/services/models/asset.ts @@ -30,6 +30,14 @@ export interface Asset { geoReferenceMethod: string | null; transportMode: TransportModeSelectItem | null; + // HTTP Parameterization Metadata + httpProxyMethod: boolean | null; + httpProxyPath: boolean | null; + httpProxyQueryParams: boolean | null; + httpProxyBody: boolean | null; + httpDefaultPath: string | null; + httpDefaultMethod: string | null; + // Unhandled Additional Properties additionalProperties: AdditionalAssetProperty[]; } diff --git a/src/app/core/services/models/http-request-params.ts b/src/app/core/services/models/http-request-params.ts index 31a52496b..f70660127 100644 --- a/src/app/core/services/models/http-request-params.ts +++ b/src/app/core/services/models/http-request-params.ts @@ -1,8 +1,14 @@ export interface HttpRequestParams { /** - * URL the request is sent to + * (Base) URL of the request */ - url: string; + baseUrl: string; + + /** + * If proxy path is set, this + */ + + defaultPath: string | null; /** * Http-method @@ -28,4 +34,13 @@ export interface HttpRequestParams { * Additional headers to be sent */ headers: Record; + + /** + * Query Parameters + */ + queryParams: string; + proxyMethod: boolean; + proxyPath: boolean; + proxyQueryParams: boolean; + proxyBody: boolean; } diff --git a/src/app/core/utils/string-utils.ts b/src/app/core/utils/string-utils.ts index 62271b352..5aceda2c8 100644 --- a/src/app/core/utils/string-utils.ts +++ b/src/app/core/utils/string-utils.ts @@ -5,3 +5,13 @@ export function trimmedOrNull(s?: string | null): string | null { const trimmed = s?.trim(); return trimmed ? trimmed : null; } + +export function everythingBefore(separator: string, s: string): string { + const index = s.indexOf(separator); + return index === -1 ? s : s.substring(0, index); +} + +export function everythingAfter(separator: string, s: string): string { + const index = s.indexOf(separator); + return index === -1 ? '' : s.substring(index + separator.length); +} diff --git a/src/app/routes/broker-ui/broker-ui.component.scss b/src/app/routes/broker-ui/broker-ui.component.scss index bde29cdb6..ce2aecbd2 100644 --- a/src/app/routes/broker-ui/broker-ui.component.scss +++ b/src/app/routes/broker-ui/broker-ui.component.scss @@ -24,9 +24,3 @@ cursor: pointer; background-color: #dedede; } - -::ng-deep .mat-drawer-inner-container { - display: flex; - flex-direction: column; - overflow: hidden !important; -} diff --git a/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts b/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts index 64d8fd54e..ddf10861b 100644 --- a/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts +++ b/src/app/routes/broker-ui/catalog-page/catalog-page/catalog-page.component.ts @@ -5,7 +5,7 @@ import {PageEvent} from '@angular/material/paginator'; import {BehaviorSubject, Subject} from 'rxjs'; import {map, takeUntil} from 'rxjs/operators'; import {Store} from '@ngxs/store'; -import {CatalogPageSortingItem} from '@sovity.de/edc-client/dist/generated/models/CatalogPageSortingItem'; +import {CatalogPageSortingItem} from '@sovity.de/broker-server-client'; import {AssetDetailDialogDataService} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-data.service'; import {AssetDetailDialogResult} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog-result'; import {AssetDetailDialogComponent} from '../../../../component-library/catalog/asset-detail-dialog/asset-detail-dialog.component'; diff --git a/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-mapper.ts b/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-mapper.ts index 46c0bbc73..e1051a10f 100644 --- a/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-mapper.ts +++ b/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-mapper.ts @@ -1,5 +1,8 @@ import {Injectable} from '@angular/core'; -import {CatalogPageResult, DataOfferListEntry} from '@sovity.de/edc-client'; +import { + CatalogDataOffer, + CatalogPageResult, +} from '@sovity.de/broker-server-client'; import {AssetPropertyMapper} from '../../../../../core/services/asset-property-mapper'; import {BrokerCatalogPageResult} from './broker-catalog-page-result'; import {BrokerDataOffer} from './broker-data-offer'; @@ -15,7 +18,7 @@ export class BrokerCatalogMapper { }; } - private buildUiDataOffer(offer: DataOfferListEntry): BrokerDataOffer { + private buildUiDataOffer(offer: CatalogDataOffer): BrokerDataOffer { return { ...offer, asset: this.assetPropertyMapper.buildAssetFromProperties( diff --git a/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-page-result.ts b/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-page-result.ts index 084967c65..8a1b03e9a 100644 --- a/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-page-result.ts +++ b/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-catalog-page-result.ts @@ -1,4 +1,4 @@ -import {CatalogPageResult} from '@sovity.de/edc-client'; +import {CatalogPageResult} from '@sovity.de/broker-server-client'; import {BrokerDataOffer} from './broker-data-offer'; /** diff --git a/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-data-offer.ts b/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-data-offer.ts index 042cb5ef8..7c44b6b98 100644 --- a/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-data-offer.ts +++ b/src/app/routes/broker-ui/catalog-page/catalog-page/mapping/broker-data-offer.ts @@ -1,9 +1,9 @@ -import {DataOfferListEntry} from '@sovity.de/edc-client'; +import {CatalogDataOffer} from '@sovity.de/broker-server-client'; import {Asset} from '../../../../../core/services/models/asset'; /** * Contract Offer, but with Assets replaced with type safe assets */ -export type BrokerDataOffer = DataOfferListEntry & { +export type BrokerDataOffer = CatalogDataOffer & { asset: Asset; }; diff --git a/src/app/routes/broker-ui/catalog-page/filter-value-select/filter-value-select-item.ts b/src/app/routes/broker-ui/catalog-page/filter-value-select/filter-value-select-item.ts index 964055905..e121ca135 100644 --- a/src/app/routes/broker-ui/catalog-page/filter-value-select/filter-value-select-item.ts +++ b/src/app/routes/broker-ui/catalog-page/filter-value-select/filter-value-select-item.ts @@ -1,4 +1,4 @@ -import {CnfFilterItem} from '@sovity.de/edc-client'; +import {CnfFilterItem} from '@sovity.de/broker-server-client'; export interface FilterValueSelectItem { type: 'ITEM' | 'NO_VALUE'; diff --git a/src/app/routes/broker-ui/catalog-page/state/catalog-page-actions.ts b/src/app/routes/broker-ui/catalog-page/state/catalog-page-actions.ts index b7637c284..ca2d126d8 100644 --- a/src/app/routes/broker-ui/catalog-page/state/catalog-page-actions.ts +++ b/src/app/routes/broker-ui/catalog-page/state/catalog-page-actions.ts @@ -1,4 +1,4 @@ -import {CatalogPageSortingItem} from '@sovity.de/edc-client/dist/generated/models/CatalogPageSortingItem'; +import {CatalogPageSortingItem} from '@sovity.de/broker-server-client'; import {FilterValueSelectItem} from '../filter-value-select/filter-value-select-item'; import {CatalogActiveFilterPill} from './catalog-active-filter-pill'; diff --git a/src/app/routes/broker-ui/catalog-page/state/catalog-page-state-model.ts b/src/app/routes/broker-ui/catalog-page/state/catalog-page-state-model.ts index 81b45684e..22dd8b315 100644 --- a/src/app/routes/broker-ui/catalog-page/state/catalog-page-state-model.ts +++ b/src/app/routes/broker-ui/catalog-page/state/catalog-page-state-model.ts @@ -1,6 +1,8 @@ import {Subscription} from 'rxjs'; -import {PaginationMetadata} from '@sovity.de/edc-client'; -import {CatalogPageSortingItem} from '@sovity.de/edc-client/dist/generated/models/CatalogPageSortingItem'; +import { + CatalogPageSortingItem, + PaginationMetadata, +} from '@sovity.de/broker-server-client'; import {Fetched} from '../../../../core/services/models/fetched'; import {BrokerCatalogPageResult} from '../catalog-page/mapping/broker-catalog-page-result'; import {FilterValueSelectVisibleState} from '../filter-value-select/filter-value-select-visible-state'; diff --git a/src/app/routes/broker-ui/catalog-page/state/catalog-page-state.ts b/src/app/routes/broker-ui/catalog-page/state/catalog-page-state.ts index ec8443bce..63f930b36 100644 --- a/src/app/routes/broker-ui/catalog-page/state/catalog-page-state.ts +++ b/src/app/routes/broker-ui/catalog-page/state/catalog-page-state.ts @@ -1,8 +1,11 @@ import {Injectable, OnDestroy} from '@angular/core'; import {Subject} from 'rxjs'; import {Action, State, StateContext} from '@ngxs/store'; -import {CatalogPageQuery, CatalogPageResult} from '@sovity.de/edc-client'; -import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import { + CatalogPageQuery, + CatalogPageResult, +} from '@sovity.de/broker-server-client'; +import {BrokerServerApiService} from '../../../../core/services/api/broker-server-api.service'; import {Fetched} from '../../../../core/services/models/fetched'; import {BrokerCatalogMapper} from '../catalog-page/mapping/broker-catalog-mapper'; import { @@ -28,7 +31,7 @@ type Ctx = StateContext; @Injectable() export class CatalogPageState implements OnDestroy { constructor( - private edcApiService: EdcApiService, + private brokerServerApiService: BrokerServerApiService, private brokerCatalogMapper: BrokerCatalogMapper, private ngxsUtils: NgxsUtils, ) { @@ -49,8 +52,8 @@ export class CatalogPageState implements OnDestroy { ctx.getState().fetchSubscription?.unsubscribe(); const query = this.buildCatalogPageQuery(ctx.getState()); - const fetchSubscription = this.edcApiService - .brokerCatalog(query) + const fetchSubscription = this.brokerServerApiService + .catalogPage(query) .pipe( Fetched.wrap({failureMessage: 'Failed fetching data offers.'}), Fetched.map((data) => diff --git a/src/app/routes/broker-ui/connector-page/connector-cards/connector-cards.component.ts b/src/app/routes/broker-ui/connector-page/connector-cards/connector-cards.component.ts index ee3064f08..69f2d7292 100644 --- a/src/app/routes/broker-ui/connector-page/connector-cards/connector-cards.component.ts +++ b/src/app/routes/broker-ui/connector-page/connector-cards/connector-cards.component.ts @@ -1,5 +1,5 @@ import {Component, HostBinding, Input} from '@angular/core'; -import {ConnectorListEntry} from '@sovity.de/edc-client'; +import {ConnectorListEntry} from '@sovity.de/broker-server-client'; @Component({ selector: 'connector-cards', diff --git a/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.service.ts b/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.service.ts index b5091ce8b..31e0e4824 100644 --- a/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.service.ts +++ b/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.service.ts @@ -1,14 +1,17 @@ import {Injectable} from '@angular/core'; import {Observable, combineLatest} from 'rxjs'; import {map, switchMap} from 'rxjs/operators'; -import {ConnectorPageQuery, ConnectorPageResult} from '@sovity.de/edc-client'; -import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import { + ConnectorPageQuery, + ConnectorPageResult, +} from '@sovity.de/broker-server-client'; +import {BrokerServerApiService} from '../../../../core/services/api/broker-server-api.service'; import {Fetched} from '../../../../core/services/models/fetched'; import {ConnectorPageData} from './connector-page-data'; @Injectable({providedIn: 'root'}) export class ConnectorPageDataService { - constructor(private edcApiService: EdcApiService) {} + constructor(private brokerServerApiService: BrokerServerApiService) {} connectorPageData$( refresh$: Observable, @@ -26,8 +29,8 @@ export class ConnectorPageDataService { const query: ConnectorPageQuery = { searchQuery, }; - return this.edcApiService - .brokerConnectors(query) + return this.brokerServerApiService + .connectorPage(query) .pipe(Fetched.wrap({failureMessage: 'Failed fetching connectors.'})); } } diff --git a/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.ts b/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.ts index e750d3aa9..e6e1ccccf 100644 --- a/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.ts +++ b/src/app/routes/broker-ui/connector-page/connector-page/connector-page-data.ts @@ -1,4 +1,4 @@ -import {ConnectorPageResult} from '@sovity.de/edc-client'; +import {ConnectorPageResult} from '@sovity.de/broker-server-client'; import {Fetched} from '../../../../core/services/models/fetched'; export interface ConnectorPageData { diff --git a/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog-form.ts b/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog-form.ts index a574f5bfd..e7d36b898 100644 --- a/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog-form.ts +++ b/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog-form.ts @@ -57,6 +57,22 @@ export class AssetCreateDialogForm { return this.all.value; } + get proxyMethod(): boolean { + return this.datasource.controls.httpProxyMethod.value; + } + + get proxyPath(): boolean { + return this.datasource.controls.httpProxyPath.value; + } + + get proxyQueryParams(): boolean { + return this.datasource.controls.httpProxyQueryParams.value; + } + + get proxyBody(): boolean { + return this.datasource.controls.httpProxyBody.value; + } + constructor( private formBuilder: FormBuilder, private activeFeatureSet: ActiveFeatureSet, @@ -96,4 +112,14 @@ export class AssetCreateDialogForm { onHttpHeadersRemoveClick(index: number) { this.datasource.controls.httpHeaders.removeAt(index); } + + onHttpQueryParamsAddClick() { + this.datasource.controls.httpQueryParams.push( + this.assetDatasourceFormBuilder.buildQueryParamFormGroup(), + ); + } + + onHttpQueryParamsRemoveClick(index: number) { + this.datasource.controls.httpQueryParams.removeAt(index); + } } diff --git a/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog.component.html b/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog.component.html index 8c9338bf2..2c940c4e9 100644 --- a/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog.component.html +++ b/src/app/routes/connector-ui/asset-page/asset-create-dialog/asset-create-dialog.component.html @@ -62,6 +62,12 @@

Create New Asset

label="Keywords" [control]="form.metadata.controls.keywords"> + + +
@@ -73,11 +79,40 @@

Create New Asset

- - + + + Endpoint Documentation + + + {{ validationMessages.invalidUrlMessage }} + + + +
+ + + Publisher + + + {{ validationMessages.invalidUrlMessage }} + + + + + + Standard License + + + {{ validationMessages.invalidUrlMessage }} + + +
@@ -155,40 +190,179 @@

Create New Asset

Custom Datasource Config (JSON) + placeholder='{"type": "HttpData", ...}' + [formControl]="ctrl"> {{ validationMessages.invalidJsonMessage }} -
- - - Method - - {{ - method - }} - - +
+ {{ form.proxyMethod ? 'Default' : '' }} Method +
+ + + + + {{ form.proxyMethod ? 'Default' : '' }} Method + + + {{ + method + }} + + + When no method override is set, this method will be used. + + + + +
+ +
+ +
URL
- + + + + URL + + + Base URL + + + + {{ validationMessages.invalidUrlMessage }} + + + + + - URL + Default Path - - {{ validationMessages.invalidUrlMessage }} - + [placeholder]="'sub-path/endpoint'" /> + When no path override is set, this path will be used. + + + + + +
+ +
+ +
+ {{ form.proxyQueryParams ? 'Default' : '' }} Query Params +
+ +
+ + + Query Param Name + + + + + Value + + + + + +
+ +
+ With query param parameterization enabled, the default query + params and the query params provided by the consumer will be + merged. +
+ +
+ + + + + +
+ +
Request Body
+ +
+ The request body can only be set from the consumer side, if + parameterization is enabled. +
+ + +
+
Authentication
@@ -337,43 +511,6 @@

Create New Asset

- -
Metadata
- - - - Endpoint Documentation - - - {{ validationMessages.invalidUrlMessage }} - - - -
- - - Publisher - - - {{ validationMessages.invalidUrlMessage }} - - - - - - Standard License - - - {{ validationMessages.invalidUrlMessage }} - - -
diff --git a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-builder.ts b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-builder.ts index a6aa29e83..0f98b05e2 100644 --- a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-builder.ts +++ b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-builder.ts @@ -10,6 +10,7 @@ import { } from './asset-datasource-form-model'; import {HttpDatasourceAuthHeaderType} from './http-datasource-auth-header-type'; import {HttpDatasourceHeaderFormModel} from './http-datasource-header-form-model'; +import {HttpDatasourceQueryParamFormModel} from './http-datasource-query-param-form-model'; @Injectable() export class AssetDatasourceFormBuilder { @@ -20,9 +21,6 @@ export class AssetDatasourceFormBuilder { this.formBuilder.nonNullable.group({ dataAddressType: 'Http' as DataAddressType, dataDestination: ['', [Validators.required, jsonValidator]], - publisher: ['', urlValidator], - standardLicense: ['', urlValidator], - endpointDocumentation: ['', urlValidator], // Http Datasource Fields httpUrl: ['', [Validators.required, urlValidator]], @@ -32,6 +30,15 @@ export class AssetDatasourceFormBuilder { httpAuthHeaderName: ['', Validators.required], httpAuthHeaderValue: ['', Validators.required], httpAuthHeaderSecretName: ['', Validators.required], + httpQueryParams: this.formBuilder.array( + new Array>(), + ), + + httpDefaultPath: [''], + httpProxyMethod: [false], + httpProxyPath: [false], + httpProxyQueryParams: [false], + httpProxyBody: [false], httpHeaders: this.formBuilder.array( new Array>(), @@ -46,6 +53,7 @@ export class AssetDatasourceFormBuilder { const httpAuth = value.httpAuthHeaderType !== 'None'; const httpAuthByValue = value.httpAuthHeaderType === 'Value'; const httpAuthByVault = value.httpAuthHeaderType === 'Vault-Secret'; + let proxyPath = !!value.httpProxyPath; return { dataAddressType: true, @@ -64,6 +72,13 @@ export class AssetDatasourceFormBuilder { httpAuthHeaderName: http && httpAuth, httpAuthHeaderValue: http && httpAuthByValue, httpAuthHeaderSecretName: http && httpAuthByVault, + httpQueryParams: http, + + httpDefaultPath: http && proxyPath, + httpProxyMethod: http, + httpProxyPath: http, + httpProxyQueryParams: http, + httpProxyBody: http, httpHeaders: http, }; @@ -78,4 +93,11 @@ export class AssetDatasourceFormBuilder { headerValue: ['', Validators.required], }); } + + buildQueryParamFormGroup(): FormGroup { + return this.formBuilder.nonNullable.group({ + paramName: ['', Validators.required], + paramValue: [''], + }); + } } diff --git a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-model.ts index 4a5a8136c..361ecda63 100644 --- a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-model.ts +++ b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-datasource-form-model.ts @@ -7,15 +7,13 @@ import { import {DataAddressType} from '../../../../../component-library/data-address/data-address-type-select/data-address-type'; import {HttpDatasourceAuthHeaderType} from './http-datasource-auth-header-type'; import {HttpDatasourceHeaderFormModel} from './http-datasource-header-form-model'; +import {HttpDatasourceQueryParamFormModel} from './http-datasource-query-param-form-model'; /** * Form Model for AssetEditorDialog > Datasource */ export interface AssetDatasourceFormModel { dataAddressType: FormControl; - publisher: FormControl; - standardLicense: FormControl; - endpointDocumentation: FormControl; // Custom Datasource JSON dataDestination: FormControl; @@ -29,6 +27,12 @@ export interface AssetDatasourceFormModel { httpAuthHeaderValue: FormControl; httpAuthHeaderSecretName: FormControl; httpHeaders: FormArray>; + httpQueryParams: FormArray>; + httpProxyMethod: FormControl; + httpProxyPath: FormControl; + httpProxyQueryParams: FormControl; + httpProxyBody: FormControl; + httpDefaultPath: FormControl; } /** diff --git a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-builder.ts b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-builder.ts index 4abaf4d15..011414fad 100644 --- a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-builder.ts +++ b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-builder.ts @@ -5,6 +5,7 @@ import {map} from 'rxjs/operators'; import {value$} from '../../../../../core/utils/form-group-utils'; import {noWhitespaceValidator} from '../../../../../core/validators/no-whitespace-validator'; import {requiresPrefixValidator} from '../../../../../core/validators/requires-prefix-validator'; +import {urlValidator} from '../../../../../core/validators/url-validator'; import {LanguageSelectItem} from '../../language-select/language-select-item'; import {LanguageSelectItemService} from '../../language-select/language-select-item.service'; import {AssetsIdValidatorBuilder} from '../assets-id-validator-builder'; @@ -37,6 +38,9 @@ export class AssetMetadataFormBuilder { keywords: [new Array()], language: this.languageSelectItemService.english() as LanguageSelectItem | null, + publisher: ['', urlValidator], + standardLicense: ['', urlValidator], + endpointDocumentation: ['', urlValidator], }); // generate id from name and version(if available) diff --git a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-model.ts index e909d8ba2..99ddece73 100644 --- a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-model.ts +++ b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/asset-metadata-form-model.ts @@ -12,4 +12,7 @@ export interface AssetMetadataFormModel { description: FormControl; keywords: FormControl; language: FormControl; + publisher: FormControl; + standardLicense: FormControl; + endpointDocumentation: FormControl; } diff --git a/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/http-datasource-query-param-form-model.ts b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/http-datasource-query-param-form-model.ts new file mode 100644 index 000000000..719bf5448 --- /dev/null +++ b/src/app/routes/connector-ui/asset-page/asset-create-dialog/model/http-datasource-query-param-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetEditorDialog > Datasource > HTTP/REST > Header + */ +export interface HttpDatasourceQueryParamFormModel { + paramName: FormControl; + paramValue: FormControl; +} + +/** + * Form Value for AssetEditorDialog > Datasource > HTTP/REST > QueryParam + */ +export type HttpDatasourceQueryParamFormValue = + ɵFormGroupValue; diff --git a/src/app/routes/connector-ui/connector-ui.component.scss b/src/app/routes/connector-ui/connector-ui.component.scss index e2631bdc3..67fc9a63d 100644 --- a/src/app/routes/connector-ui/connector-ui.component.scss +++ b/src/app/routes/connector-ui/connector-ui.component.scss @@ -34,9 +34,3 @@ cursor: pointer; background-color: #dedede; } - -::ng-deep app-navigation .mat-drawer-inner-container { - display: flex; - flex-direction: column; - overflow: hidden; -} diff --git a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data.ts b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data.ts index ffb76b965..03bee860c 100644 --- a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data.ts +++ b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data.ts @@ -1,3 +1,6 @@ +import {Asset} from '../../../../core/services/models/asset'; + export interface ContractAgreementTransferDialogData { contractId: string; + asset: Asset; } diff --git a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model.ts b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model.ts index 20ed65fb1..07faaad45 100644 --- a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model.ts +++ b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model.ts @@ -5,6 +5,7 @@ import { ɵFormGroupValue, } from '@angular/forms'; import {DataAddressType} from '../../../../component-library/data-address/data-address-type-select/data-address-type'; +import {HttpDatasourceQueryParamFormModel} from '../../asset-page/asset-create-dialog/model/http-datasource-query-param-form-model'; import {HttpDatasinkAuthHeaderType} from './http-datasink-auth-header-type'; import {HttpDatasinkHeaderFormModel} from './http-datasink-header-form-model'; @@ -23,6 +24,9 @@ export interface ContractAgreementTransferDialogFormModel { // Custom Datasink JSON dataDestination: FormControl; + // Custom Transfer Process Request JSON + transferProcessRequest: FormControl; + // Http Datasink httpUrl: FormControl; httpMethod: FormControl; @@ -32,4 +36,13 @@ export interface ContractAgreementTransferDialogFormModel { httpAuthHeaderValue: FormControl; httpAuthHeaderSecretName: FormControl; httpHeaders: FormArray>; + + // Http Datasource Parameterization + httpProxiedPath: FormControl; + httpProxiedMethod: FormControl; + httpProxiedQueryParams: FormArray< + FormGroup + >; + httpProxiedBody: FormControl; + httpProxiedBodyContentType: FormControl; } diff --git a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form.ts b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form.ts index 2094ce251..37b68bff3 100644 --- a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form.ts +++ b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form.ts @@ -5,6 +5,7 @@ import {switchDisabledControls} from '../../../../core/utils/form-group-utils'; import {jsonValidator} from '../../../../core/validators/json-validator'; import {urlValidator} from '../../../../core/validators/url-validator'; import {HttpDatasourceAuthHeaderType} from '../../asset-page/asset-create-dialog/model/http-datasource-auth-header-type'; +import {HttpDatasourceQueryParamFormModel} from '../../asset-page/asset-create-dialog/model/http-datasource-query-param-form-model'; import { ContractAgreementTransferDialogFormModel, ContractAgreementTransferDialogFormValue, @@ -39,6 +40,7 @@ export class ContractAgreementTransferDialogForm { this.formBuilder.nonNullable.group({ dataAddressType: 'Http' as DataAddressType, dataDestination: ['', [Validators.required, jsonValidator]], + transferProcessRequest: ['', [Validators.required, jsonValidator]], // Http Datasink Fields httpUrl: ['', [Validators.required, urlValidator]], @@ -52,6 +54,14 @@ export class ContractAgreementTransferDialogForm { httpHeaders: this.formBuilder.array( new Array>(), ), + + httpProxiedPath: [''], + httpProxiedMethod: ['GET'], + httpProxiedQueryParams: this.formBuilder.array( + new Array>(), + ), + httpProxiedBody: [''], + httpProxiedBodyContentType: [''], }); switchDisabledControls( @@ -60,6 +70,9 @@ export class ContractAgreementTransferDialogForm { const customDataAddressJson = value.dataAddressType === 'Custom-Data-Address-Json'; + const customTransferProcessRequest = + value.dataAddressType === 'Custom-Transfer-Process-Request'; + const http = value.dataAddressType === 'Http'; const httpAuth = value.httpAuthHeaderType !== 'None'; const httpAuthByValue = value.httpAuthHeaderType === 'Value'; @@ -70,6 +83,7 @@ export class ContractAgreementTransferDialogForm { // Custom Datasink JSON dataDestination: customDataAddressJson, + transferProcessRequest: customTransferProcessRequest, // Http Datasink Fields httpUrl: http, @@ -81,6 +95,12 @@ export class ContractAgreementTransferDialogForm { httpAuthHeaderSecretName: http && httpAuthByVault, httpHeaders: http, + + httpProxiedPath: !customTransferProcessRequest, + httpProxiedMethod: !customTransferProcessRequest, + httpProxiedQueryParams: !customTransferProcessRequest, + httpProxiedBody: !customTransferProcessRequest, + httpProxiedBodyContentType: !customTransferProcessRequest, }; }, ); @@ -94,6 +114,13 @@ export class ContractAgreementTransferDialogForm { }); } + buildQueryParamFormGroup(): FormGroup { + return this.formBuilder.nonNullable.group({ + paramName: ['', Validators.required], + paramValue: [''], + }); + } + onHttpHeadersAddClick() { this.all.controls.httpHeaders.push(this.buildHeaderFormGroup()); } @@ -101,4 +128,14 @@ export class ContractAgreementTransferDialogForm { onHttpHeadersRemoveClick(index: number) { this.all.controls.httpHeaders.removeAt(index); } + + onHttpQueryParamsAddClick() { + this.all.controls.httpProxiedQueryParams.push( + this.buildQueryParamFormGroup(), + ); + } + + onHttpQueryParamsRemoveClick(index: number) { + this.all.controls.httpProxiedQueryParams.removeAt(index); + } } diff --git a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.html b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.html index c5eaddbba..07ca81869 100644 --- a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.html +++ b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.html @@ -1,8 +1,9 @@

Initiate Transfer

-
Datasink
+
Datasink
+ Initiate Transfer form.all.controls.dataAddressType "> - + Custom Datasink Config (JSON) + + + {{ validationMessages.invalidJsonMessage }} + + + + + + Custom Transfer Process Request (JSON) {{ validationMessages.invalidJsonMessage }} + + JSON Fields "assetId", "contractId" and "connectorAddress" will be + overridden. + @@ -36,9 +59,11 @@

Initiate Transfer

class="w-1/3"> Method - {{ - method - }} + {{ method }} Initiate Transfer
+
Authentication
@@ -175,6 +201,125 @@

Initiate Transfer

+ + +
Datasource Parameterization
+ +
+ + + + Method + + {{ method }} + + + Default: {{ data.asset.httpDefaultMethod ?? 'Unknown' }} + + + + + + + + Custom Path Segment + + + Will be added to Base URL. Default: + {{ data.asset.httpDefaultPath ?? 'Unknown' }} + + + +
+ + +
+ + + Query Param Name + + + + + + Value + + + + + +
+ +
+ + +
+
+ + + + + Content Type + + + + + + Body + + + +
diff --git a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.ts b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.ts index fe1db60f1..539538951 100644 --- a/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.ts +++ b/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.ts @@ -2,11 +2,10 @@ import {Component, Inject, OnDestroy} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {Subject} from 'rxjs'; import {finalize} from 'rxjs/operators'; -import { - ContractAgreementService, - DataAddressDto, -} from '../../../../core/services/api/legacy-managent-api-client'; +import {ContractAgreementTransferRequest} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; import {AssetEntryBuilder} from '../../../../core/services/asset-entry-builder'; +import {DataAddressMapper} from '../../../../core/services/data-address-mapper'; import {HttpRequestParamsMapper} from '../../../../core/services/http-params-mapper.service'; import {NotificationService} from '../../../../core/services/notification.service'; import {ValidationMessages} from '../../../../core/validators/validation-messages'; @@ -23,16 +22,34 @@ import {ContractAgreementTransferDialogResult} from './contract-agreement-transf export class ContractAgreementTransferDialogComponent implements OnDestroy { loading = false; - methods = ['POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']; + dataSinkMethods = ['POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']; + dataSourceMethods = ['GET', ...this.dataSinkMethods]; + + get proxyMethod(): boolean { + return this.data.asset.httpProxyMethod == true; + } + + get proxyPath(): boolean { + return this.data.asset.httpProxyPath == true; + } + + get proxyQueryParams(): boolean { + return this.data.asset.httpProxyQueryParams == true; + } + + get proxyBody(): boolean { + return this.data.asset.httpProxyBody == true; + } constructor( public form: ContractAgreementTransferDialogForm, public validationMessages: ValidationMessages, private dialogRef: MatDialogRef, - private contractAgreementService: ContractAgreementService, + private edcApiService: EdcApiService, private notificationService: NotificationService, private httpRequestParamsMapper: HttpRequestParamsMapper, - @Inject(MAT_DIALOG_DATA) private data: ContractAgreementTransferDialogData, + private dataAddressMapper: DataAddressMapper, + @Inject(MAT_DIALOG_DATA) public data: ContractAgreementTransferDialogData, ) {} onSave() { @@ -42,11 +59,8 @@ export class ContractAgreementTransferDialogComponent implements OnDestroy { this.loading = true; this.form.all.disable(); - this.contractAgreementService - .initiateTransfer( - this.data.contractId, - this.buildDataAddressDto(this.form.value), - ) + this.edcApiService + .initiateTranfer(this.buildTransferRequest(this.form.value)) .pipe( finalize(() => { this.loading = false; @@ -66,21 +80,6 @@ export class ContractAgreementTransferDialogComponent implements OnDestroy { }); } - private buildDataAddressDto( - formValue: ContractAgreementTransferDialogFormValue, - ): DataAddressDto { - switch (formValue.dataAddressType) { - case 'Custom-Data-Address-Json': - return JSON.parse(formValue.dataDestination?.trim()!!); - case 'Http': - return this.httpRequestParamsMapper.buildHttpDataAddressDto(formValue); - default: - throw new Error( - `Invalid Data Address Type ${formValue.dataAddressType}`, - ); - } - } - private close(params: ContractAgreementTransferDialogResult) { this.dialogRef.close(params); } @@ -91,4 +90,40 @@ export class ContractAgreementTransferDialogComponent implements OnDestroy { this.ngOnDestroy$.next(null); this.ngOnDestroy$.complete(); } + + private buildTransferRequest( + value: ContractAgreementTransferDialogFormValue, + ): ContractAgreementTransferRequest { + if (value.dataAddressType === 'Custom-Transfer-Process-Request') { + const customJson: any = JSON.parse( + value.transferProcessRequest?.trim() ?? '', + ); + customJson.assetId = this.data.asset.id; + customJson.contractId = this.data.contractId; + customJson.connectorAddress = this.data.asset.originator; + + return { + type: 'CUSTOM_JSON', + customJson: JSON.stringify(customJson), + }; + } + + let transferRequestProperties = + this.httpRequestParamsMapper.encodeHttpProxyTransferRequestPropreties( + this.data.asset, + value, + ); + + let dataAddressProperties = + this.dataAddressMapper.buildDataAddressProperties(value); + + return { + type: 'PARAMS_ONLY', + params: { + contractAgreementId: this.data.contractId, + properties: transferRequestProperties, + dataSinkProperties: dataAddressProperties, + }, + }; + } } diff --git a/src/styles.scss b/src/styles.scss index 176810ab1..02246d0e1 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -171,3 +171,9 @@ body { text-overflow: ellipsis; overflow: hidden !important; } + +.mat-drawer-inner-container { + display: flex; + flex-direction: column; + overflow-x: hidden !important; +}