From 69b00a7a266321c3b304aa260de219e6126704d9 Mon Sep 17 00:00:00 2001 From: amentis Date: Mon, 26 Feb 2024 19:40:31 +0200 Subject: [PATCH] add prefilling source frontend --- dmp-frontend/src/app/app-routing.module.ts | 12 + .../external-fetcher-api-http-method-type.ts | 4 + .../enum/external-fetcher-source-type.ts | 4 + .../app/core/common/enum/permission.enum.ts | 6 + ...ence-type-external-api-http-method-type.ts | 4 - .../common/enum/reference-type-source-type.ts | 4 - .../src/app/core/core-service.module.ts | 4 +- .../external-fetcher/external-fetcher.ts | 136 ++++ .../prefilling-source/prefilling-source.ts | 43 ++ .../model/reference-type/reference-type.ts | 135 +--- .../core/query/prefilling-source.lookup.ts | 21 + .../prefilling-source.service.ts | 97 +++ .../services/semantic/semantics.service.ts | 10 + .../services/utilities/enum-utils.service.ts | 16 +- .../prefilling-source-editor.component.html | 136 ++++ .../prefilling-source-editor.component.scss | 43 ++ .../prefilling-source-editor.component.ts | 264 +++++++ .../editor/prefilling-source-editor.model.ts | 244 ++++++ .../prefilling-source-editor.resolver.ts | 101 +++ .../prefilling-source-editor.service.ts | 15 + ...ling-source-listing-filters.component.html | 36 + ...ling-source-listing-filters.component.scss | 25 + ...illing-source-listing-filters.component.ts | 94 +++ .../prefilling-source-listing.component.html | 96 +++ .../prefilling-source-listing.component.scss | 83 ++ .../prefilling-source-listing.component.ts | 171 +++++ .../prefilling-source.module.ts | 40 + .../prefilling-source.routing.ts | 58 ++ .../reference-type-editor.component.html | 67 +- .../editor/reference-type-editor.component.ts | 73 +- .../editor/reference-type-editor.model.ts | 706 +----------------- .../editor/reference-type-editor.resolver.ts | 69 +- .../reference-type/reference-type.module.ts | 4 +- .../dmp/overview/dmp-overview.component.html | 2 +- .../external-fetcher-source-editor.model.ts | 697 +++++++++++++++++ .../external-fetcher-source.component.html | 359 +++++++++ .../external-fetcher-source.component.scss | 0 .../external-fetcher-source.component.ts | 103 +++ .../external-fetcher-source.module.ts | 24 + .../src/app/ui/sidebar/sidebar.component.ts | 1 + dmp-frontend/src/assets/i18n/en.json | 64 +- 41 files changed, 3095 insertions(+), 976 deletions(-) create mode 100644 dmp-frontend/src/app/core/common/enum/external-fetcher-api-http-method-type.ts create mode 100644 dmp-frontend/src/app/core/common/enum/external-fetcher-source-type.ts delete mode 100644 dmp-frontend/src/app/core/common/enum/reference-type-external-api-http-method-type.ts delete mode 100644 dmp-frontend/src/app/core/common/enum/reference-type-source-type.ts create mode 100644 dmp-frontend/src/app/core/model/external-fetcher/external-fetcher.ts create mode 100644 dmp-frontend/src/app/core/model/prefilling-source/prefilling-source.ts create mode 100644 dmp-frontend/src/app/core/query/prefilling-source.lookup.ts create mode 100644 dmp-frontend/src/app/core/services/prefilling-source/prefilling-source.service.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.html create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.scss create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.model.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.resolver.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.service.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.html create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.scss create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.html create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.scss create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.module.ts create mode 100644 dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.routing.ts create mode 100644 dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source-editor.model.ts create mode 100644 dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.html create mode 100644 dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.scss create mode 100644 dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.ts create mode 100644 dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.module.ts diff --git a/dmp-frontend/src/app/app-routing.module.ts b/dmp-frontend/src/app/app-routing.module.ts index f5385cbfa..1f33204c8 100644 --- a/dmp-frontend/src/app/app-routing.module.ts +++ b/dmp-frontend/src/app/app-routing.module.ts @@ -258,6 +258,18 @@ const appRoutes: Routes = [ }) }, }, + { + path: 'prefilling-sources', + loadChildren: () => import('./ui/admin/prefilling-source/prefilling-source.module').then(m => m.PrefillingSourceModule), + data: { + authContext: { + permissions: [AppPermission.ViewPrefillingSourcePage] + }, + ...BreadcrumbService.generateRouteDataConfiguration({ + title: 'BREADCRUMBS.PREFILLING-SOURCES' + }) + }, + }, { path: 'tenants', loadChildren: () => import('./ui/admin/tenant/tenant.module').then(m => m.TenantModule), diff --git a/dmp-frontend/src/app/core/common/enum/external-fetcher-api-http-method-type.ts b/dmp-frontend/src/app/core/common/enum/external-fetcher-api-http-method-type.ts new file mode 100644 index 000000000..22c528703 --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/external-fetcher-api-http-method-type.ts @@ -0,0 +1,4 @@ +export enum ExternalFetcherApiHTTPMethodType { + GET = 0, + POST = 1 +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/common/enum/external-fetcher-source-type.ts b/dmp-frontend/src/app/core/common/enum/external-fetcher-source-type.ts new file mode 100644 index 000000000..f7d504c7d --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/external-fetcher-source-type.ts @@ -0,0 +1,4 @@ +export enum ExternalFetcherSourceType { + API = 0, + STATIC = 1 +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/common/enum/permission.enum.ts b/dmp-frontend/src/app/core/common/enum/permission.enum.ts index 0077e5859..bfa9e4966 100644 --- a/dmp-frontend/src/app/core/common/enum/permission.enum.ts +++ b/dmp-frontend/src/app/core/common/enum/permission.enum.ts @@ -36,6 +36,7 @@ export enum AppPermission { ViewNotificationTemplatePage = "ViewNotificationTemplatePage", ViewMineInAppNotificationPage = "ViewMineInAppNotificationPage", ViewNotificationPage = "ViewNotificationPage", + ViewPrefillingSourcePage = "ViewPrefillingSourcePage", //ReferenceType BrowseReferenceType = "BrowseReferenceType", @@ -67,5 +68,10 @@ export enum AppPermission { BrowseNotificationTemplate = "BrowseNotificationTemplate", EditNotificationTemplate = "EditNotificationTemplate", DeleteNotificationTemplate = "DeleteNotificationTemplate", + + //Prefilling Source + BrowsePrefillingSource= "BrowsePrefillingSource", + EditPrefillingSource = "EditPrefillingSource", + DeletePrefillingSource = "DeletePrefillingSource", } diff --git a/dmp-frontend/src/app/core/common/enum/reference-type-external-api-http-method-type.ts b/dmp-frontend/src/app/core/common/enum/reference-type-external-api-http-method-type.ts deleted file mode 100644 index fa98d8e14..000000000 --- a/dmp-frontend/src/app/core/common/enum/reference-type-external-api-http-method-type.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum ReferenceTypeExternalApiHTTPMethodType { - GET = 0, - POST = 1 -} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/common/enum/reference-type-source-type.ts b/dmp-frontend/src/app/core/common/enum/reference-type-source-type.ts deleted file mode 100644 index 3f0de2272..000000000 --- a/dmp-frontend/src/app/core/common/enum/reference-type-source-type.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum ReferenceTypeSourceType { - API = 0, - STATIC = 1 -} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index 5eac92f6e..f7b5e8ecd 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -47,6 +47,7 @@ import { FileTransformerHttpService } from './services/file-transformer/file-tra import { InAppNotificationService } from './services/inapp-notification/inapp-notification.service'; import { NotificationService } from './services/notification/notification-service'; import { SemanticsService } from './services/semantic/semantics.service'; +import { PrefillingSourceService } from './services/prefilling-source/prefilling-source.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -112,7 +113,8 @@ export class CoreServiceModule { FileTransformerHttpService, InAppNotificationService, NotificationService, - SemanticsService + SemanticsService, + PrefillingSourceService ], }; } diff --git a/dmp-frontend/src/app/core/model/external-fetcher/external-fetcher.ts b/dmp-frontend/src/app/core/model/external-fetcher/external-fetcher.ts new file mode 100644 index 000000000..10b0a7a71 --- /dev/null +++ b/dmp-frontend/src/app/core/model/external-fetcher/external-fetcher.ts @@ -0,0 +1,136 @@ +import { ExternalFetcherApiHTTPMethodType } from "@app/core/common/enum/external-fetcher-api-http-method-type"; +import { ReferenceType } from "../reference-type/reference-type"; +import { ExternalFetcherSourceType } from "@app/core/common/enum/external-fetcher-source-type"; +import { Guid } from "@common/types/guid"; + +export interface ExternalFetcherBaseSourceConfiguration extends ExternalFetcherApiSourceConfiguration, ExternalFetcherStaticOptionSourceConfiguration{ + type: ExternalFetcherSourceType; + key: string; + label: string; + ordinal: number; + referenceTypeDependencies?: ReferenceType[]; +} + +export interface ExternalFetcherApiSourceConfiguration{ + url: string; + results: ResultsConfiguration; + paginationPath: string; + contentType: string; + firstPage: string; + httpMethod: ExternalFetcherApiHTTPMethodType; + requestBody?: string; + filterType?: string; + auth: AuthenticationConfiguration; + queries?: QueryConfig[]; +} + +export interface ResultsConfiguration{ + resultsArrayPath: string; + fieldsMapping: ResultFieldsMappingConfiguration[]; +} + + +export interface ResultFieldsMappingConfiguration{ + code: string; + responsePath: string; +} + +export interface AuthenticationConfiguration{ + enabled: boolean; + authUrl: string; + authMethod: ExternalFetcherApiHTTPMethodType; + authTokenPath: string; + authRequestBody: string; + type: string; +} + +export interface QueryConfig{ + name: string; + defaultValue: string; + cases: QueryCaseConfig[]; +} + +export interface QueryCaseConfig{ + likePattern: string, + separator: string; + value: string; + referenceType?: ReferenceType; + referenceTypeSourceKey: string +} + +export interface ExternalFetcherStaticOptionSourceConfiguration{ + options: StaticOption[]; +} + +export interface StaticOption{ + code: string; + value: string; +} + +// +// Persist +// + +export interface ExternalFetcherBaseSourceConfigurationPersist extends ExternalFetcherApiSourceConfigurationPersist, ExternalFetcherStaticOptionSourceConfigurationPersist{ + type: ExternalFetcherSourceType; + key: string; + label: string; + ordinal: number; + referenceTypeDependencyIds?: Guid[]; +} + +export interface ExternalFetcherApiSourceConfigurationPersist{ + url: string; + results: ResultsConfigurationPersist; + paginationPath: string; + contentType: string; + firstPage: string; + httpMethod: ExternalFetcherApiHTTPMethodType; + requestBody?: string; + filterType?: string; + auth: AuthenticationConfigurationPersist; + queries?: QueryConfigPersist[]; +} + +export interface ResultsConfigurationPersist{ + resultsArrayPath: string; + fieldsMapping: ResultFieldsMappingConfigurationPersist[]; +} + + +export interface ResultFieldsMappingConfigurationPersist{ + code: string; + responsePath: string; +} + +export interface AuthenticationConfigurationPersist{ + enabled: boolean; + authUrl: string; + authMethod: ExternalFetcherApiHTTPMethodType; + authTokenPath: string; + authRequestBody: string; + type: string; +} + +export interface QueryConfigPersist{ + name: string; + defaultValue: string; + cases: QueryCaseConfigPersist[]; +} + +export interface QueryCaseConfigPersist{ + likePattern: string, + separator: string; + value: string; + referenceTypeId: Guid; + referenceTypeSourceKey: string +} + +export interface ExternalFetcherStaticOptionSourceConfigurationPersist { + options: StaticOptionPersist[]; +} + +export interface StaticOptionPersist{ + code: string; + value: string; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/prefilling-source/prefilling-source.ts b/dmp-frontend/src/app/core/model/prefilling-source/prefilling-source.ts new file mode 100644 index 000000000..eb18185ad --- /dev/null +++ b/dmp-frontend/src/app/core/model/prefilling-source/prefilling-source.ts @@ -0,0 +1,43 @@ +import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model"; +import { ExternalFetcherBaseSourceConfiguration, ExternalFetcherBaseSourceConfigurationPersist} from "../external-fetcher/external-fetcher"; + +export interface PrefillingSource extends BaseEntity{ + label: string; + definition: PrefillingSourceDefinition; +} + +export interface PrefillingSourceDefinition{ + fields: PrefillingSourceDefinitionField[]; + searchConfiguration: ExternalFetcherBaseSourceConfiguration; + getConfiguration: ExternalFetcherBaseSourceConfiguration; +} + +export interface PrefillingSourceDefinitionField { + code: string; + systemFieldTarget: string; + semanticTarget: string; + trimRegex: string; + fixedValue: string; +} + +// Persist + +export interface PrefillingSourcePersist extends BaseEntityPersist{ + label: string; + definition: PrefillingSourceDefinitionPersist; +} + +export interface PrefillingSourceDefinitionPersist{ + fields: PrefillingSourceDefinitionFieldPersist[]; + searchConfiguration: ExternalFetcherBaseSourceConfigurationPersist; + getConfiguration: ExternalFetcherBaseSourceConfigurationPersist; +} + +export interface PrefillingSourceDefinitionFieldPersist { + code: string; + systemFieldTarget: string; + semanticTarget: string; + trimRegex: string; + fixedValue: string; +} + diff --git a/dmp-frontend/src/app/core/model/reference-type/reference-type.ts b/dmp-frontend/src/app/core/model/reference-type/reference-type.ts index f0ba458b3..e3410babf 100644 --- a/dmp-frontend/src/app/core/model/reference-type/reference-type.ts +++ b/dmp-frontend/src/app/core/model/reference-type/reference-type.ts @@ -1,8 +1,6 @@ import { ReferenceFieldDataType } from "@app/core/common/enum/reference-field-data-type"; -import { ReferenceTypeExternalApiHTTPMethodType } from "@app/core/common/enum/reference-type-external-api-http-method-type"; -import { ReferenceTypeSourceType } from "@app/core/common/enum/reference-type-source-type"; import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model"; -import { Guid } from "@common/types/guid"; +import { ExternalFetcherBaseSourceConfiguration, ExternalFetcherBaseSourceConfigurationPersist } from "../external-fetcher/external-fetcher"; export interface ReferenceType extends BaseEntity{ name: string; @@ -12,7 +10,7 @@ export interface ReferenceType extends BaseEntity{ export interface ReferenceTypeDefinition{ fields: ReferenceTypeField[]; - sources: ReferenceTypeSourceBaseConfiguration[]; + sources: ExternalFetcherBaseSourceConfiguration[]; } export interface ReferenceTypeField { @@ -22,69 +20,7 @@ export interface ReferenceTypeField { dataType: ReferenceFieldDataType; } -export interface ReferenceTypeSourceBaseConfiguration extends ReferenceTypeSourceExternalApiConfiguration, ReferenceTypeSourceStaticOptionConfiguration{ - type: ReferenceTypeSourceType; - key: string; - label: string; - ordinal: number; - referenceTypeDependencies?: ReferenceType[]; -} -export interface ReferenceTypeSourceExternalApiConfiguration{ - url: string; - results: ResultsConfiguration; - paginationPath: string; - contentType: string; - firstPage: string; - httpMethod: ReferenceTypeExternalApiHTTPMethodType; - requestBody?: string; - filterType?: string; - auth: AuthenticationConfiguration; - queries?: QueryConfig[]; -} - -export interface ResultsConfiguration{ - resultsArrayPath: string; - fieldsMapping: ResultFieldsMappingConfiguration[]; -} - - -export interface ResultFieldsMappingConfiguration{ - code: string; - responsePath: string; -} - -export interface AuthenticationConfiguration{ - enabled: boolean; - authUrl: string; - authMethod: ReferenceTypeExternalApiHTTPMethodType; - authTokenPath: string; - authRequestBody: string; - type: string; -} - -export interface QueryConfig{ - name: string; - defaultValue: string; - cases: QueryCaseConfig[]; -} - -export interface QueryCaseConfig{ - likePattern: string, - separator: string; - value: string; - referenceType?: ReferenceType; - referenceTypeSourceKey: string -} - -export interface ReferenceTypeSourceStaticOptionConfiguration{ - options: ReferenceTypeStaticOption[]; -} - -export interface ReferenceTypeStaticOption{ - code: string; - value: string; -} // Persist @@ -96,7 +32,7 @@ export interface ReferenceTypePersist extends BaseEntityPersist{ export interface ReferenceTypeDefinitionPersist{ fields?: ReferenceTypeFieldPersist[]; - sources: ReferenceTypeSourceBaseConfigurationPersist[]; + sources: ExternalFetcherBaseSourceConfigurationPersist[]; } export interface ReferenceTypeFieldPersist { @@ -105,68 +41,3 @@ export interface ReferenceTypeFieldPersist { description: string; dataType: ReferenceFieldDataType; } - -export interface ReferenceTypeSourceBaseConfigurationPersist extends ReferenceTypeSourceExternalApiConfigurationPersist, ReferenceTypeSourceStaticOptionConfigurationPersist{ - type: ReferenceTypeSourceType; - key: string; - label: string; - ordinal: number; - referenceTypeDependencyIds?: Guid[]; -} - -export interface ReferenceTypeSourceExternalApiConfigurationPersist{ - url: string; - results: ResultsConfigurationPersist; - paginationPath: string; - contentType: string; - firstPage: string; - httpMethod: ReferenceTypeExternalApiHTTPMethodType; - requestBody?: string; - filterType?: string; - auth: AuthenticationConfigurationPersist; - queries?: QueryConfigPersist[]; -} - -export interface ResultsConfigurationPersist{ - resultsArrayPath: string; - fieldsMapping: ResultFieldsMappingConfigurationPersist[]; -} - - - -export interface ResultFieldsMappingConfigurationPersist{ - code: string; - responsePath: string; -} - -export interface AuthenticationConfigurationPersist{ - enabled: boolean; - authUrl: string; - authMethod: ReferenceTypeExternalApiHTTPMethodType; - authTokenPath: string; - authRequestBody: string; - type: string; -} - -export interface QueryConfigPersist{ - name: string; - defaultValue: string; - cases: QueryCaseConfigPersist[]; -} - -export interface QueryCaseConfigPersist{ - likePattern: string, - separator: string; - value: string; - referenceTypeId: Guid; - referenceTypeSourceKey: string -} - -export interface ReferenceTypeSourceStaticOptionConfigurationPersist { - options: ReferenceTypeStaticOptionPersist[]; -} - -export interface ReferenceTypeStaticOptionPersist{ - code: string; - value: string; -} diff --git a/dmp-frontend/src/app/core/query/prefilling-source.lookup.ts b/dmp-frontend/src/app/core/query/prefilling-source.lookup.ts new file mode 100644 index 000000000..154fb871d --- /dev/null +++ b/dmp-frontend/src/app/core/query/prefilling-source.lookup.ts @@ -0,0 +1,21 @@ +import { Lookup } from "@common/model/lookup"; +import { Guid } from "@common/types/guid"; +import { IsActive } from "../common/enum/is-active.enum"; + +export class PrefillingSourceLookup extends Lookup implements PrefillingSourceFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + isActive: IsActive[]; + + constructor() { + super(); + } +} + +export interface PrefillingSourceFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + isActive: IsActive[]; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/services/prefilling-source/prefilling-source.service.ts b/dmp-frontend/src/app/core/services/prefilling-source/prefilling-source.service.ts new file mode 100644 index 000000000..f2d1fcd73 --- /dev/null +++ b/dmp-frontend/src/app/core/services/prefilling-source/prefilling-source.service.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@angular/core'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { PrefillingSource, PrefillingSourcePersist } from '@app/core/model/prefilling-source/prefilling-source'; +import { PrefillingSourceLookup } from '@app/core/query/prefilling-source.lookup'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; +import { QueryResult } from '@common/model/query-result'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { Guid } from '@common/types/guid'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; +import { ConfigurationService } from '../configuration/configuration.service'; +import { BaseHttpV2Service } from '../http/base-http-v2.service'; + +@Injectable() +export class PrefillingSourceService { + + constructor( + private http: BaseHttpV2Service, + private configurationService: ConfigurationService, + private filterService: FilterService + ) { + } + + private get apiBase(): string { return `${this.configurationService.server}prefilling-source`; } + + query(q: PrefillingSourceLookup): Observable> { + const url = `${this.apiBase}/query`; + return this.http.post>(url, q).pipe(catchError((error: any) => throwError(error))); + } + + getSingle(id: Guid, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/${id}`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + persist(item: PrefillingSourcePersist): Observable { + const url = `${this.apiBase}/persist`; + + return this.http + .post(url, item).pipe( + catchError((error: any) => throwError(error))); + } + + delete(id: Guid): Observable { + const url = `${this.apiBase}/${id}`; + + return this.http + .delete(url).pipe( + catchError((error: any) => throwError(error))); + } + + // + // Autocomplete Commons + // + // + public singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { + initialItems: (data?: any) => this.query(this.buildAutocompleteLookup()).pipe(map(x => x.items)), + filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteLookup(searchQuery)).pipe(map(x => x.items)), + getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])), + displayFn: (item: PrefillingSource) => item.label, + titleFn: (item: PrefillingSource) => item.label, + valueAssign: (item: PrefillingSource) => item.id, + }; + + public multipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = { + initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)), + getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)), + displayFn: (item: PrefillingSource) => item.label, + titleFn: (item: PrefillingSource) => item.label, + valueAssign: (item: PrefillingSource) => item.id, + }; + + private buildAutocompleteLookup(like?: string, excludedIds?: Guid[], ids?: Guid[]): PrefillingSourceLookup { + const lookup: PrefillingSourceLookup = new PrefillingSourceLookup(); + lookup.page = { size: 100, offset: 0 }; + if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; } + if (ids && ids.length > 0) { lookup.ids = ids; } + lookup.isActive = [IsActive.Active]; + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label) + ] + }; + lookup.order = { items: [nameof(x => x.label)] }; + if (like) { lookup.like = this.filterService.transformLike(like); } + return lookup; + } + +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/services/semantic/semantics.service.ts b/dmp-frontend/src/app/core/services/semantic/semantics.service.ts index c8b7b8ca5..b229e2e30 100644 --- a/dmp-frontend/src/app/core/services/semantic/semantics.service.ts +++ b/dmp-frontend/src/app/core/services/semantic/semantics.service.ts @@ -7,6 +7,7 @@ import { catchError, map } from 'rxjs/operators'; import { ConfigurationService } from '../configuration/configuration.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service'; import { SemanticsLookup } from '@app/core/query/semantic.lookup'; +import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; @Injectable() export class SemanticsService { @@ -26,6 +27,15 @@ export class SemanticsService { // Autocomplete + + singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { + initialItems: (data?: any) => this.searchSemantics(this.buildSemanticsAutocompleteLookup()).pipe(map(x => x)), + filterFn: (searchQuery: string, data?: any) => this.searchSemantics(this.buildSemanticsAutocompleteLookup(searchQuery)).pipe(map(x => x)), + displayFn: (item) => item, + titleFn: (item) => item, + valueAssign: (item) => item, + }; + multipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = { initialItems: (data?: any) => this.searchSemantics(this.buildSemanticsAutocompleteLookup()).pipe(map(x => x)), filterFn: (searchQuery: string, data?: any) => this.searchSemantics(this.buildSemanticsAutocompleteLookup(searchQuery)).pipe(map(x => x)), diff --git a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts index 42277c26f..19d7510b5 100644 --- a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts +++ b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts @@ -24,8 +24,8 @@ import { NotificationType } from '@app/core/common/enum/notification-type'; import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type'; import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; -import { ReferenceTypeExternalApiHTTPMethodType } from '@app/core/common/enum/reference-type-external-api-http-method-type'; -import { ReferenceTypeSourceType } from '@app/core/common/enum/reference-type-source-type'; +import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type'; +import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type'; import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type'; import { SupportiveMaterialFieldType } from '@app/core/common/enum/supportive-material-field-type'; import { UserDescriptionTemplateRole } from '@app/core/common/enum/user-description-template-role'; @@ -174,10 +174,10 @@ export class EnumUtils { } } - toReferenceTypeSourceTypeString(status: ReferenceTypeSourceType): string { + toExternalFetcherSourceTypeString(status: ExternalFetcherSourceType): string { switch (status) { - case ReferenceTypeSourceType.API: return this.language.instant('TYPES.REFERENCE-TYPE-SOURCE-TYPE.API'); - case ReferenceTypeSourceType.STATIC: return this.language.instant('TYPES.REFERENCE-TYPE-SOURCE-TYPE.STATIC'); + case ExternalFetcherSourceType.API: return this.language.instant('TYPES.REFERENCE-TYPE-SOURCE-TYPE.API'); + case ExternalFetcherSourceType.STATIC: return this.language.instant('TYPES.REFERENCE-TYPE-SOURCE-TYPE.STATIC'); } } @@ -189,10 +189,10 @@ export class EnumUtils { } - toReferenceTypeExternalApiHTTPMethodTypeString(status: ReferenceTypeExternalApiHTTPMethodType): string { + toExternalFetcherApiHTTPMethodTypeString(status: ExternalFetcherApiHTTPMethodType): string { switch (status) { - case ReferenceTypeExternalApiHTTPMethodType.GET: return this.language.instant('TYPES.REFERENCE-TYPE-EXTERNAL-API-HTTP-METHOD-TYPE.GET'); - case ReferenceTypeExternalApiHTTPMethodType.POST: return this.language.instant('TYPES.REFERENCE-TYPE-EXTERNAL-API-HTTP-METHOD-TYPE.POST'); + case ExternalFetcherApiHTTPMethodType.GET: return this.language.instant('TYPES.REFERENCE-TYPE-EXTERNAL-API-HTTP-METHOD-TYPE.GET'); + case ExternalFetcherApiHTTPMethodType.POST: return this.language.instant('TYPES.REFERENCE-TYPE-EXTERNAL-API-HTTP-METHOD-TYPE.POST'); } } diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.html b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.html new file mode 100644 index 000000000..369a7f40a --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.html @@ -0,0 +1,136 @@ +
+
+ +
+
+

{{'PREFILLING-SOURCE-EDITOR.NEW' | translate}}

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + {{'PREFILLING-SOURCE-EDITOR.NEW' | translate}} + + +
+
+ + {{'PREFILLING-SOURCE-EDITOR.FIELDS.LABEL' | translate}} + + {{formGroup.get('label').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ +
+

+ +

+
+
+
+
+ {{'PREFILLING-SOURCE-EDITOR.FIELDS.FIELD' | translate}} {{fieldIndex + 1}} +
+
+ +
+
+
+
+ + {{'PREFILLING-SOURCE-EDITOR.FIELDS.CODE' | translate}} + + {{field.get('code').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'PREFILLING-SOURCE-EDITOR.FIELDS.SYSTEM-TARGET' | translate}} + + {{field.get('systemFieldTarget').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'PREFILLING-SOURCE-EDITOR.FIELDS.SEMANTIC-TARGET' | translate}} + + {{field.get('semanticTarget').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'PREFILLING-SOURCE-EDITOR.FIELDS.TRIM-REGEX' | translate}} + + {{field.get('trimRegex').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'PREFILLING-SOURCE-EDITOR.FIELDS.FIXED-VALUE' | translate}} + + {{field.get('fixedValue').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+ +
+
+
+
+ + + + {{'PREFILLING-SOURCE-EDITOR.FIELDS.SOURCE-CONFIGURATION' | translate}} + + + +
+ + {{'PREFILLING-SOURCE-EDITOR.FIELDS.GET-SOURCE-CONFIGURATION' | translate}} + {{formGroup.get('definition').get('getEnabled').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+ + + + {{'PREFILLING-SOURCE-EDITOR.FIELDS.GET-SOURCE-CONFIGURATION' | translate}} + + + + + +
+ +
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.scss b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.scss new file mode 100644 index 000000000..09a802a50 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.scss @@ -0,0 +1,43 @@ +.prefilling-source-editor { + margin-top: 1.3rem; + margin-left: 1em; + margin-right: 3em; + + .remove { + background-color: white; + color: black; + } + + .add { + background-color: white; + color: #009700; + } +} + +::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background, .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background { + background-color: var(--primary-color-3); + // background-color: #0070c0; +} + +::ng-deep .mat-checkbox-disabled.mat-checkbox-checked .mat-checkbox-background, .mat-checkbox-disabled.mat-checkbox-indeterminate .mat-checkbox-background { + background-color: #b0b0b0; +} + +.action-btn { + border-radius: 30px; + background-color: var(--secondary-color); + border: 1px solid transparent; + padding-left: 2em; + padding-right: 2em; + box-shadow: 0px 3px 6px #1E202029; + + transition-property: background-color, color; + transition-duration: 200ms; + transition-delay: 50ms; + transition-timing-function: ease-in-out; + &:disabled{ + background-color: #CBCBCB; + color: #FFF; + border: 0px; + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.ts new file mode 100644 index 000000000..7687535f4 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.component.ts @@ -0,0 +1,264 @@ + +import { Component, OnInit } from '@angular/core'; +import { FormArray, FormGroup, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { PrefillingSourceService } from '@app/core/services/prefilling-source/prefilling-source.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { DatePipe } from '@angular/common'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { PrefillingSource, PrefillingSourcePersist } from '@app/core/model/prefilling-source/prefilling-source'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { LoggingService } from '@app/core/services/logging/logging-service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; +import { BaseEditor } from '@common/base/base-editor'; +import { FormService } from '@common/forms/form-service'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import { map, takeUntil } from 'rxjs/operators'; +import { PrefillingSourceEditorResolver } from './prefilling-source-editor.resolver'; +import { PrefillingSourceEditorService } from './prefilling-source-editor.service'; +import { PrefillingSourceEditorModel } from './prefilling-source-editor.model'; +import { ResultFieldsMappingConfigurationEditorModel } from '@app/ui/external-fetcher/external-fetcher-source-editor.model'; +import { SemanticsService } from '@app/core/services/semantic/semantics.service'; + +@Component({ + selector: 'app-prefilling-source-editor-component', + templateUrl: 'prefilling-source-editor.component.html', + styleUrls: ['./prefilling-source-editor.component.scss'], + providers: [PrefillingSourceEditorService] +}) +export class PrefillingSourceEditorComponent extends BaseEditor implements OnInit { + + isNew = true; + isDeleted = false; + formGroup: UntypedFormGroup = null; + showInactiveDetails = false; + + protected get canDelete(): boolean { + return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeletePrefillingSource); + } + + protected get canSave(): boolean { + return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditPrefillingSource); + } + + private hasPermission(permission: AppPermission): boolean { + return this.authService.hasPermission(permission) || this.editorModel?.permissions?.includes(permission); + } + + constructor( + // BaseFormEditor injected dependencies + protected dialog: MatDialog, + protected language: TranslateService, + protected formService: FormService, + protected router: Router, + protected uiNotificationService: UiNotificationService, + protected httpErrorHandlingService: HttpErrorHandlingService, + protected filterService: FilterService, + protected datePipe: DatePipe, + protected route: ActivatedRoute, + protected queryParamsService: QueryParamsService, + // Rest dependencies. Inject any other needed deps here: + public authService: AuthService, + public enumUtils: EnumUtils, + private prefillingSourceService: PrefillingSourceService, + private logger: LoggingService, + private prefillingSourceEditorService: PrefillingSourceEditorService, + public semanticsService: SemanticsService, + private matomoService: MatomoService + ) { + super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService); + } + + ngOnInit(): void { + this.matomoService.trackPageView('Admin: PrefillingSources'); + super.ngOnInit(); + } + + getItem(itemId: Guid, successFunction: (item: PrefillingSource) => void) { + this.prefillingSourceService.getSingle(itemId, PrefillingSourceEditorResolver.lookupFields()) + .pipe(map(data => data as PrefillingSource), takeUntil(this._destroyed)) + .subscribe( + data => successFunction(data), + error => this.onCallbackError(error) + ); + } + + prepareForm(data: PrefillingSource) { + try { + this.editorModel = data ? new PrefillingSourceEditorModel().fromModel(data) : new PrefillingSourceEditorModel(); + this.isDeleted = data ? data.isActive === IsActive.Inactive : false; + this.buildForm(); + } catch (error) { + this.logger.error('Could not parse PrefillingSource item: ' + data + error); + this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error); + } + } + + buildForm() { + this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.authService.hasPermission(AppPermission.EditPrefillingSource)); + this.prefillingSourceEditorService.setValidationErrorModel(this.editorModel.validationErrorModel); + this.addFieldMapping("prefilling_id", "searchConfiguration"); + this.addFieldMapping("label", "searchConfiguration"); + this.addFieldMapping("description", "searchConfiguration"); + + this.addFieldMapping("prefilling_id", "getConfiguration"); + this.addFieldMapping("label", "getConfiguration"); + this.addFieldMapping("description", "getConfiguration"); + } + + refreshData(): void { + this.getItem(this.editorModel.id, (data: PrefillingSource) => this.prepareForm(data)); + } + + refreshOnNavigateToData(id?: Guid): void { + this.formGroup.markAsPristine(); + let route = []; + + if (id === null) { + route.push('../..'); + } else if (this.isNew) { + route.push('../' + id); + } else { + route.push('..'); + } + + this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route }); + } + + persistEntity(onSuccess?: (response) => void): void { + const formData = this.formService.getValue(this.formGroup.value) as PrefillingSourcePersist; + + this.prefillingSourceService.persist(formData) + .pipe(takeUntil(this._destroyed)).subscribe( + complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete), + error => this.onCallbackError(error) + ); + } + + formSubmit(): void { + this.formService.touchAllFormFields(this.formGroup); + // if (!this.isFormValid()) { + // return; + // } + + this.persistEntity(); + } + + public delete() { + const value = this.formGroup.value; + if (value.id) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '300px', + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL') + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.prefillingSourceService.delete(value.id).pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }); + } + } + + clearErrorModel() { + this.editorModel.validationErrorModel.clear(); + this.formService.validateAllFormFields(this.formGroup); + } + + // + // + // fields + // + // + addField(): void { + (this.formGroup.get('definition').get('fields') as FormArray).push(this.editorModel.createChildField((this.formGroup.get('definition').get('fields') as FormArray).length)); + } + + removeField(fieldIndex: number): void { + const fieldForm = this.formGroup.get('definition').get('fields') as FormArray; + const fieldCode = fieldForm.at(fieldIndex).get('code').value; + + fieldForm.removeAt(fieldIndex); + + //Reapply validators + PrefillingSourceEditorModel.reApplyDefinitionValidators( + { + formGroup: this.formGroup, + validationErrorModel: this.editorModel.validationErrorModel + } + ) + fieldForm.markAsDirty(); + + this.removeFieldMapping((this.formGroup.get('definition').get('searchConfiguration') as FormGroup), fieldCode); + this.removeFieldMapping((this.formGroup.get('definition').get('getConfiguration') as FormGroup), fieldCode); + } + + submitFields(): void { + const fieldsFormArray = (this.formGroup.get('definition').get('fields') as FormArray); + + if (fieldsFormArray.valid) { + for (let i = 0; i < fieldsFormArray.length; i++) { + const code = fieldsFormArray.at(i).get('code').value; + this.addFieldMapping(code, "searchConfiguration"); + this.addFieldMapping(code, "getConfiguration"); + } + } + } + + + // + // + // resultFieldsMapping + // + // + addFieldMapping(code: string, controlName: string): void { + const formArray = (this.formGroup.get('definition').get(controlName).get('results').get('fieldsMapping') as FormArray); + const fieldMappingSize = formArray.length; + + if (fieldMappingSize > 0) { + for (let i = 0; i < fieldMappingSize; i++) { + if (formArray.at(i).get('code').getRawValue() == code) { + return; + } + } + } + const fieldsMapping = new ResultFieldsMappingConfigurationEditorModel(this.editorModel.validationErrorModel); + fieldsMapping.code = code; + formArray.push(fieldsMapping.buildForm({rootPath: "definition." + controlName + ".results.fieldsMapping[" + fieldMappingSize + "]."})); + } + + removeFieldMapping(baseFormGroup: any, fieldCode: string){ + if(baseFormGroup){ + const fieldMappingFormArray = (baseFormGroup.get('results').get('fieldsMapping') as FormArray); + for (let j = 0; j < fieldMappingFormArray.length; j++) { + if (fieldCode == fieldMappingFormArray.at(j).get('code').getRawValue()) { + fieldMappingFormArray.removeAt(j); + + PrefillingSourceEditorModel.reApplyDefinitionValidators({ + formGroup: this.formGroup, + validationErrorModel: this.editorModel.validationErrorModel + } + ); + } + } + } + } + + +} diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.model.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.model.ts new file mode 100644 index 000000000..3807b7f69 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.model.ts @@ -0,0 +1,244 @@ +import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { PrefillingSource, PrefillingSourceDefinition, PrefillingSourceDefinitionField, PrefillingSourceDefinitionFieldPersist, PrefillingSourceDefinitionPersist, PrefillingSourcePersist } from "@app/core/model/prefilling-source/prefilling-source"; +import { ExternalFetcherBaseSourceConfigurationEditorModel, QueryCaseConfigEditorModel, QueryConfigEditorModel } from "@app/ui/external-fetcher/external-fetcher-source-editor.model"; + +import { BaseEditorModel } from "@common/base/base-form-editor-model"; +import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; +import { Validation, ValidationContext } from "@common/forms/validation/validation-context"; + +export class PrefillingSourceEditorModel extends BaseEditorModel implements PrefillingSourcePersist { + label: string; + definition: PrefillingSourceDefinitionEditorModel = new PrefillingSourceDefinitionEditorModel(); + permissions: string[]; + + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor() { super(); } + + public fromModel(item: PrefillingSource): PrefillingSourceEditorModel { + if (item) { + super.fromModel(item); + this.label = item.label; + if (item.definition) this.definition = new PrefillingSourceDefinitionEditorModel(this.validationErrorModel).fromModel(item.definition); + } + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + if (context == null) { context = this.createValidationContext(); } + + return this.formBuilder.group({ + id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], + definition: this.definition.buildForm({ + rootPath: `definition.`, + }), + hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] + }); + } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseValidationArray.push({ key: 'definition', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'definition')] }); + baseValidationArray.push({ key: 'hash', validators: [] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reApplyDefinitionValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + }): void { + + const { formGroup, validationErrorModel } = params; + const control = formGroup?.get('definition'); + PrefillingSourceDefinitionEditorModel.reapplyValidators({ + formArray: control.get('fields') as UntypedFormArray, + rootPath: `definition.`, + validationErrorModel: validationErrorModel + }); + } + + createChildField(index: number): UntypedFormGroup { + const field: PrefillingSourceDefinitionFieldEditorModel = new PrefillingSourceDefinitionFieldEditorModel(this.validationErrorModel); + return field.buildForm({ rootPath: 'definition.fields[' + index + '].' }); + } +} + +export class PrefillingSourceDefinitionEditorModel implements PrefillingSourceDefinitionPersist { + fields: PrefillingSourceDefinitionFieldEditorModel[] = []; + searchConfiguration: ExternalFetcherBaseSourceConfigurationEditorModel = new ExternalFetcherBaseSourceConfigurationEditorModel(); + getConfiguration: ExternalFetcherBaseSourceConfigurationEditorModel = new ExternalFetcherBaseSourceConfigurationEditorModel(); + getEnabled = false; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: PrefillingSourceDefinition): PrefillingSourceDefinitionEditorModel { + if (item) { + if (item.fields) { item.fields.map(x => this.fields.push(new PrefillingSourceDefinitionFieldEditorModel(this.validationErrorModel).fromModel(x))); } + if (item.searchConfiguration) this.searchConfiguration = new ExternalFetcherBaseSourceConfigurationEditorModel(this.validationErrorModel).fromModel(item.searchConfiguration); + if (item.getConfiguration) { + this.getConfiguration = new ExternalFetcherBaseSourceConfigurationEditorModel(this.validationErrorModel).fromModel(item.getConfiguration); + this.getEnabled = true; + } + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = PrefillingSourceDefinitionEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + fields: this.formBuilder.array( + (this.fields ?? []).map( + (item, index) => item.buildForm({ + rootPath: `${rootPath}fields[${index}].` + }), context.getValidation('fields') + ) + ), + searchConfiguration: this.searchConfiguration.buildForm({ + rootPath: `${rootPath}searchConfiguration.` + }), + getConfiguration: this.getConfiguration.buildForm({ + rootPath: `${rootPath}getConfiguration.` + }), + getEnabled: [{ value: this.getEnabled, disabled: disabled }, context.getValidation('getEnabled').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'fields', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fields`)] }); + baseValidationArray.push({ key: 'searchConfiguration', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}searchConfiguration`)] }); + baseValidationArray.push({ key: 'getConfiguration', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}getConfiguration`)] }); + baseValidationArray.push({ key: 'getEnabled', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}getConfiguration`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyValidators(params: { + formArray: UntypedFormArray, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + const { validationErrorModel, rootPath, formArray } = params; + formArray?.controls?.forEach( + (control, index) => PrefillingSourceDefinitionFieldEditorModel.reapplyValidators({ + formGroup: control as UntypedFormGroup, + rootPath: `${rootPath}fields[${index}].`, + validationErrorModel: validationErrorModel + }) + ); + } +} + +export class PrefillingSourceDefinitionFieldEditorModel implements PrefillingSourceDefinitionFieldPersist { + code: string; + systemFieldTarget: string; + semanticTarget: string; + trimRegex: string; + fixedValue: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: PrefillingSourceDefinitionField): PrefillingSourceDefinitionFieldEditorModel { + if (item) { + this.code = item.code; + this.systemFieldTarget = item.systemFieldTarget; + this.semanticTarget = item.semanticTarget; + this.trimRegex = item.trimRegex; + this.fixedValue = item.fixedValue; + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = PrefillingSourceDefinitionFieldEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators], + systemFieldTarget: [{ value: this.systemFieldTarget, disabled: disabled }, context.getValidation('systemFieldTarget').validators], + semanticTarget: [{ value: this.semanticTarget, disabled: disabled }, context.getValidation('semanticTarget').validators], + trimRegex: [{ value: this.trimRegex, disabled: disabled }, context.getValidation('trimRegex').validators], + fixedValue: [{ value: this.fixedValue, disabled: disabled }, context.getValidation('fixedValue').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'code', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); + baseValidationArray.push({ key: 'systemFieldTarget', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}systemFieldTarget`)] }); + baseValidationArray.push({ key: 'semanticTarget', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}semanticTarget`)] }); + baseValidationArray.push({ key: 'trimRegex', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}trimRegex`)] }); + baseValidationArray.push({ key: 'fixedValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}clientSecret`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = PrefillingSourceDefinitionFieldEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['code', 'systemFieldTarget', 'semanticTarget', 'trimRegex', 'fixedValue'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }) + } +} diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.resolver.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.resolver.ts new file mode 100644 index 000000000..e2a4972b0 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.resolver.ts @@ -0,0 +1,101 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { AuthenticationConfiguration, ExternalFetcherBaseSourceConfiguration, QueryCaseConfig, QueryConfig, ResultFieldsMappingConfiguration, ResultsConfiguration } from '@app/core/model/external-fetcher/external-fetcher'; +import { PrefillingSource, PrefillingSourceDefinition, PrefillingSourceDefinitionField } from '@app/core/model/prefilling-source/prefilling-source'; +import { PrefillingSourceService } from '@app/core/services/prefilling-source/prefilling-source.service'; +import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; +import { BaseEditorResolver } from '@common/base/base-editor.resolver'; +import { Guid } from '@common/types/guid'; +import { takeUntil, tap } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; + +@Injectable() +export class PrefillingSourceEditorResolver extends BaseEditorResolver { + + constructor(private prefillingSourceService: PrefillingSourceService, private breadcrumbService: BreadcrumbService) { + super(); + } + + public static lookupFields(): string[] { + return [ + ...BaseEditorResolver.lookupFields(), + nameof(x => x.id), + nameof(x => x.label), + + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.systemFieldTarget)].join('.'), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.semanticTarget)].join('.'), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.trimRegex)].join('.'), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.fixedValue)].join('.'), + + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.type)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.key)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.ordinal)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.url)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.results), nameof(x => x.resultsArrayPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.responsePath)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.paginationPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.contentType)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.firstPage)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.httpMethod)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.requestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.filterType)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.auth), nameof(x => x.enabled)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.auth), nameof(x => x.authUrl)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.auth), nameof(x => x.authMethod)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.auth), nameof(x => x.authTokenPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.auth), nameof(x => x.authRequestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.auth), nameof(x => x.type)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.queries), nameof(x => x.name)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.queries), nameof(x => x.defaultValue)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.queries), nameof(x => x.cases),nameof(x => x.likePattern)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.queries), nameof(x => x.cases),nameof(x => x.separator)].join('.'), + [nameof(x => x.definition), nameof(x => x.searchConfiguration), nameof(x => x.queries), nameof(x => x.cases),nameof(x => x.value)].join('.'), + + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.type)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.key)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.ordinal)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.url)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.results), nameof(x => x.resultsArrayPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.responsePath)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.paginationPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.contentType)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.firstPage)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.httpMethod)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.requestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.filterType)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.auth), nameof(x => x.enabled)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.auth), nameof(x => x.authUrl)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.auth), nameof(x => x.authMethod)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.auth), nameof(x => x.authTokenPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.auth), nameof(x => x.authRequestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.auth), nameof(x => x.type)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.queries), nameof(x => x.name)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.queries), nameof(x => x.defaultValue)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.queries), nameof(x => x.cases),nameof(x => x.likePattern)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.queries), nameof(x => x.cases),nameof(x => x.separator)].join('.'), + [nameof(x => x.definition), nameof(x => x.getConfiguration), nameof(x => x.queries), nameof(x => x.cases),nameof(x => x.value)].join('.'), + + nameof(x => x.createdAt), + nameof(x => x.updatedAt), + nameof(x => x.hash), + nameof(x => x.isActive) + ] + } + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + + const fields = [ + ...PrefillingSourceEditorResolver.lookupFields() + ]; + const id = route.paramMap.get('id'); + + if (id != null) { + return this.prefillingSourceService.getSingle(Guid.parse(id), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed)); + } + } +} diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.service.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.service.ts new file mode 100644 index 000000000..df3e675fc --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/editor/prefilling-source-editor.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from "@angular/core"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; + +@Injectable() +export class PrefillingSourceEditorService { + private validationErrorModel: ValidationErrorModel; + + public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void { + this.validationErrorModel = validationErrorModel; + } + + public getValidationErrorModel(): ValidationErrorModel { + return this.validationErrorModel; + } +} diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.html b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.html new file mode 100644 index 000000000..0d6059198 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.html @@ -0,0 +1,36 @@ +
+ + + + + +
+
+
+

{{'PREFILLING-SOURCE-LISTING.FILTER.TITLE' | translate}}

+ +
+ + + {{'PREFILLING-SOURCE-LISTING.FILTER.IS-ACTIVE' | translate}} + + +
+ + +
+
+
+
+ + +
diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.scss b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.scss new file mode 100644 index 000000000..c31bf588d --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.scss @@ -0,0 +1,25 @@ +.prefilling-source-listing-filters { + +} + +::ng-deep.mat-mdc-menu-panel { + max-width: 100% !important; + height: 100% !important; +} + +:host::ng-deep.mat-mdc-menu-content:not(:empty) { + padding-top: 0 !important; +} + + +.filter-button{ + padding-top: .6rem; + padding-bottom: .6rem; + // .mat-icon{ + // font-size: 1.5em; + // width: 1.2em; + // height: 1.2em; + // } +} + + diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.ts new file mode 100644 index 000000000..6d96affe4 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/filters/prefilling-source-listing-filters.component.ts @@ -0,0 +1,94 @@ +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { PrefillingSourceFilter } from '@app/core/query/prefilling-source.lookup'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { BaseComponent } from '@common/base/base.component'; +import { nameof } from 'ts-simple-nameof'; + +@Component({ + selector: 'app-prefilling-source-listing-filters', + templateUrl: './prefilling-source-listing-filters.component.html', + styleUrls: ['./prefilling-source-listing-filters.component.scss'] +}) +export class PrefillingSourceListingFiltersComponent extends BaseComponent implements OnInit, OnChanges { + + @Input() readonly filter: PrefillingSourceFilter; + @Output() filterChange = new EventEmitter(); + + // * State + internalFilters: PrefillingSourceListingFilters = this._getEmptyFilters(); + + protected appliedFilterCount: number = 0; + constructor( + public enumUtils: EnumUtils, + ) { super(); } + + ngOnInit() { + } + + ngOnChanges(changes: SimpleChanges): void { + const filterChange = changes[nameof(x => x.filter)]?.currentValue as PrefillingSourceFilter; + if (filterChange) { + this.updateFilters() + } + } + + + onSearchTermChange(searchTerm: string): void { + this.applyFilters() + } + + + protected updateFilters(): void { + this.internalFilters = this._parseToInternalFilters(this.filter); + this.appliedFilterCount = this._computeAppliedFilters(this.internalFilters); + } + + protected applyFilters(): void { + const { isActive, like } = this.internalFilters ?? {} + this.filterChange.emit({ + ...this.filter, + like, + isActive: isActive ? [IsActive.Active] : [IsActive.Inactive] + }) + } + + + private _parseToInternalFilters(inputFilter: PrefillingSourceFilter): PrefillingSourceListingFilters { + if (!inputFilter) { + return this._getEmptyFilters(); + } + + let { excludedIds, ids, isActive, like } = inputFilter; + + return { + isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length, + like: like + } + + } + + private _getEmptyFilters(): PrefillingSourceListingFilters { + return { + isActive: true, + like: null, + } + } + + private _computeAppliedFilters(filters: PrefillingSourceListingFilters): number { + let count = 0; + if (filters?.isActive) { + count++ + } + return count; + } + + clearFilters() { + this.internalFilters = this._getEmptyFilters(); + } +} + +interface PrefillingSourceListingFilters { + isActive: boolean; + like: string; +} diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.html b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.html new file mode 100644 index 000000000..307834601 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.html @@ -0,0 +1,96 @@ +
+
+ +
+
+

{{'PREFILLING-SOURCE-LISTING.TITLE' | translate}}

+ + +
+
+ +
+
+ + + + + + + + +
+
+ + + + +
+
+ + {{item?.name | nullifyValue}} +
+
+ + + + {{'PREFILLING-SOURCE-LISTING.FIELDS.CODE' | translate}}: + + {{item?.code | nullifyValue}} + + +
+
+ + + + {{'PREFILLING-SOURCE-LISTING.FIELDS.CREATED-AT' | translate}}: + + {{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}} + + +
+
+ + + {{'PREFILLING-SOURCE-LISTING.FIELDS.UPDATED-AT' | translate}}: + + {{item?.updatedAt | dateTimeFormatter : 'short' | nullifyValue}} + + + +
+
+
+ + +
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.scss b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.scss new file mode 100644 index 000000000..76392cf77 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.scss @@ -0,0 +1,83 @@ +.mat-table { + margin-top: 47px; + border-radius: 4px; +} + +.prefilling-source-listing { + margin-top: 1.3rem; + margin-left: 1rem; + margin-right: 2rem; + + .mat-header-row { + background: #f3f5f8; + } + + .mat-card { + margin: 16px 0; + padding: 0px; + } + + .mat-row { + cursor: pointer; + min-height: 4.5em; + } + + mat-row:hover { + background-color: #eef5f6; + } + + .mat-fab-bottom-right { + float: right; + z-index: 5; + } +} + +// PAGINATOR +:host ::ng-deep .mat-paginator-container { + flex-direction: row-reverse !important; + justify-content: space-between !important; + background-color: #f6f6f6; + align-items: center; +} + +.create-btn { + border-radius: 30px; + background-color: var(--secondary-color); + padding-left: 2em; + padding-right: 2em; + // color: #000; + + .button-text { + display: inline-block; + } +} + +.import-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border-radius: 30px; + // color: var(--primary-color); + // border: 1px solid var(--primary-color); + padding-left: 2em; + padding-right: 2em; + color: #000; + border: 1px solid #000; +} + +.status-chip { + + border-radius: 20px; + padding-left: 1em; + padding-right: 1em; + padding-top: 0.2em; + font-size: .8em; +} + +.status-chip-finalized { + color: #568b5a; + background: #9dd1a1 0% 0% no-repeat padding-box; +} + +.status-chip-draft { + color: #00c4ff; + background: #d3f5ff 0% 0% no-repeat padding-box; +} diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.ts new file mode 100644 index 000000000..42f221f73 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/listing/prefilling-source-listing.component.ts @@ -0,0 +1,171 @@ +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { PrefillingSource } from '@app/core/model/prefilling-source/prefilling-source'; +import { PrefillingSourceLookup } from '@app/core/query/prefilling-source.lookup'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { PrefillingSourceService } from '@app/core/services/prefilling-source/prefilling-source.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; +import { BaseListingComponent } from '@common/base/base-listing-component'; +import { PipeService } from '@common/formatting/pipe.service'; +import { DataTableDateTimeFormatPipe } from '@common/formatting/pipes/date-time-format.pipe'; +import { QueryResult } from '@common/model/query-result'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { ColumnDefinition, ColumnsChangedEvent, HybridListingComponent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; +import { IsActiveTypePipe } from '@common/formatting/pipes/is-active-type.pipe'; + +@Component({ + templateUrl: './prefilling-source-listing.component.html', + styleUrls: ['./prefilling-source-listing.component.scss'] +}) +export class PrefillingSourceListingComponent extends BaseListingComponent implements OnInit { + publish = false; + userSettingsKey = { key: 'PrefillingSourceListingUserSettings' }; + propertiesAvailableForOrder: ColumnDefinition[]; + + // @ViewChild('PrefillingSourceStatus', { static: true }) PrefillingSourceStatus?: TemplateRef; + @ViewChild('actions', { static: true }) actions?: TemplateRef; + @ViewChild(HybridListingComponent, { static: true }) hybridListingComponent: HybridListingComponent; + + private readonly lookupFields: string[] = [ + nameof(x => x.id), + nameof(x => x.label), + nameof(x => x.updatedAt), + nameof(x => x.createdAt), + nameof(x => x.hash), + nameof(x => x.isActive) + ]; + + rowIdentity = x => x.id; + + constructor( + protected router: Router, + protected route: ActivatedRoute, + protected uiNotificationService: UiNotificationService, + protected httpErrorHandlingService: HttpErrorHandlingService, + protected queryParamsService: QueryParamsService, + private PrefillingSourceService: PrefillingSourceService, + public authService: AuthService, + private pipeService: PipeService, + public enumUtils: EnumUtils, + private language: TranslateService, + private dialog: MatDialog + ) { + super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService); + // Lookup setup + // Default lookup values are defined in the user settings class. + this.lookup = this.initializeLookup(); + } + + ngOnInit() { + super.ngOnInit(); + } + + protected initializeLookup(): PrefillingSourceLookup { + const lookup = new PrefillingSourceLookup(); + lookup.metadata = { countAll: true }; + lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE }; + lookup.isActive = [IsActive.Active]; + lookup.order = { items: [this.toDescSortField(nameof(x => x.createdAt))] }; + this.updateOrderUiFields(lookup.order); + + lookup.project = { + fields: this.lookupFields + }; + + return lookup; + } + + protected setupColumns() { + this.gridColumns.push(...[{ + prop: nameof(x => x.label), + sortable: true, + languageName: 'PREFILLING-SOURCE-LISTING.FIELDS.LABEL' + }, + { + prop: nameof(x => x.createdAt), + sortable: true, + languageName: 'PREFILLING-SOURCE-LISTING.FIELDS.CREATED-AT', + pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') + }, + { + prop: nameof(x => x.updatedAt), + sortable: true, + languageName: 'PREFILLING-SOURCE-LISTING.FIELDS.UPDATED-AT', + pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') + }, + { + prop: nameof(x => x.isActive), + sortable: true, + languageName: 'PREFILLING-SOURCE-LISTING.FIELDS.IS-ACTIVE', + pipe: this.pipeService.getPipe(IsActiveTypePipe) + }, + { + alwaysShown: true, + cellTemplate: this.actions, + maxWidth: 120 + } + ]); + this.propertiesAvailableForOrder = this.gridColumns.filter(x => x.sortable); + } + + // + // Listing Component functions + // + onColumnsChanged(event: ColumnsChangedEvent) { + super.onColumnsChanged(event); + this.onColumnsChangedInternal(event.properties.map(x => x.toString())); + } + + private onColumnsChangedInternal(columns: string[]) { + // Here are defined the projection fields that always requested from the api. + const fields = new Set(this.lookupFields); + this.gridColumns.map(x => x.prop) + .filter(x => !columns?.includes(x as string)) + .forEach(item => { + fields.delete(item as string) + }); + this.lookup.project = { fields: [...fields] }; + this.onPageLoad({ offset: 0 } as PageLoadEvent); + } + + protected loadListing(): Observable> { + return this.PrefillingSourceService.query(this.lookup); + } + + public deleteType(id: Guid) { + if (id) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + isDeleteConfirmation: true, + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL') + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.PrefillingSourceService.delete(id).pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }); + } + } + + onCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success); + this.refresh(); + } +} diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.module.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.module.ts new file mode 100644 index 000000000..92f403cf7 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.module.ts @@ -0,0 +1,40 @@ +import { DragDropModule } from '@angular/cdk/drag-drop'; +import { NgModule } from "@angular/core"; +import { AutoCompleteModule } from "@app/library/auto-complete/auto-complete.module"; +import { CommonFormattingModule } from '@common/formatting/common-formatting.module'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; +import { HybridListingModule } from "@common/modules/hybrid-listing/hybrid-listing.module"; +import { TextFilterModule } from "@common/modules/text-filter/text-filter.module"; +import { UserSettingsModule } from "@common/modules/user-settings/user-settings.module"; +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { NgxDropzoneModule } from "ngx-dropzone"; +import { PrefillingSourceRoutingModule } from './prefilling-source.routing'; +import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module'; +import { PrefillingSourceListingComponent } from './listing/prefilling-source-listing.component'; +import { PrefillingSourceListingFiltersComponent } from './listing/filters/prefilling-source-listing-filters.component'; +import { PrefillingSourceEditorComponent } from './editor/prefilling-source-editor.component'; +import { ExternalFetcherSourceModule } from '@app/ui/external-fetcher/external-fetcher-source.module'; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + ConfirmationDialogModule, + PrefillingSourceRoutingModule, + NgxDropzoneModule, + DragDropModule, + AutoCompleteModule, + HybridListingModule, + TextFilterModule, + UserSettingsModule, + CommonFormattingModule, + RichTextEditorModule, + ExternalFetcherSourceModule + ], + declarations: [ + PrefillingSourceEditorComponent, + PrefillingSourceListingComponent, + PrefillingSourceListingFiltersComponent ] +}) +export class PrefillingSourceModule { } diff --git a/dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.routing.ts b/dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.routing.ts new file mode 100644 index 000000000..4eedc48fe --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/prefilling-source/prefilling-source.routing.ts @@ -0,0 +1,58 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AdminAuthGuard } from '@app/core/admin-auth-guard.service'; +import { PrefillingSourceEditorComponent } from './editor/prefilling-source-editor.component'; +import { PrefillingSourceListingComponent } from './listing/prefilling-source-listing.component'; +import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { AuthGuard } from '@app/core/auth-guard.service'; +import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; +import { PendingChangesGuard } from '@common/forms/pending-form-changes/pending-form-changes-guard.service'; +import { PrefillingSourceEditorResolver } from './editor/prefilling-source-editor.resolver'; + +const routes: Routes = [ + { + path: '', + component: PrefillingSourceListingComponent, + canActivate: [AuthGuard] + }, + { + path: 'new', + canActivate: [AuthGuard], + component: PrefillingSourceEditorComponent, + canDeactivate: [PendingChangesGuard], + data: { + authContext: { + permissions: [AppPermission.EditPrefillingSource] + }, + ...BreadcrumbService.generateRouteDataConfiguration({ + title: 'BREADCRUMBS.NEW-PREFILLING-SOURCE' + }) + } + }, + { + path: ':id', + canActivate: [AuthGuard], + component: PrefillingSourceEditorComponent, + canDeactivate: [PendingChangesGuard], + resolve: { + 'entity': PrefillingSourceEditorResolver + }, + data: { + ...BreadcrumbService.generateRouteDataConfiguration({ + title: 'BREADCRUMBS.EDIT-PREFILLING-SOURCE' + }), + authContext: { + permissions: [AppPermission.EditPrefillingSource] + } + } + + }, + { path: '**', loadChildren: () => import('@common/modules/page-not-found/page-not-found.module').then(m => m.PageNotFoundModule) }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [PrefillingSourceEditorResolver] +}) +export class PrefillingSourceRoutingModule { } diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.html b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.html index d8cf90708..62248ebfd 100644 --- a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.html @@ -123,7 +123,7 @@ + type="button" (click)="submitFields()" [disabled]="!formGroup.get('definition').get('fields').valid">{{'REFERENCE-TYPE-EDITOR.ACTIONS.SUBMIT-FIELDS' | translate}} @@ -142,18 +142,26 @@
- -
-
- {{'REFERENCE-TYPE-EDITOR.FIELDS.SOURCE-CONFIGURATION' | translate}} {{sourceIndex + 1}} -
-
- -
+ +
+
+ {{'REFERENCE-TYPE-EDITOR.FIELDS.SOURCE-CONFIGURATION' | translate}} {{sourceIndex + 1}}
-
+
+ +
+
+ + + + + + Results info

{{'REFERENCE-TYPE-EDITOR.FIELDS.RESULTS' | translate}}

@@ -270,7 +281,7 @@ {{'GENERAL.VALIDATION.REQUIRED' | translate}}
- + fields mapping
@@ -304,7 +315,7 @@
- + Auth info

{{'REFERENCE-TYPE-EDITOR.FIELDS.AUTHENTICATION' | translate}}

@@ -354,7 +365,7 @@
- + Queries info

{{'REFERENCE-TYPE-EDITOR.FIELDS.QUERIES' | translate}}

@@ -397,7 +408,7 @@
- + Query Cases
@@ -437,16 +448,17 @@ {{case.get('value').getError('backendError').message}} {{'GENERAL.VALIDATION.REQUIRED' | translate}} -
-
+
+
{{'REFERENCE-TYPE-EDITOR.FIELDS.REFERENCE-TYPE' | translate}} - - + + {{referenceType.code}} - - {{case.get('referenceTypeId').getError('backendError').message}} + --> + + + Options

{{'REFERENCE-TYPE-EDITOR.FIELDS.OPTIONS' | translate}}

@@ -506,7 +518,7 @@
-
+
--> @@ -514,7 +526,6 @@ {{formGroup.get('definition').get('sources').getError('backendError').message}} - {{formGroup.value | json}} diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.ts b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.ts index c9e6a0c0b..b6de26213 100644 --- a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.ts @@ -11,8 +11,8 @@ import { DatePipe } from '@angular/common'; import { IsActive } from '@app/core/common/enum/is-active.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum'; import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type'; -import { ReferenceTypeExternalApiHTTPMethodType } from '@app/core/common/enum/reference-type-external-api-http-method-type'; -import { ReferenceTypeSourceType } from '@app/core/common/enum/reference-type-source-type'; +import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type'; +import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type'; import { ReferenceType, ReferenceTypeDefinition, ReferenceTypePersist } from '@app/core/model/reference-type/reference-type'; import { AuthService } from '@app/core/services/auth/auth.service'; import { LoggingService } from '@app/core/services/logging/logging-service'; @@ -42,11 +42,11 @@ export class ReferenceTypeEditorComponent extends BaseEditor(ReferenceTypeSourceType); + referenceTypeSourceType = ExternalFetcherSourceType; + referenceTypeExternalApiHTTPMethodType = ExternalFetcherApiHTTPMethodType; + public referenceTypeSourceTypeEnum = this.enumUtils.getEnumValues(ExternalFetcherSourceType); public referenceFieldDataTypeEnum = this.enumUtils.getEnumValues(ReferenceFieldDataType); - public referenceTypeExternalApiHTTPMethodTypeEnum = this.enumUtils.getEnumValues(ReferenceTypeExternalApiHTTPMethodType); + public referenceTypeExternalApiHTTPMethodTypeEnum = this.enumUtils.getEnumValues(ExternalFetcherApiHTTPMethodType); referenceTypes: ReferenceType[] = null; sourceKeysMap: Map = new Map(); @@ -111,14 +111,6 @@ export class ReferenceTypeEditorComponent extends BaseEditor source.queries?.forEach(query => { - query?.cases?.forEach(queryCase => { - if(queryCase.referenceType && queryCase.referenceType.id) this.selectedReferenceTypeChanged(queryCase.referenceType.id); - }) - })); - } - this.isDeleted = data ? data.isActive === IsActive.Inactive : false; this.buildForm(); } catch (error) { @@ -163,9 +155,9 @@ export class ReferenceTypeEditorComponent extends BaseEditor { this.referenceTypes = response.items as ReferenceType[]; this.referenceTypes.forEach(referenceType => { - this.sourceKeysMap.set(referenceType.id, []); + sourceKeys = referenceType.definition.sources.map(x => x.key); + this.sourceKeysMap.set(referenceType.id, sourceKeys); }) }); } diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.model.ts b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.model.ts index a663ee8f3..b761614dc 100644 --- a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.model.ts +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.model.ts @@ -1,13 +1,11 @@ import { FormArray, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { ReferenceFieldDataType } from "@app/core/common/enum/reference-field-data-type"; -import { ReferenceTypeExternalApiHTTPMethodType } from "@app/core/common/enum/reference-type-external-api-http-method-type"; -import { ReferenceTypeSourceType } from "@app/core/common/enum/reference-type-source-type"; -import { AuthenticationConfiguration, AuthenticationConfigurationPersist, QueryCaseConfig, QueryCaseConfigPersist, QueryConfig, QueryConfigPersist, ReferenceType, ReferenceTypeDefinition, ReferenceTypeDefinitionPersist, ReferenceTypeField, ReferenceTypeFieldPersist, ReferenceTypePersist, ReferenceTypeSourceBaseConfiguration, ReferenceTypeSourceBaseConfigurationPersist, ReferenceTypeStaticOption, ReferenceTypeStaticOptionPersist, ResultFieldsMappingConfiguration, ResultFieldsMappingConfigurationPersist, ResultsConfiguration, ResultsConfigurationPersist } from "@app/core/model/reference-type/reference-type"; +import { ReferenceType, ReferenceTypeDefinition, ReferenceTypeDefinitionPersist, ReferenceTypeField, ReferenceTypeFieldPersist, ReferenceTypePersist } from "@app/core/model/reference-type/reference-type"; import { BaseEditorModel } from "@common/base/base-form-editor-model"; import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; import { Validation, ValidationContext } from "@common/forms/validation/validation-context"; -import { Guid } from "@common/types/guid"; +import { ExternalFetcherBaseSourceConfigurationEditorModel, QueryCaseConfigEditorModel, QueryConfigEditorModel, ResultFieldsMappingConfigurationEditorModel, StaticOptionEditorModel } from "@app/ui/external-fetcher/external-fetcher-source-editor.model"; export class ReferenceTypeEditorModel extends BaseEditorModel implements ReferenceTypePersist { name: string; @@ -66,7 +64,7 @@ export class ReferenceTypeEditorModel extends BaseEditorModel implements Referen } createChildSource(index: number): UntypedFormGroup { - const source: ReferenceTypeSourceBaseConfigurationEditorModel = new ReferenceTypeSourceBaseConfigurationEditorModel(this.validationErrorModel); + const source: ExternalFetcherBaseSourceConfigurationEditorModel = new ExternalFetcherBaseSourceConfigurationEditorModel(this.validationErrorModel); return source.buildForm({ rootPath: 'definition.sources[' + index + '].' }); } @@ -76,7 +74,7 @@ export class ReferenceTypeEditorModel extends BaseEditorModel implements Referen } createOptions(sourceIndex: number, index: number): UntypedFormGroup { - const fieldMapping: ReferenceTypeStaticOptionEditorModel = new ReferenceTypeStaticOptionEditorModel(this.validationErrorModel); + const fieldMapping: StaticOptionEditorModel = new StaticOptionEditorModel(this.validationErrorModel); return fieldMapping.buildForm({ rootPath: 'definition.sources[' + sourceIndex + '].options[' + index + '].'}); } @@ -121,7 +119,7 @@ export class ReferenceTypeEditorModel extends BaseEditorModel implements Referen export class ReferenceTypeDefinitionEditorModel implements ReferenceTypeDefinitionPersist { fields: ReferenceTypeFieldEditorModel[] = []; - sources: ReferenceTypeSourceBaseConfigurationEditorModel[] = []; + sources: ExternalFetcherBaseSourceConfigurationEditorModel[] = []; protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); @@ -132,7 +130,7 @@ export class ReferenceTypeDefinitionEditorModel implements ReferenceTypeDefiniti public fromModel(item: ReferenceTypeDefinition): ReferenceTypeDefinitionEditorModel { if (item) { if (item.fields) { item.fields.map(x => this.fields.push(new ReferenceTypeFieldEditorModel(this.validationErrorModel).fromModel(x))); } - if (item.sources) { item.sources.map(x => this.sources.push(new ReferenceTypeSourceBaseConfigurationEditorModel(this.validationErrorModel).fromModel(x))); } + if (item.sources) { item.sources.map(x => this.sources.push(new ExternalFetcherBaseSourceConfigurationEditorModel(this.validationErrorModel).fromModel(x))); } } return this; } @@ -207,7 +205,7 @@ export class ReferenceTypeDefinitionEditorModel implements ReferenceTypeDefiniti }): void { const { validationErrorModel, rootPath, formArray } = params; formArray?.controls?.forEach( - (control, index) => ReferenceTypeSourceBaseConfigurationEditorModel.reapplyValidators({ + (control, index) => ExternalFetcherBaseSourceConfigurationEditorModel.reapplyValidators({ formGroup: control as UntypedFormGroup, rootPath: `${rootPath}sources[${index}].`, validationErrorModel: validationErrorModel @@ -296,693 +294,3 @@ export class ReferenceTypeFieldEditorModel implements ReferenceTypeFieldPersist }) } } - -export class ReferenceTypeSourceBaseConfigurationEditorModel implements ReferenceTypeSourceBaseConfigurationPersist { - type: ReferenceTypeSourceType; - key: string; - label: string; - ordinal: number; - - url: string; - results: ResultsConfigurationEditorModel = new ResultsConfigurationEditorModel(this.validationErrorModel); - paginationPath: string; - contentType: string; - firstPage: string; - httpMethod: ReferenceTypeExternalApiHTTPMethodType; - requestBody?: string; - filterType?: string; - auth: AuthenticationConfigurationEditorModel = new AuthenticationConfigurationEditorModel(this.validationErrorModel); - queries?: QueryConfigEditorModel[] = []; - - options: ReferenceTypeStaticOptionEditorModel[] = []; - - referenceTypeDependencyIds: Guid[] = []; - - protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); - - constructor( - public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() - ) { } - - public fromModel(item: ReferenceTypeSourceBaseConfiguration): ReferenceTypeSourceBaseConfigurationEditorModel { - if (item) { - this.type = item.type; - this.key = item.key; - this.label = item.label; - this.ordinal = item.ordinal; - - if (item.url) this.url = item.url; - if (item.results) this.results = new ResultsConfigurationEditorModel(this.validationErrorModel).fromModel(item.results); - if (item.paginationPath) this.paginationPath = item.paginationPath; - if (item.contentType) this.contentType = item.contentType; - if (item.firstPage) this.firstPage = item.firstPage; - if (item.httpMethod != null) this.httpMethod = item.httpMethod; - if (item.requestBody) this.requestBody = item.requestBody; - if (item.filterType) this.filterType = item.filterType; - if (item.auth) this.auth = new AuthenticationConfigurationEditorModel(this.validationErrorModel).fromModel(item.auth); - if (item.queries) { item.queries.map(x => this.queries.push(new QueryConfigEditorModel(this.validationErrorModel).fromModel(x))); } - - if (item.options) { - item.options.map(x => this.options.push(new ReferenceTypeStaticOptionEditorModel(this.validationErrorModel).fromModel(x))); - } else { - this.options.push(new ReferenceTypeStaticOptionEditorModel().fromModel({ code: 'reference_id', value: undefined })); - this.options.push(new ReferenceTypeStaticOptionEditorModel().fromModel({ code: 'label', value: undefined })); - this.options.push(new ReferenceTypeStaticOptionEditorModel().fromModel({ code: 'description', value: undefined })); - } - - if (item.referenceTypeDependencies) { item.referenceTypeDependencies.forEach(referenceType => this.referenceTypeDependencyIds.push(referenceType.id))} - } - return this; - } - - buildForm(params?: { - context?: ValidationContext, - disabled?: boolean, - rootPath?: string - }): UntypedFormGroup { - let { context = null, disabled = false, rootPath } = params ?? {} - if (context == null) { - context = ReferenceTypeSourceBaseConfigurationEditorModel.createValidationContext({ - validationErrorModel: this.validationErrorModel, - rootPath - }); - } - - return this.formBuilder.group({ - type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators], - key: [{ value: this.key, disabled: disabled }, context.getValidation('key').validators], - label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], - ordinal: [{ value: this.ordinal, disabled: disabled }, context.getValidation('ordinal').validators], - - url: [{ value: this.url, disabled: disabled }, context.getValidation('url').validators], - results: this.results.buildForm({ - rootPath: `${rootPath}results.`, - }), - paginationPath: [{ value: this.paginationPath, disabled: disabled }, context.getValidation('paginationPath').validators], - contentType: [{ value: this.contentType, disabled: disabled }, context.getValidation('contentType').validators], - firstPage: [{ value: this.firstPage, disabled: disabled }, context.getValidation('firstPage').validators], - httpMethod: [{ value: this.httpMethod, disabled: disabled }, context.getValidation('httpMethod').validators], - requestBody: [{ value: this.requestBody, disabled: disabled }, context.getValidation('requestBody').validators], - filterType: [{ value: this.filterType, disabled: disabled }, context.getValidation('filterType').validators], - auth: this.auth.buildForm({ - rootPath: `${rootPath}auth.` - }), - queries: this.formBuilder.array( - (this.queries ?? []).map( - (item, index) => item.buildForm({ - rootPath: `${rootPath}queries[${index}].` - }) - ), context.getValidation('queries').validators - ), - options: this.formBuilder.array( - (this.options ?? []).map( - (item, index) => new ReferenceTypeStaticOptionEditorModel( - this.validationErrorModel - ).fromModel(item).buildForm({ - rootPath: `${rootPath}options[${index}].` - }) - ), context.getValidation('options').validators - ), - referenceTypeDependencyIds: [{ value: this.referenceTypeDependencyIds, disabled: disabled }, context.getValidation('referenceTypeDependencyIds').validators], - - }); - } - - static createValidationContext(params: { - rootPath?: string, - validationErrorModel: ValidationErrorModel - }): ValidationContext { - const { rootPath = '', validationErrorModel } = params; - - const baseContext: ValidationContext = new ValidationContext(); - const baseValidationArray: Validation[] = new Array(); - baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}type`)] }); - baseValidationArray.push({ key: 'key', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}key`)] }); - baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}label`)] }); - baseValidationArray.push({ key: 'ordinal', validators: [Validators.required, Validators.pattern("^[0-9]*$"), BackendErrorValidator(validationErrorModel, `${rootPath}ordinal`)] }); - - baseValidationArray.push({ key: 'url', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}url`)] }); - baseValidationArray.push({ key: 'paginationPath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}paginationPath`)] }); - baseValidationArray.push({ key: 'contentType', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}contentType`)] }); - baseValidationArray.push({ key: 'firstPage', validators: [Validators.pattern("^[0-9]*$"), BackendErrorValidator(validationErrorModel, `${rootPath}firstPage`)] }); - baseValidationArray.push({ key: 'httpMethod', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}httpMethod`)] }); - baseValidationArray.push({ key: 'requestBody', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}requestBody`)] }); - baseValidationArray.push({ key: 'filterType', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}filterType`)] }); - baseValidationArray.push({ key: 'results', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}results`)] }); - baseValidationArray.push({ key: 'queries', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}queries`)] }); - - baseValidationArray.push({ key: 'options', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}options`)] }); - - baseValidationArray.push({ key: 'referenceTypeDependencyIds', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}referenceTypeDependencyIds`)] }); - - baseContext.validation = baseValidationArray; - return baseContext; - } - - static reapplyValidators(params: { - formGroup: UntypedFormGroup, - validationErrorModel: ValidationErrorModel, - rootPath: string - }): void { - - const { formGroup, rootPath, validationErrorModel } = params; - const context = ReferenceTypeSourceBaseConfigurationEditorModel.createValidationContext({ - rootPath, - validationErrorModel - }); - - ['type', 'key', 'label', 'ordinal', 'url', 'paginationPath', 'contentType', 'firstPage', 'httpMethod', 'requestBody','filterType', 'referenceTypeDependencyIds'].forEach(keyField => { - const control = formGroup?.get(keyField); - control?.clearValidators(); - control?.addValidators(context.getValidation(keyField).validators); - }); - - AuthenticationConfigurationEditorModel.reapplyAuthValidators({ - formGroup: formGroup?.get('auth') as UntypedFormGroup, - rootPath: `${rootPath}auth.`, - validationErrorModel: validationErrorModel - }); - - ResultsConfigurationEditorModel.reapplyValidators({ - formGroup: formGroup?.get('results') as UntypedFormGroup, - rootPath: `${rootPath}results.`, - validationErrorModel: validationErrorModel - }); - - (formGroup.get('options') as FormArray).controls?.forEach( - (control, index) => ReferenceTypeStaticOptionEditorModel.reapplyStaticOptionsValidators({ - formGroup: control as UntypedFormGroup, - rootPath: `${rootPath}options[${index}].`, - validationErrorModel: validationErrorModel - } - ) - ); - - (formGroup.get('queries') as FormArray).controls?.forEach( - (control, index) => QueryConfigEditorModel.reapplyValidators({ - formGroup: control as UntypedFormGroup, - rootPath: `${rootPath}queries[${index}].`, - validationErrorModel: validationErrorModel - }) - ); - - } -} - -export class ResultsConfigurationEditorModel implements ResultsConfigurationPersist { - public resultsArrayPath: string; - public fieldsMapping: ResultFieldsMappingConfigurationEditorModel[] = []; - - protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); - - constructor( - public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() - ) { } - - fromModel(item: ResultsConfiguration): ResultsConfigurationEditorModel { - this.resultsArrayPath = item.resultsArrayPath; - if (item.fieldsMapping) { item.fieldsMapping.map(x => this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel(x))); } - else { - this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel({ code: 'reference_id', responsePath: undefined })); - this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel({ code: 'label', responsePath: undefined })); - this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel({ code: 'description', responsePath: undefined })); - } - return this; - } - - buildForm(params?: { - context?: ValidationContext, - disabled?: boolean, - rootPath?: string - }): UntypedFormGroup { - let { context = null, disabled = false, rootPath } = params ?? {} - if (context == null) { - context = ResultsConfigurationEditorModel.createValidationContext({ - validationErrorModel: this.validationErrorModel, - rootPath - }); - } - - return this.formBuilder.group({ - resultsArrayPath: [{ value: this.resultsArrayPath, disabled: disabled }, context.getValidation('resultsArrayPath').validators], - fieldsMapping: this.formBuilder.array( - (this.fieldsMapping ?? []).map( - (item, index) => new ResultFieldsMappingConfigurationEditorModel( - this.validationErrorModel - ).fromModel(item).buildForm({ - rootPath: `${rootPath}fieldsMapping[${index}].` - }) - ), context.getValidation('fieldsMapping').validators - ) - - }); - } - - static createValidationContext(params: { - rootPath?: string, - validationErrorModel: ValidationErrorModel - }): ValidationContext { - const { rootPath = '', validationErrorModel } = params; - - const baseContext: ValidationContext = new ValidationContext(); - const baseValidationArray: Validation[] = new Array(); - baseValidationArray.push({ key: 'resultsArrayPath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}resultsArrayPath`)] }); - baseValidationArray.push({ key: 'fieldsMapping', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldsMapping`)] }); - - baseContext.validation = baseValidationArray; - return baseContext; - } - - static reapplyValidators(params: { - formGroup: UntypedFormGroup, - validationErrorModel: ValidationErrorModel, - rootPath: string - }): void { - - const { formGroup, rootPath, validationErrorModel } = params; - const context = ResultsConfigurationEditorModel.createValidationContext({ - rootPath, - validationErrorModel - }); - - ['resultsArrayPath'].forEach(keyField => { - const control = formGroup?.get(keyField); - control?.clearValidators(); - control?.addValidators(context.getValidation(keyField).validators); - }); - - (formGroup.get('fieldsMapping') as FormArray).controls?.forEach( - (control, index) => ResultFieldsMappingConfigurationEditorModel.reapplyFieldsMappingValidators({ - formGroup: control as UntypedFormGroup, - rootPath: `${rootPath}fieldsMapping[${index}].`, - validationErrorModel: validationErrorModel - } - ) - ); - } -} - -export class ResultFieldsMappingConfigurationEditorModel implements ResultFieldsMappingConfigurationPersist { - public code: string; - public responsePath: string; - - protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); - - constructor( - public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() - ) { } - - fromModel(item: ResultFieldsMappingConfiguration): ResultFieldsMappingConfigurationEditorModel { - this.code = item.code; - this.responsePath = item.responsePath; - - return this; - } - - buildForm(params?: { - context?: ValidationContext, - disabled?: boolean, - rootPath?: string - }): UntypedFormGroup { - let { context = null, disabled = false, rootPath } = params ?? {} - if (context == null) { - context = ResultFieldsMappingConfigurationEditorModel.createValidationContext({ - validationErrorModel: this.validationErrorModel, - rootPath - }); - } - - return this.formBuilder.group({ - code: [{ value: this.code, disabled: true }, context.getValidation('code').validators], - responsePath: [{ value: this.responsePath, disabled: disabled }, context.getValidation('responsePath').validators], - }); - } - - static createValidationContext(params: { - rootPath?: string, - validationErrorModel: ValidationErrorModel - }): ValidationContext { - const { rootPath = '', validationErrorModel } = params; - - const baseContext: ValidationContext = new ValidationContext(); - const baseValidationArray: Validation[] = new Array(); - baseValidationArray.push({ key: 'code', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); - baseValidationArray.push({ key: 'responsePath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}responsePath`)] }); - - baseContext.validation = baseValidationArray; - return baseContext; - } - - static reapplyFieldsMappingValidators(params: { - formGroup: UntypedFormGroup, - validationErrorModel: ValidationErrorModel, - rootPath: string - }): void { - - const { formGroup, rootPath, validationErrorModel } = params; - const context = ResultFieldsMappingConfigurationEditorModel.createValidationContext({ - rootPath, - validationErrorModel - }); - - ['code', 'responsePath'].forEach(keyField => { - const control = formGroup?.get(keyField); - control?.clearValidators(); - control?.addValidators(context.getValidation(keyField).validators); - }); - } -} - -export class AuthenticationConfigurationEditorModel implements AuthenticationConfigurationPersist { - public enabled: boolean = false; - public authUrl: string; - public authMethod: ReferenceTypeExternalApiHTTPMethodType; - public authTokenPath: string; - public authRequestBody: string; - public type: string; - - protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); - - constructor( - public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() - ) { } - - fromModel(item: AuthenticationConfiguration): AuthenticationConfigurationEditorModel { - this.enabled = item.enabled; - this.authUrl = item.authUrl; - this.authMethod = item.authMethod; - this.authTokenPath = item.authTokenPath; - this.authRequestBody = item.authRequestBody; - this.type = item.type; - - return this; - } - - buildForm(params?: { - context?: ValidationContext, - disabled?: boolean, - rootPath?: string - }): UntypedFormGroup { - let { context = null, disabled = false, rootPath } = params ?? {} - if (context == null) { - context = AuthenticationConfigurationEditorModel.createValidationContext({ - validationErrorModel: this.validationErrorModel, - rootPath - }); - } - - return this.formBuilder.group({ - enabled: [{ value: this.enabled, disabled: disabled }, context.getValidation('enabled').validators], - authUrl: [{ value: this.authUrl, disabled: disabled }, context.getValidation('authUrl').validators], - authMethod: [{ value: this.authMethod, disabled: disabled }, context.getValidation('authMethod').validators], - authTokenPath: [{ value: this.authTokenPath, disabled: disabled }, context.getValidation('authTokenPath').validators], - authRequestBody: [{ value: this.authRequestBody, disabled: disabled }, context.getValidation('authRequestBody').validators], - type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators], - }); - } - - static createValidationContext(params: { - rootPath?: string, - validationErrorModel: ValidationErrorModel - }): ValidationContext { - const { rootPath = '', validationErrorModel } = params; - - const baseContext: ValidationContext = new ValidationContext(); - const baseValidationArray: Validation[] = new Array(); - baseValidationArray.push({ key: 'enabled', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}enabled`)] }); - baseValidationArray.push({ key: 'authUrl', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authUrl`)] }); - baseValidationArray.push({ key: 'authMethod', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authMethod`)] }); - baseValidationArray.push({ key: 'authTokenPath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authTokenPath`)] }); - baseValidationArray.push({ key: 'authRequestBody', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authRequestBody`)] }); - baseValidationArray.push({ key: 'type', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}type`)] }); - - baseContext.validation = baseValidationArray; - return baseContext; - } - - static reapplyAuthValidators(params: { - formGroup: UntypedFormGroup, - validationErrorModel: ValidationErrorModel, - rootPath: string - }): void { - - const { formGroup, rootPath, validationErrorModel } = params; - const context = AuthenticationConfigurationEditorModel.createValidationContext({ - rootPath, - validationErrorModel - }); - - ['enabled', 'authUrl', 'authMethod', 'authTokenPath', 'authRequestBody', 'type'].forEach(keyField => { - const control = formGroup?.get(keyField); - control?.clearValidators(); - control?.addValidators(context.getValidation(keyField).validators); - }) - } -} - -export class QueryConfigEditorModel implements QueryConfigPersist { - public name: string; - public defaultValue: string; - public cases: QueryCaseConfigEditorModel[] = []; - - protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); - - constructor( - public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() - ) { } - - fromModel(item: QueryConfig): QueryConfigEditorModel { - this.name = item.name; - this.defaultValue = item.defaultValue; - if (item.cases) { item.cases.map(x => this.cases.push(new QueryCaseConfigEditorModel(this.validationErrorModel).fromModel(x))); } - - return this; - } - - buildForm(params?: { - context?: ValidationContext, - disabled?: boolean, - rootPath?: string - }): UntypedFormGroup { - let { context = null, disabled = false, rootPath } = params ?? {} - if (context == null) { - context = QueryConfigEditorModel.createValidationContext({ - validationErrorModel: this.validationErrorModel, - rootPath - }); - } - - return this.formBuilder.group({ - name: [{ value: this.name, disabled: disabled }, context.getValidation('name').validators], - defaultValue: [{ value: this.defaultValue, disabled: disabled }, context.getValidation('defaultValue').validators], - cases: this.formBuilder.array( - (this.cases ?? []).map( - (item, index) => item.buildForm({ - rootPath: `${rootPath}cases[${index}].` - }) - ), context.getValidation('cases').validators - ) - }); - } - - static createValidationContext(params: { - rootPath?: string, - validationErrorModel: ValidationErrorModel - }): ValidationContext { - const { rootPath = '', validationErrorModel } = params; - - const baseContext: ValidationContext = new ValidationContext(); - const baseValidationArray: Validation[] = new Array(); - baseValidationArray.push({ key: 'name', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}name`)] }); - baseValidationArray.push({ key: 'defaultValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}defaultValue`)] }); - baseValidationArray.push({ key: 'cases', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}cases`)] }); - - baseContext.validation = baseValidationArray; - return baseContext; - } - - static reapplyValidators(params: { - formGroup: UntypedFormGroup, - validationErrorModel: ValidationErrorModel, - rootPath: string - }): void { - - const { formGroup, rootPath, validationErrorModel } = params; - const context = QueryConfigEditorModel.createValidationContext({ - rootPath, - validationErrorModel - }); - - ['name', 'defaultValue'].forEach(keyField => { - const control = formGroup?.get(keyField); - control?.clearValidators(); - control?.addValidators(context.getValidation(keyField).validators); - }); - - (formGroup.get('cases') as FormArray).controls?.forEach( - (control, index) => QueryCaseConfigEditorModel.reapplyValidators({ - formGroup: control as UntypedFormGroup, - rootPath: `${rootPath}cases[${index}].`, - validationErrorModel: validationErrorModel - } - ) - ); - } -} - -export class QueryCaseConfigEditorModel implements QueryCaseConfigPersist { - public likePattern: string; - public separator: string; - public value: string; - public referenceTypeId: Guid; - public referenceTypeSourceKey: string; - - protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); - - constructor( - public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() - ) { } - - fromModel(item: QueryCaseConfig): QueryCaseConfigEditorModel { - this.likePattern = item.likePattern; - this.separator = item.separator; - this.value = item.value; - if(item?.referenceType?.id) this.referenceTypeId = item.referenceType.id; - this.referenceTypeSourceKey = item.referenceTypeSourceKey; - - return this; - } - - buildForm(params?: { - context?: ValidationContext, - disabled?: boolean, - rootPath?: string - }): UntypedFormGroup { - let { context = null, disabled = false, rootPath } = params ?? {} - if (context == null) { - context = QueryCaseConfigEditorModel.createValidationContext({ - validationErrorModel: this.validationErrorModel, - rootPath - }); - } - - return this.formBuilder.group({ - likePattern: [{ value: this.likePattern, disabled: disabled }, context.getValidation('likePattern').validators], - separator: [{ value: this.separator, disabled: disabled }, context.getValidation('separator').validators], - value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators], - referenceTypeId: [{ value: this.referenceTypeId, disabled: disabled }, context.getValidation('referenceTypeId').validators], - referenceTypeSourceKey: [{ value: this.referenceTypeSourceKey, disabled: disabled }, context.getValidation('referenceTypeSourceKey').validators], - }); - } - - static createValidationContext(params: { - rootPath?: string, - validationErrorModel: ValidationErrorModel - }): ValidationContext { - const { rootPath = '', validationErrorModel } = params; - - const baseContext: ValidationContext = new ValidationContext(); - const baseValidationArray: Validation[] = new Array(); - baseValidationArray.push({ key: 'likePattern', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}likePattern`)] }); - baseValidationArray.push({ key: 'separator', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}separator`)] }); - baseValidationArray.push({ key: 'value', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}value`)] }); - baseValidationArray.push({ key: 'referenceTypeId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}referenceTypeId`)] }); - baseValidationArray.push({ key: 'referenceTypeSourceKey', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}referenceTypeSourceKey`)] }); - - baseContext.validation = baseValidationArray; - return baseContext; - } - - static reapplyValidators(params: { - formGroup: UntypedFormGroup, - validationErrorModel: ValidationErrorModel, - rootPath: string - }): void { - - const { formGroup, rootPath, validationErrorModel } = params; - const context = QueryCaseConfigEditorModel.createValidationContext({ - rootPath, - validationErrorModel - }); - - ['likePattern', 'separator', 'value', 'referenceTypeId', 'referenceTypeSourceKey'].forEach(keyField => { - const control = formGroup?.get(keyField); - control?.clearValidators(); - control?.addValidators(context.getValidation(keyField).validators); - }) - } -} - -export class ReferenceTypeStaticOptionEditorModel implements ReferenceTypeStaticOptionPersist { - public code: string; - public value: string; - - - protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); - - constructor( - public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() - ) { } - - fromModel(item: ReferenceTypeStaticOption): ReferenceTypeStaticOptionEditorModel { - this.code = item.code; - this.value = item.value; - - return this; - } - - buildForm(params?: { - context?: ValidationContext, - disabled?: boolean, - rootPath?: string - }): UntypedFormGroup { - let { context = null, disabled = false, rootPath } = params ?? {} - if (context == null) { - context = ReferenceTypeStaticOptionEditorModel.createValidationContext({ - validationErrorModel: this.validationErrorModel, - rootPath - }); - } - - return this.formBuilder.group({ - code: [{ value: this.code, disabled: true }, context.getValidation('code').validators], - value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators], - }); - } - - static createValidationContext(params: { - rootPath?: string, - validationErrorModel: ValidationErrorModel - }): ValidationContext { - const { rootPath = '', validationErrorModel } = params; - - const baseContext: ValidationContext = new ValidationContext(); - const baseValidationArray: Validation[] = new Array(); - baseValidationArray.push({ key: 'code', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); - baseValidationArray.push({ key: 'value', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}value`)] }); - - baseContext.validation = baseValidationArray; - return baseContext; - } - - static reapplyStaticOptionsValidators(params: { - formGroup: UntypedFormGroup, - validationErrorModel: ValidationErrorModel, - rootPath: string - }): void { - - const { formGroup, rootPath, validationErrorModel } = params; - const context = ReferenceTypeStaticOptionEditorModel.createValidationContext({ - rootPath, - validationErrorModel - }); - - ['code', 'value'].forEach(keyField => { - const control = formGroup?.get(keyField); - control?.clearValidators(); - control?.addValidators(context.getValidation(keyField).validators); - }); - - } -} - diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.resolver.ts b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.resolver.ts index fac38d952..0ec6d22c9 100644 --- a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.resolver.ts +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.resolver.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { AuthenticationConfiguration, QueryConfig, ReferenceType, ReferenceTypeDefinition, ReferenceTypeField, ReferenceTypeSourceBaseConfiguration, ResultsConfiguration, ResultFieldsMappingConfiguration, ReferenceTypeStaticOption, QueryCaseConfig } from '@app/core/model/reference-type/reference-type'; +import { AuthenticationConfiguration, ExternalFetcherBaseSourceConfiguration, QueryCaseConfig, QueryConfig, ResultFieldsMappingConfiguration, ResultsConfiguration, StaticOption } from '@app/core/model/external-fetcher/external-fetcher'; +import { ReferenceType, ReferenceTypeDefinition, ReferenceTypeField } from '@app/core/model/reference-type/reference-type'; import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service'; import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; import { BaseEditorResolver } from '@common/base/base-editor.resolver'; @@ -27,45 +28,45 @@ export class ReferenceTypeEditorResolver extends BaseEditorResolver { [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.description)].join('.'), [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.dataType)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.type)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.key)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.label)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.ordinal)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.referenceTypeDependencies),nameof(x => x.id)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.referenceTypeDependencies),nameof(x => x.name)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.type)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.key)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.ordinal)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.referenceTypeDependencies),nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.referenceTypeDependencies),nameof(x => x.name)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.url)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.results), nameof(x => x.resultsArrayPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.url)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.results), nameof(x => x.resultsArrayPath)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.code)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.responsePath)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.responsePath)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.paginationPath)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.contentType)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.firstPage)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.httpMethod)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.requestBody)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.filterType)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.paginationPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.contentType)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.firstPage)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.httpMethod)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.requestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.filterType)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.enabled)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authUrl)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authMethod)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authTokenPath)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authRequestBody)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.type)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.enabled)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authUrl)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authMethod)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authTokenPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.authRequestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.auth),nameof(x => x.type)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.name)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.defaultValue)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.likePattern)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.separator)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.value)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceType)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceType),nameof(x => x.id)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceType),nameof(x => x.name)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceTypeSourceKey)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.name)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.defaultValue)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.likePattern)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.separator)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.value)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceType)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceType),nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceType),nameof(x => x.name)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.queries),nameof(x => x.cases),nameof(x => x.referenceTypeSourceKey)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.options),nameof(x => x.code)].join('.'), - [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.options),nameof(x => x.value)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.options),nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.sources), nameof(x => x.options),nameof(x => x.value)].join('.'), nameof(x => x.createdAt), nameof(x => x.updatedAt), diff --git a/dmp-frontend/src/app/ui/admin/reference-type/reference-type.module.ts b/dmp-frontend/src/app/ui/admin/reference-type/reference-type.module.ts index 57eca9e06..b160fbb8d 100644 --- a/dmp-frontend/src/app/ui/admin/reference-type/reference-type.module.ts +++ b/dmp-frontend/src/app/ui/admin/reference-type/reference-type.module.ts @@ -15,6 +15,7 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; import { CommonFormattingModule } from '@common/formatting/common-formatting.module'; import { ReferenceTypeListingComponent } from './listing/reference-type-listing.component'; +import { ExternalFetcherSourceModule } from '@app/ui/external-fetcher/external-fetcher-source.module'; @NgModule({ @@ -36,7 +37,8 @@ import { ReferenceTypeListingComponent } from './listing/reference-type-listing. NgxDropzoneModule, DragDropModule, AutoCompleteModule, - CommonFormattingModule + CommonFormattingModule, + ExternalFetcherSourceModule ] }) export class ReferenceTypeModule { } diff --git a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html index 72761e31c..6b08a1e55 100644 --- a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html +++ b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html @@ -162,7 +162,7 @@ -

{{ 'DMP-OVERVIEW.OVERVIEW.NEW-VERSION' | translate }} +

{{ 'DMP-OVERVIEW.ACTIONS.NEW-VERSION' | translate }}

diff --git a/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source-editor.model.ts b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source-editor.model.ts new file mode 100644 index 000000000..550c097e9 --- /dev/null +++ b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source-editor.model.ts @@ -0,0 +1,697 @@ +import { FormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; +import { Validation, ValidationContext } from "@common/forms/validation/validation-context"; +import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; +import { ExternalFetcherApiHTTPMethodType } from "@app/core/common/enum/external-fetcher-api-http-method-type"; +import { ExternalFetcherSourceType } from "@app/core/common/enum/external-fetcher-source-type"; +import { AuthenticationConfiguration, AuthenticationConfigurationPersist, ExternalFetcherBaseSourceConfiguration, ExternalFetcherBaseSourceConfigurationPersist, QueryCaseConfig, QueryCaseConfigPersist, QueryConfig, QueryConfigPersist, ResultFieldsMappingConfiguration, ResultFieldsMappingConfigurationPersist, ResultsConfiguration, ResultsConfigurationPersist, StaticOption, StaticOptionPersist } from "@app/core/model/external-fetcher/external-fetcher"; +import { Guid } from "@common/types/guid"; + +export class ExternalFetcherBaseSourceConfigurationEditorModel implements ExternalFetcherBaseSourceConfigurationPersist { + type: ExternalFetcherSourceType = ExternalFetcherSourceType.API; + key: string; + label: string; + ordinal: number; + + url: string; + results: ResultsConfigurationEditorModel = new ResultsConfigurationEditorModel(this.validationErrorModel); + paginationPath: string; + contentType: string; + firstPage: string; + httpMethod: ExternalFetcherApiHTTPMethodType; + requestBody?: string; + filterType?: string; + auth: AuthenticationConfigurationEditorModel = new AuthenticationConfigurationEditorModel(this.validationErrorModel); + queries?: QueryConfigEditorModel[] = []; + + options: StaticOptionEditorModel[] = []; + + referenceTypeDependencyIds: Guid[]; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: ExternalFetcherBaseSourceConfiguration): ExternalFetcherBaseSourceConfigurationEditorModel { + if (item) { + this.type = item.type; + this.key = item.key; + this.label = item.label; + this.ordinal = item.ordinal; + + if (item.url) this.url = item.url; + if (item.results) this.results = new ResultsConfigurationEditorModel(this.validationErrorModel).fromModel(item.results); + if (item.paginationPath) this.paginationPath = item.paginationPath; + if (item.contentType) this.contentType = item.contentType; + if (item.firstPage) this.firstPage = item.firstPage; + if (item.httpMethod != null) this.httpMethod = item.httpMethod; + if (item.requestBody) this.requestBody = item.requestBody; + if (item.filterType) this.filterType = item.filterType; + if (item.auth) this.auth = new AuthenticationConfigurationEditorModel(this.validationErrorModel).fromModel(item.auth); + if (item.queries) { item.queries.map(x => this.queries.push(new QueryConfigEditorModel(this.validationErrorModel).fromModel(x))); } + + if (item.options) { + item.options.map(x => this.options.push(new StaticOptionEditorModel(this.validationErrorModel).fromModel(x))); + } else { + this.options.push(new StaticOptionEditorModel().fromModel({ code: 'reference_id', value: undefined })); + this.options.push(new StaticOptionEditorModel().fromModel({ code: 'label', value: undefined })); + this.options.push(new StaticOptionEditorModel().fromModel({ code: 'description', value: undefined })); + } + + if (item.referenceTypeDependencies) { this.referenceTypeDependencyIds = item.referenceTypeDependencies.map(x => x.id)} + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ExternalFetcherBaseSourceConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators], + key: [{ value: this.key, disabled: disabled }, context.getValidation('key').validators], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], + ordinal: [{ value: this.ordinal, disabled: disabled }, context.getValidation('ordinal').validators], + + url: [{ value: this.url, disabled: disabled }, context.getValidation('url').validators], + results: this.results.buildForm({ + rootPath: `${rootPath}results.`, + }), + paginationPath: [{ value: this.paginationPath, disabled: disabled }, context.getValidation('paginationPath').validators], + contentType: [{ value: this.contentType, disabled: disabled }, context.getValidation('contentType').validators], + firstPage: [{ value: this.firstPage, disabled: disabled }, context.getValidation('firstPage').validators], + httpMethod: [{ value: this.httpMethod, disabled: disabled }, context.getValidation('httpMethod').validators], + requestBody: [{ value: this.requestBody, disabled: disabled }, context.getValidation('requestBody').validators], + filterType: [{ value: this.filterType, disabled: disabled }, context.getValidation('filterType').validators], + auth: this.auth.buildForm({ + rootPath: `${rootPath}auth.` + }), + queries: this.formBuilder.array( + (this.queries ?? []).map( + (item, index) => item.buildForm({ + rootPath: `${rootPath}queries[${index}].` + }) + ), context.getValidation('queries').validators + ), + options: this.formBuilder.array( + (this.options ?? []).map( + (item, index) => new StaticOptionEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `${rootPath}options[${index}].` + }) + ), context.getValidation('options').validators + ), + referenceTypeDependencyIds: [{ value: this.referenceTypeDependencyIds, disabled: disabled }, context.getValidation('referenceTypeDependencyIds').validators], + + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}type`)] }); + baseValidationArray.push({ key: 'key', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}key`)] }); + baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}label`)] }); + baseValidationArray.push({ key: 'ordinal', validators: [Validators.required, Validators.pattern("^[0-9]*$"), BackendErrorValidator(validationErrorModel, `${rootPath}ordinal`)] }); + + baseValidationArray.push({ key: 'url', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}url`)] }); + baseValidationArray.push({ key: 'paginationPath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}paginationPath`)] }); + baseValidationArray.push({ key: 'contentType', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}contentType`)] }); + baseValidationArray.push({ key: 'firstPage', validators: [Validators.pattern("^[0-9]*$"), BackendErrorValidator(validationErrorModel, `${rootPath}firstPage`)] }); + baseValidationArray.push({ key: 'httpMethod', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}httpMethod`)] }); + baseValidationArray.push({ key: 'requestBody', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}requestBody`)] }); + baseValidationArray.push({ key: 'filterType', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}filterType`)] }); + baseValidationArray.push({ key: 'results', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}results`)] }); + baseValidationArray.push({ key: 'queries', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}queries`)] }); + + baseValidationArray.push({ key: 'options', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}options`)] }); + + baseValidationArray.push({ key: 'referenceTypeDependencyIds', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}referenceTypeDependencyIds`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = ExternalFetcherBaseSourceConfigurationEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['type', 'key', 'label', 'ordinal', 'url', 'paginationPath', 'contentType', 'firstPage', 'httpMethod', 'requestBody','filterType', 'referenceTypeDependencyIds'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }); + + AuthenticationConfigurationEditorModel.reapplyAuthValidators({ + formGroup: formGroup?.get('auth') as UntypedFormGroup, + rootPath: `${rootPath}auth.`, + validationErrorModel: validationErrorModel + }); + + ResultsConfigurationEditorModel.reapplyValidators({ + formGroup: formGroup?.get('results') as UntypedFormGroup, + rootPath: `${rootPath}results.`, + validationErrorModel: validationErrorModel + }); + + (formGroup.get('options') as FormArray).controls?.forEach( + (control, index) => StaticOptionEditorModel.reapplyStaticOptionsValidators({ + formGroup: control as UntypedFormGroup, + rootPath: `${rootPath}options[${index}].`, + validationErrorModel: validationErrorModel + } + ) + ); + + (formGroup.get('queries') as FormArray).controls?.forEach( + (control, index) => QueryConfigEditorModel.reapplyValidators({ + formGroup: control as UntypedFormGroup, + rootPath: `${rootPath}queries[${index}].`, + validationErrorModel: validationErrorModel + }) + ); + + } +} + +export class ResultsConfigurationEditorModel implements ResultsConfigurationPersist { + public resultsArrayPath: string; + public fieldsMapping: ResultFieldsMappingConfigurationEditorModel[] = []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: ResultsConfiguration): ResultsConfigurationEditorModel { + this.resultsArrayPath = item.resultsArrayPath; + if (item.fieldsMapping) { item.fieldsMapping.map(x => this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel(x))); } + else { + this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel({ code: 'reference_id', responsePath: undefined })); + this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel({ code: 'label', responsePath: undefined })); + this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel(this.validationErrorModel).fromModel({ code: 'description', responsePath: undefined })); + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ResultsConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + resultsArrayPath: [{ value: this.resultsArrayPath, disabled: disabled }, context.getValidation('resultsArrayPath').validators], + fieldsMapping: this.formBuilder.array( + (this.fieldsMapping ?? []).map( + (item, index) => new ResultFieldsMappingConfigurationEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `${rootPath}fieldsMapping[${index}].` + }) + ), context.getValidation('fieldsMapping').validators + ) + + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'resultsArrayPath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}resultsArrayPath`)] }); + baseValidationArray.push({ key: 'fieldsMapping', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldsMapping`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = ResultsConfigurationEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['resultsArrayPath'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }); + + (formGroup.get('fieldsMapping') as FormArray).controls?.forEach( + (control, index) => ResultFieldsMappingConfigurationEditorModel.reapplyFieldsMappingValidators({ + formGroup: control as UntypedFormGroup, + rootPath: `${rootPath}fieldsMapping[${index}].`, + validationErrorModel: validationErrorModel + } + ) + ); + } +} + +export class ResultFieldsMappingConfigurationEditorModel implements ResultFieldsMappingConfigurationPersist { + public code: string; + public responsePath: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: ResultFieldsMappingConfiguration): ResultFieldsMappingConfigurationEditorModel { + this.code = item.code; + this.responsePath = item.responsePath; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ResultFieldsMappingConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators], + responsePath: [{ value: this.responsePath, disabled: disabled }, context.getValidation('responsePath').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'code', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); + baseValidationArray.push({ key: 'responsePath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}responsePath`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyFieldsMappingValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = ResultFieldsMappingConfigurationEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['code', 'responsePath'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }); + } +} + +export class AuthenticationConfigurationEditorModel implements AuthenticationConfigurationPersist { + public enabled: boolean = false; + public authUrl: string; + public authMethod: ExternalFetcherApiHTTPMethodType; + public authTokenPath: string; + public authRequestBody: string; + public type: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: AuthenticationConfiguration): AuthenticationConfigurationEditorModel { + this.enabled = item.enabled; + this.authUrl = item.authUrl; + this.authMethod = item.authMethod; + this.authTokenPath = item.authTokenPath; + this.authRequestBody = item.authRequestBody; + this.type = item.type; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = AuthenticationConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + enabled: [{ value: this.enabled, disabled: disabled }, context.getValidation('enabled').validators], + authUrl: [{ value: this.authUrl, disabled: disabled }, context.getValidation('authUrl').validators], + authMethod: [{ value: this.authMethod, disabled: disabled }, context.getValidation('authMethod').validators], + authTokenPath: [{ value: this.authTokenPath, disabled: disabled }, context.getValidation('authTokenPath').validators], + authRequestBody: [{ value: this.authRequestBody, disabled: disabled }, context.getValidation('authRequestBody').validators], + type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'enabled', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}enabled`)] }); + baseValidationArray.push({ key: 'authUrl', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authUrl`)] }); + baseValidationArray.push({ key: 'authMethod', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authMethod`)] }); + baseValidationArray.push({ key: 'authTokenPath', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authTokenPath`)] }); + baseValidationArray.push({ key: 'authRequestBody', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authRequestBody`)] }); + baseValidationArray.push({ key: 'type', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}type`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyAuthValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = AuthenticationConfigurationEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['enabled', 'authUrl', 'authMethod', 'authTokenPath', 'authRequestBody', 'type'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }) + } +} + +export class QueryConfigEditorModel implements QueryConfigPersist { + public name: string; + public defaultValue: string; + public cases: QueryCaseConfigEditorModel[] = []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: QueryConfig): QueryConfigEditorModel { + this.name = item.name; + this.defaultValue = item.defaultValue; + if (item.cases) { item.cases.map(x => this.cases.push(new QueryCaseConfigEditorModel(this.validationErrorModel).fromModel(x))); } + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = QueryConfigEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + name: [{ value: this.name, disabled: disabled }, context.getValidation('name').validators], + defaultValue: [{ value: this.defaultValue, disabled: disabled }, context.getValidation('defaultValue').validators], + cases: this.formBuilder.array( + (this.cases ?? []).map( + (item, index) => item.buildForm({ + rootPath: `${rootPath}cases[${index}].` + }) + ), context.getValidation('cases').validators + ) + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'name', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}name`)] }); + baseValidationArray.push({ key: 'defaultValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}defaultValue`)] }); + baseValidationArray.push({ key: 'cases', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}cases`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = QueryConfigEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['name', 'defaultValue'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }); + + (formGroup.get('cases') as FormArray).controls?.forEach( + (control, index) => QueryCaseConfigEditorModel.reapplyValidators({ + formGroup: control as UntypedFormGroup, + rootPath: `${rootPath}cases[${index}].`, + validationErrorModel: validationErrorModel + } + ) + ); + } +} + +export class QueryCaseConfigEditorModel implements QueryCaseConfigPersist { + public likePattern: string; + public separator: string; + public value: string; + public referenceTypeId: Guid; + public referenceTypeSourceKey: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: QueryCaseConfig): QueryCaseConfigEditorModel { + this.likePattern = item.likePattern; + this.separator = item.separator; + this.value = item.value; + if(item?.referenceType?.id) this.referenceTypeId = item.referenceType.id; + this.referenceTypeSourceKey = item.referenceTypeSourceKey; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = QueryCaseConfigEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + likePattern: [{ value: this.likePattern, disabled: disabled }, context.getValidation('likePattern').validators], + separator: [{ value: this.separator, disabled: disabled }, context.getValidation('separator').validators], + value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators], + referenceTypeId: [{ value: this.referenceTypeId, disabled: disabled }, context.getValidation('referenceTypeId').validators], + referenceTypeSourceKey: [{ value: this.referenceTypeSourceKey, disabled: disabled }, context.getValidation('referenceTypeSourceKey').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'likePattern', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}likePattern`)] }); + baseValidationArray.push({ key: 'separator', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}separator`)] }); + baseValidationArray.push({ key: 'value', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}value`)] }); + baseValidationArray.push({ key: 'referenceTypeId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}referenceTypeId`)] }); + baseValidationArray.push({ key: 'referenceTypeSourceKey', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}referenceTypeSourceKey`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = QueryCaseConfigEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['likePattern', 'separator', 'value', 'referenceTypeId', 'referenceTypeSourceKey'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }) + } +} + +export class StaticOptionEditorModel implements StaticOptionPersist { + public code: string; + public value: string; + + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: StaticOption): StaticOptionEditorModel { + this.code = item.code; + this.value = item.value; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = StaticOptionEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + code: [{ value: this.code, disabled: true }, context.getValidation('code').validators], + value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'code', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); + baseValidationArray.push({ key: 'value', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}value`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + + static reapplyStaticOptionsValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = StaticOptionEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['code', 'value'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }); + + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.html b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.html new file mode 100644 index 000000000..87ab361d9 --- /dev/null +++ b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.html @@ -0,0 +1,359 @@ +
+
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.KEY' | translate}} + + {{formGroup.get('key').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.LABEL' | translate}} + + {{formGroup.get('label').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.ORDINAL' | translate}} + + {{formGroup.get('ordinal').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.DEPENDENCIES' | translate}} + + {{referenceType.name}} + + {{formGroup.get('referenceTypeDependencyIds').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.SOURCE-TYPE' | translate}} + + + {{enumUtils.toExternalFetcherSourceTypeString(sourceType)}} + + + {{formGroup.get('type').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.URL' | translate}} + + {{formGroup.get('url').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.PAGINATION-PATH' | translate}} + + {{formGroup.get('paginationPath').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.CONTENT-TYPE' | translate}} + + {{formGroup.get('contentType').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.FIRST-PAGE' | translate}} + + {{formGroup.get('firstPage').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.HTTP-METHOD' | translate}} + + + {{enumUtils.toExternalFetcherApiHTTPMethodTypeString(httpMethod)}} + + + {{formGroup.get('httpMethod').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.REQUEST-BODY' | translate}} + + {{formGroup.get('requestBody').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.FILTER-TYPE' | translate}} + + {{formGroup.get('filterType').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ +

{{'REFERENCE-TYPE-EDITOR.FIELDS.RESULTS' | translate}}

+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.RESULTS-PATH' | translate}} + + {{formGroup.get('results').get('resultsArrayPath').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ +
+
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.FIELDS.FIELD-MAPPING' | translate}} {{fieldMappingIndex + 1}}

+
+
+
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.CODE' | translate}} + + {{field.get('code').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.RESPONSE-PATH' | translate}} + + {{field.get('responsePath').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
+ +

{{'REFERENCE-TYPE-EDITOR.FIELDS.AUTHENTICATION' | translate}} + +

+
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.URL' | translate}} + + {{formGroup.get('auth').get('authUrl').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.HTTP-METHOD' | translate}} + + + {{enumUtils.toExternalFetcherApiHTTPMethodTypeString(httpMethod)}} + + + {{formGroup.get('auth').get('authMethod').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.TOKEN-PATH' | translate}} + + {{formGroup.get('auth').get('authTokenPath').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.REQUEST-BODY' | translate}} + + {{formGroup.get('auth').get('authRequestBody').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.TYPE' | translate}} + + {{formGroup.get('auth').get('type').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ +

{{'REFERENCE-TYPE-EDITOR.FIELDS.QUERIES' | translate}} + +

+
+
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.FIELDS.QUERY' | translate}} {{queryIndex + 1}}

+
+
+ +
+
+ +
+
+
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.NAME' | translate}} + + {{query.get('name').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.DEFAULT-VALUE' | translate}} + + {{query.get('defaultValue').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+ +
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.FIELDS.CASE' | translate}} {{caseIndex + 1}}

+
+
+ +
+
+
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.LIKE-PATTERN' | translate}} + + {{case.get('likePattern').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.SEPARATOR' | translate}} + + {{case.get('separator').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.VALUE' | translate}} + + {{case.get('value').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.REFERENCE-TYPE' | translate}} + + + {{referenceType.code}} + + + {{case.get('referenceTypeId').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.SOURCE-KEY' | translate}} + + + {{sourceKey}} + + + {{case.get('referenceTypeSourceKey').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
+
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.FIELDS.OPTIONS' | translate}}

+
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.FIELDS.OPTION' | translate}} {{optionsIndex + 1}}

+
+
+
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.CODE' | translate}} + + {{option.get('code').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.VALUE' | translate}} + + {{option.get('value').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
+
+
+ {{formGroup.value | json}} +
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.scss b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.ts b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.ts new file mode 100644 index 000000000..6f3174d06 --- /dev/null +++ b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.component.ts @@ -0,0 +1,103 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormArray, UntypedFormGroup } from '@angular/forms'; +import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type'; +import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type'; +import { ReferenceType } from '@app/core/model/reference-type/reference-type'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { BaseComponent } from '@common/base/base.component'; +import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +import { QueryCaseConfigEditorModel, QueryConfigEditorModel, StaticOptionEditorModel } from './external-fetcher-source-editor.model'; +import { Guid } from '@common/types/guid'; + +@Component({ + selector: 'app-external-fetcher-source-component', + templateUrl: 'external-fetcher-source.component.html', + styleUrls: ['./external-fetcher-source.component.scss'] +}) +export class ExternalFetcherSourceComponent extends BaseComponent implements OnInit { + + @Input() formGroup: UntypedFormGroup = null; + @Input() validationErrorModel: ValidationErrorModel = null; + @Input() validationRootPath: string = null; + @Input() referenceTypeSourceIndex: number = null; + @Input() referenceTypes: ReferenceType[] = null; + @Input() sourceKeysMap: Map = new Map(); + + externalFetcherSourceType = ExternalFetcherSourceType; + externalFetcherApiHTTPMethodType = ExternalFetcherApiHTTPMethodType; + externalFetcherSourceTypeEnum = this.enumUtils.getEnumValues(ExternalFetcherSourceType); + externalFetcherApiHTTPMethodTypeEnum = this.enumUtils.getEnumValues(ExternalFetcherApiHTTPMethodType); + referenceTypeDependenciesMap: Map = new Map(); + + constructor( + public enumUtils: EnumUtils, + ) { super(); } + + ngOnInit() { + console.log(this.referenceTypeSourceIndex); + } + + // + // + // queries + // + // + addQuery(): void { + const formArray= this.formGroup.get('queries') as FormArray; + const query: QueryConfigEditorModel = new QueryConfigEditorModel(this.validationErrorModel); + formArray.push(query.buildForm({rootPath: this.validationRootPath + 'queries[' + formArray.length + '].'})); + } + + removeQuery(queryIndex: number): void { + const formArray = (this.formGroup.get('queries') as FormArray); + formArray.removeAt(queryIndex); + } + + // cases + + addCase(queryIndex: number): void { + const formArray = (this.formGroup.get('queries') as FormArray).at(queryIndex).get('cases') as FormArray; + const queryCase: QueryCaseConfigEditorModel = new QueryCaseConfigEditorModel(this.validationErrorModel); + formArray.push(queryCase.buildForm({rootPath: this.validationRootPath + 'queries[' + queryIndex + '].cases[' + formArray.length + '].'})); + + } + + removeCase(queryIndex: number, index: number): void { + const formArray = (this.formGroup.get('queries') as FormArray).at(queryIndex).get('cases') as FormArray; + formArray.removeAt(index); + } + + // Options + + addOption(code: string): void { + const optionsSize = (this.formGroup.get('options') as FormArray).length; + + if (optionsSize > 0) { + for (let i = 0; i < optionsSize; i++) { + if ((this.formGroup.get('options') as FormArray).at(i).get('code').getRawValue() == code) { + return; + } + } + } + + const option = new StaticOptionEditorModel(this.validationErrorModel); + (this.formGroup.get('options') as FormArray).push(option.buildForm()); + (this.formGroup.get('options') as FormArray).at(optionsSize).get('code').patchValue(code); + } + + removeOption(optionIndex: number): void { + const formArray = (this.formGroup.get('options') as FormArray); + formArray.removeAt(optionIndex); + } + + setReferenceTypeDependenciesMap(ids: Guid[], index: number){ + let mapValues :ReferenceType[] = []; + this.referenceTypes.forEach(x => { + if(ids.includes(x.id)){ + mapValues.push(x); + } + }) + this.referenceTypeDependenciesMap.set(index, mapValues); + } + +} diff --git a/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.module.ts b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.module.ts new file mode 100644 index 000000000..15c83f1b4 --- /dev/null +++ b/dmp-frontend/src/app/ui/external-fetcher/external-fetcher-source.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { FormattingModule } from '@app/core/formatting.module'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; +import { DescriptionRoutingModule } from '@app/ui/description/description.routing'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { ExternalFetcherSourceComponent } from './external-fetcher-source.component'; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + FormattingModule, + DescriptionRoutingModule, + AutoCompleteModule + ], + declarations: [ + ExternalFetcherSourceComponent + ], + exports: [ + ExternalFetcherSourceComponent + ] +}) +export class ExternalFetcherSourceModule { } diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts index 6dbf6d10c..819df99fc 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts +++ b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts @@ -53,6 +53,7 @@ export const ADMIN_ROUTES: RouteInfo[] = [ { path: '/description-template-type', title: 'SIDE-BAR.DESCRIPTION-TEMPLATE-TYPES', icon: 'stack' }, { path: '/references', title: 'SIDE-BAR.REFERENCES', icon: 'dataset_linked' }, { path: '/reference-type', title: 'SIDE-BAR.REFERENCE-TYPES', icon: 'add_link' }, + { path: '/prefilling-sources', title: 'SIDE-BAR.PREFILLING-SOURCES', icon: 'add_link' }, { path: '/tenants', title: 'SIDE-BAR.TENANTS', icon: 'tenancy' }, { path: '/users', title: 'SIDE-BAR.USERS', icon: 'people' }, { path: '/languages', title: 'SIDE-BAR.LANGUAGES', icon: 'language' }, diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index babe55052..b0c119da6 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -256,7 +256,10 @@ "EDIT-LANGUAGE": "Edit", "NOTIFICATION-TEMPLATES": "Notification Templates", "INAPP-NOTIFICATIONS":"Notifications", - "NOTIFICATIONS": "Notifications" + "NOTIFICATIONS": "Notifications", + "PREFILLING-SOURCES": "Prefilling Sources", + "NEW-PREFILLING-SOURCE": "New", + "EDIT-PREFILLING-SOURCE": "Edit" }, "COOKIE": { "MESSAGE": "This website uses cookies to enhance the user experience.", @@ -359,7 +362,8 @@ "LANGUAGES": "Languages", "MAINTENANCE": "Maintenance", "NOTIFICATION-TEMPLATES": "Notification Templates", - "NOTIFICATIONS":"Notifications" + "NOTIFICATIONS":"Notifications", + "PREFILLING-SOURCES":"Prefilling Sources" }, "DESCRIPTION-TEMPLATE-EDITOR": { "TITLE": { @@ -1196,6 +1200,33 @@ "SUCCESSFUL-DELETE": "Successful Delete", "UNSUCCESSFUL-DELETE": "This item could not be deleted." }, + "PREFILLING-SOURCE-LISTING": { + "TITLE": "Prefilling Sources", + "CREATE": "Create Prefilling Source", + "FIELDS": { + "LABEL": "Label", + "UPDATED-AT": "Updated", + "CREATED-AT": "Created", + "IS-ACTIVE": "Is Active" + }, + "FILTER": { + "TITLE": "Filters", + "IS-ACTIVE": "Is Active", + "CANCEL": "Cancel", + "APPLY-FILTERS": "Apply filters" + }, + "CONFIRM-DELETE-DIALOG": { + "MESSAGE": "Would you like to delete this Preffiling Source?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" + }, + "ACTIONS": { + "DELETE": "Delete", + "EDIT": "Edit" + }, + "SUCCESSFUL-DELETE": "Successful Delete", + "UNSUCCESSFUL-DELETE": "This item could not be deleted." + }, "REFERENCE-LISTING": { "TITLE": "References", "CREATE": "Create Reference", @@ -1515,7 +1546,8 @@ "ADD-QUERY": "Add Query", "REMOVE-QUERY": "Remove Query", "ADD-CASE": "Add Case", - "REMOVE-CASE": "Remove Case" + "REMOVE-CASE": "Remove Case", + "SUBMIT-FIELDS": "Submit" }, "CONFIRM-DELETE-DIALOG": { "MESSAGE": "Would you like to delete this Reference type?", @@ -1556,6 +1588,32 @@ "CANCEL-BUTTON": "No" } }, + "PREFILLING-SOURCE-EDITOR": { + "NEW": "New Prefilling Source", + "FIELDS": { + "LABEL": "Label", + "FIELD": "Field", + "CODE": "Code", + "TARGET": "Target", + "SYSTEM-TARGET": "System Target", + "SEMANTIC-TARGET": "Semantic Target", + "TRIM-REGEX": "Trim Regex", + "FIXED-VALUE": "Fixed Value" + }, + "ACTIONS": { + "SAVE": "Save", + "CANCEL": "Cancel", + "DELETE": "Delete", + "ADD-FIELD": "Add Field", + "REMOVE-FIELD": "Remove Field", + "SUBMIT-FIELDS": "Submit" + }, + "CONFIRM-DELETE-DIALOG": { + "MESSAGE": "Would you like to delete this Prefilling Source?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" + } + }, "REFERENCE-EDITOR": { "NEW": "New Reference", "FIELDS": {