diff --git a/src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.ts b/src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.ts index 35f9bee04f..cdd259447a 100644 --- a/src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.ts +++ b/src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.ts @@ -28,7 +28,7 @@ import { ChangeAnalyzer } from '../../../core/data/change-analyzer'; import { Operation } from 'fast-json-patch'; import { RestRequestMethod } from 'src/app/core/data/rest-request-method'; import { CreateData, CreateDataImpl } from '../../../core/data/base/create-data'; -import { ldnServiceConstrain } from '../ldn-services-model/ldn-service.constrain.model'; +import { LdnServiceConstrain } from '../ldn-services-model/ldn-service.constrain.model'; import { getFirstCompletedRemoteData } from 'src/app/core/shared/operators'; import { hasValue } from 'src/app/shared/empty.util'; @@ -92,7 +92,7 @@ export class LdnServicesService extends IdentifiableDataService impl return this.deleteData.deleteByHref(href, copyVirtualMetadata); } - public invoke(serviceName: string, serviceId: string, parameters: ldnServiceConstrain[], files: File[]): Observable> { + public invoke(serviceName: string, serviceId: string, parameters: LdnServiceConstrain[], files: File[]): Observable> { const requestId = this.requestService.generateRequestId(); this.getBrowseEndpoint().pipe( take(1), @@ -115,7 +115,7 @@ export class LdnServicesService extends IdentifiableDataService impl ); } - private getInvocationFormData(constrain: ldnServiceConstrain[], files: File[]): FormData { + private getInvocationFormData(constrain: LdnServiceConstrain[], files: File[]): FormData { const form: FormData = new FormData(); form.set('properties', JSON.stringify(constrain)); files.forEach((file: File) => { diff --git a/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service.constrain.model.ts b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service.constrain.model.ts index 69a9baf273..500cefbd52 100644 --- a/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service.constrain.model.ts +++ b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service.constrain.model.ts @@ -1,3 +1,3 @@ -export class ldnServiceConstrain { +export class LdnServiceConstrain { void: any; } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 88da00f02c..d0f2dbbbaf 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -190,7 +190,11 @@ import { SuggestionSource } from './suggestion-notifications/reciter-suggestions import { LdnServicesService } from '../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service'; import { LdnService } from '../admin/admin-ldn-services/ldn-services-model/ldn-services.model'; import { LdnItemfiltersService } from '../admin/admin-ldn-services/ldn-services-data/ldn-itemfilters-data.service'; -import { Itemfilter } from "../admin/admin-ldn-services/ldn-services-model/ldn-service-itemfilters"; +import { Itemfilter } from '../admin/admin-ldn-services/ldn-services-model/ldn-service-itemfilters'; +import { + CoarNotifyConfigDataService +} from '../submission/sections/section-coar-notify/coar-notify-config-data.service'; +import { SubmissionCoarNotifyConfig } from '../submission/sections/section-coar-notify/submission-coar-notify.config'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -315,7 +319,8 @@ const PROVIDERS = [ OrcidHistoryDataService, SupervisionOrderDataService, LdnServicesService, - LdnItemfiltersService + LdnItemfiltersService, + CoarNotifyConfigDataService ]; /** @@ -398,7 +403,8 @@ export const models = SuggestionTarget, SuggestionSource, LdnService, - Itemfilter + Itemfilter, + SubmissionCoarNotifyConfig ]; diff --git a/src/app/submission/form/submission-form.component.html b/src/app/submission/form/submission-form.component.html index fa04a6793c..c79364e2af 100644 --- a/src/app/submission/form/submission-form.component.html +++ b/src/app/submission/form/submission-form.component.html @@ -38,9 +38,14 @@ [sectionData]="object"> + + diff --git a/src/app/submission/sections/container/section-container.component.html b/src/app/submission/sections/container/section-container.component.html index e6ae9d1b9c..f39ba72ffa 100644 --- a/src/app/submission/sections/container/section-container.component.html +++ b/src/app/submission/sections/container/section-container.component.html @@ -42,10 +42,10 @@ -
+
aaaaaaaaa
-
\ No newline at end of file + diff --git a/src/app/submission/sections/ldn-service/ldn-service.component.scss b/src/app/submission/sections/ldn-service/ldn-service.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/submission/sections/ldn-service/ldn-service.component.spec.ts b/src/app/submission/sections/ldn-service/ldn-service.component.spec.ts deleted file mode 100644 index ec36f1f010..0000000000 --- a/src/app/submission/sections/ldn-service/ldn-service.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { LdnServiceComponent } from './ldn-service.component'; - -describe('LdnServiceComponent', () => { - let component: LdnServiceComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ LdnServiceComponent ] - }) - .compileComponents(); - - fixture = TestBed.createComponent(LdnServiceComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/submission/sections/ldn-service/ldn-service.component.ts b/src/app/submission/sections/ldn-service/ldn-service.component.ts deleted file mode 100644 index 6690395e72..0000000000 --- a/src/app/submission/sections/ldn-service/ldn-service.component.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { ChangeDetectorRef, Component, Inject, ViewChild } from '@angular/core'; -import { DynamicFormControlEvent } from '@ng-dynamic-forms/core'; -import { Observable, Subscription } from 'rxjs'; -import { SectionModelComponent } from '../models/section.model'; -import { renderSectionFor } from '../sections-decorator'; -import { SectionsType } from '../sections-type'; -import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; -import { FormComponent } from '../../../shared/form/form.component'; -import { CollectionDataService } from '../../../core/data/collection-data.service'; -import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; -import { SectionFormOperationsService } from '../form/section-form-operations.service'; -import { FormService } from '../../../shared/form/form.service'; -import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; -import { SectionsService } from '../sections.service'; -import { SubmissionService } from '../../submission.service'; -import { TranslateService } from '@ngx-translate/core'; -import { SectionDataObject } from '../models/section-data.model'; -import { - WorkspaceitemSectionNotifyServiceRequestItemDissemination -} from '../../../core/submission/models/workspaceitem-section-form-notify-service.model'; -import { hasValue } from '../../../shared/empty.util'; -/** - * This component represents a section that contains the submission ldn-service form. - */ -@Component({ - selector: 'ds-ldn-service', - templateUrl: './ldn-service.component.html', - styleUrls: ['./ldn-service.component.scss'] -}) -@renderSectionFor(SectionsType.LdnService) -export class LdnServiceComponent extends SectionModelComponent { - - /** - * The [[JsonPatchOperationPathCombiner]] object - * @type {JsonPatchOperationPathCombiner} - */ - protected pathCombiner: JsonPatchOperationPathCombiner; - - /** - * Array to track all subscriptions and unsubscribe them onDestroy - * @type {Array} - */ - protected subs: Subscription[] = []; - - /** - * A boolean representing if div should start collapsed - */ - public isCollapsed = false; - - /** - * The FormComponent reference - */ - @ViewChild('formRef') private formRef: FormComponent; - - /** - * Initialize instance variables - * - * @param {ChangeDetectorRef} changeDetectorRef - * @param {CollectionDataService} collectionDataService - * @param {FormBuilderService} formBuilderService - * @param {SectionFormOperationsService} formOperationsService - * @param {FormService} formService - * @param {JsonPatchOperationsBuilder} operationsBuilder - * @param {SectionsService} sectionService - * @param {SubmissionService} submissionService - * @param {TranslateService} translateService - * @param {string} injectedCollectionId - * @param {SectionDataObject} injectedSectionData - * @param {string} injectedSubmissionId - */ - constructor(protected changeDetectorRef: ChangeDetectorRef, - protected collectionDataService: CollectionDataService, - protected formBuilderService: FormBuilderService, - protected formOperationsService: SectionFormOperationsService, - protected formService: FormService, - protected operationsBuilder: JsonPatchOperationsBuilder, - protected sectionService: SectionsService, - protected submissionService: SubmissionService, - protected translateService: TranslateService, - @Inject('collectionIdProvider') public injectedCollectionId: string, - @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, - @Inject('submissionIdProvider') public injectedSubmissionId: string) { - super(injectedCollectionId, injectedSectionData, injectedSubmissionId); - } - - /** - * Initialize all instance variables - */ - onSectionInit() { - this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id); - this.subs.push( - this.sectionService.getSectionData(this.submissionId, this.sectionData.id, this.sectionData.sectionType) - .subscribe((ldnServicesSection: WorkspaceitemSectionNotifyServiceRequestItemDissemination) => { - console.log(ldnServicesSection); - }) - ); - } - - - - /** - * Method called when a form dfChange event is fired. - * Dispatch form operations based on changes. - */ - onChange(event: DynamicFormControlEvent) { - const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event); - const value = this.formOperationsService.getFieldValueFromChangeEvent(event); - if (value) { - this.operationsBuilder.add(this.pathCombiner.getPath(path), value.value.toString(), false, true); - this.sectionService.dispatchRemoveSectionErrors(this.submissionId, this.sectionData.id); - } else { - this.operationsBuilder.remove(this.pathCombiner.getPath(path)); - } - } - - /** - * Unsubscribe from all subscriptions - */ - onSectionDestroy() { - this.subs - .filter((subscription) => hasValue(subscription)) - .forEach((subscription) => subscription.unsubscribe()); - } - - protected getSectionStatus(): Observable { - return undefined; - } - -} diff --git a/src/app/submission/sections/section-coar-notify/coar-notify-config-data.service.ts b/src/app/submission/sections/section-coar-notify/coar-notify-config-data.service.ts new file mode 100644 index 0000000000..7b8d309667 --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/coar-notify-config-data.service.ts @@ -0,0 +1,122 @@ +import { Injectable } from '@angular/core'; +import { dataService } from '../../../core/data/base/data-service.decorator'; +import { IdentifiableDataService } from '../../../core/data/base/identifiable-data.service'; +import { FindAllData, FindAllDataImpl } from '../../../core/data/base/find-all-data'; +import { DeleteData, DeleteDataImpl } from '../../../core/data/base/delete-data'; +import { RequestService } from '../../../core/data/request.service'; +import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../../core/cache/object-cache.service'; +import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; +import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; +import { Observable } from 'rxjs'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; +import { NoContent } from '../../../core/shared/NoContent.model'; +import { map, take } from 'rxjs/operators'; +import { URLCombiner } from '../../../core/url-combiner/url-combiner'; +import { MultipartPostRequest } from '../../../core/data/request.models'; +import { RestRequest } from '../../../core/data/rest-request.model'; +import { SUBMISSION_COAR_NOTIFY_CONFIG } from './section-coar-notify-service.resource-type'; +import { SubmissionCoarNotifyConfig } from './submission-coar-notify.config'; +import { CreateData, CreateDataImpl } from '../../../core/data/base/create-data'; +import { PatchData, PatchDataImpl } from '../../../core/data/base/patch-data'; +import { ChangeAnalyzer } from '../../../core/data/change-analyzer'; +import { Operation } from 'fast-json-patch'; +import { RestRequestMethod } from '../../../core/data/rest-request-method'; +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { hasValue } from '../../../shared/empty.util'; + + +/** + * A service responsible for fetching/sending data from/to the REST API on the CoarNotifyConfig endpoint + */ +@Injectable() +@dataService(SUBMISSION_COAR_NOTIFY_CONFIG) +export class CoarNotifyConfigDataService extends IdentifiableDataService implements FindAllData, DeleteData, PatchData, CreateData { + createData: CreateDataImpl; + private findAllData: FindAllDataImpl; + private deleteData: DeleteDataImpl; + private patchData: PatchDataImpl; + private comparator: ChangeAnalyzer; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + ) { + super('submissioncoarnotifyconfigs', requestService, rdbService, objectCache, halService); + + this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint); + this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.comparator, this.responseMsToLive, this.constructIdEndpoint); + this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive); + } + + + create(object: SubmissionCoarNotifyConfig): Observable> { + return this.createData.create(object); + } + + patch(object: SubmissionCoarNotifyConfig, operations: Operation[]): Observable> { + return this.patchData.patch(object, operations); + } + + update(object: SubmissionCoarNotifyConfig): Observable> { + return this.patchData.update(object); + } + + commitUpdates(method?: RestRequestMethod): void { + return this.patchData.commitUpdates(method); + } + + createPatchFromCache(object: SubmissionCoarNotifyConfig): Observable { + return this.patchData.createPatchFromCache(object); + } + + findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig[]): Observable>> { + return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } + + public delete(objectId: string, copyVirtualMetadata?: string[]): Observable> { + return this.deleteData.delete(objectId, copyVirtualMetadata); + } + + public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable> { + return this.deleteData.deleteByHref(href, copyVirtualMetadata); + } + + public invoke(serviceName: string, serviceId: string, files: File[]): Observable> { + const requestId = this.requestService.generateRequestId(); + this.getBrowseEndpoint().pipe( + take(1), + map((endpoint: string) => new URLCombiner(endpoint, serviceName, 'submissioncoarnotifyconfigmodel', serviceId).toString()), + map((endpoint: string) => { + const body = this.getInvocationFormData(files); + return new MultipartPostRequest(requestId, endpoint, body); + }) + ).subscribe((request: RestRequest) => this.requestService.send(request)); + + return this.rdbService.buildFromRequestUUID(requestId); + } + + public SubmissionCoarNotifyConfigModelWithNameExistsAndCanExecute(scriptName: string): Observable { + return this.findById(scriptName).pipe( + getFirstCompletedRemoteData(), + map((rd: RemoteData) => { + return hasValue(rd.payload); + }), + ); + } + + private getInvocationFormData(files: File[]): FormData { + const form: FormData = new FormData(); + files.forEach((file: File) => { + form.append('file', file); + }); + return form; + } +} diff --git a/src/app/submission/sections/section-coar-notify/section-coar-notify-model.ts b/src/app/submission/sections/section-coar-notify/section-coar-notify-model.ts new file mode 100644 index 0000000000..c8904fdca3 --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/section-coar-notify-model.ts @@ -0,0 +1,74 @@ +export const REQUEST_REVIEW_DROPDOWN = { + element: { + container: 'custom-control custom-select pl-1', + control: 'custom-select', + label: 'custom-control-label pt-1' + } +}; + +export const REQUEST_ENDORSEMENT_DROPDOWN = { + element: { + container: 'custom-control custom-select pl-1', + control: 'custom-select', + label: 'custom-control-label pt-1' + } +}; + +export const REQUEST_INGEST_DROPDOWN = { + element: { + container: 'custom-control custom-select pl-1', + control: 'custom-select', + label: 'custom-control-label pt-1' + } +}; + +export const SECTION_COAR_FORM_LAYOUT = { + requestReview: REQUEST_REVIEW_DROPDOWN, + requestEndorsement: REQUEST_ENDORSEMENT_DROPDOWN, + requestIngest: REQUEST_INGEST_DROPDOWN +}; + +export const SECTION_COAR_FORM_MODEL = [ + { + id: 'requestReview', + label: 'submission.sections.license.request-review-label', + required: false, + value: '', + validators: { + required: null + }, + errorMessages: { + required: 'submission.sections.license.required', + notgranted: 'submission.sections.license.notgranted' + }, + type: 'SELECT', + }, + { + id: 'requestEndorsement', + label: 'submission.sections.license.request-endorsement-label', + required: false, + value: '', + validators: { + required: null + }, + errorMessages: { + required: 'submission.sections.license.required', + notgranted: 'submission.sections.license.notgranted' + }, + type: 'SELECT', + }, + { + id: 'requestIngest', + label: 'submission.sections.license.request-ingest-label', + required: false, + value: '', + validators: { + required: null + }, + errorMessages: { + required: 'submission.sections.license.required', + notgranted: 'submission.sections.license.notgranted' + }, + type: 'SELECT', + } +]; diff --git a/src/app/submission/sections/section-coar-notify/section-coar-notify-service.resource-type.ts b/src/app/submission/sections/section-coar-notify/section-coar-notify-service.resource-type.ts new file mode 100644 index 0000000000..53e41783ce --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/section-coar-notify-service.resource-type.ts @@ -0,0 +1,13 @@ +/** + * The resource type for Ldn-Services + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +import { ResourceType } from '../../../core/shared/resource-type'; + + +export const SUBMISSION_COAR_NOTIFY_CONFIG = new ResourceType('submissioncoarnotifyconfig'); + +export const COAR_NOTIFY_WORKSPACEITEM = new ResourceType('workspaceitem'); + diff --git a/src/app/submission/sections/section-coar-notify/section-coar-notify-workspaceitems-data-service.ts b/src/app/submission/sections/section-coar-notify/section-coar-notify-workspaceitems-data-service.ts new file mode 100644 index 0000000000..dd287b096a --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/section-coar-notify-workspaceitems-data-service.ts @@ -0,0 +1,117 @@ +import { Injectable } from '@angular/core'; +import { dataService } from '../../../core/data/base/data-service.decorator'; +import { IdentifiableDataService } from '../../../core/data/base/identifiable-data.service'; +import { FindAllData, FindAllDataImpl } from '../../../core/data/base/find-all-data'; +import { DeleteData, DeleteDataImpl } from '../../../core/data/base/delete-data'; +import { RequestService } from '../../../core/data/request.service'; +import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../../../core/cache/object-cache.service'; +import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; +import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; +import { Observable } from 'rxjs'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; +import { NoContent } from '../../../core/shared/NoContent.model'; +import { map, take } from 'rxjs/operators'; +import { URLCombiner } from '../../../core/url-combiner/url-combiner'; +import { MultipartPostRequest } from '../../../core/data/request.models'; +import { RestRequest } from '../../../core/data/rest-request.model'; +import { COAR_NOTIFY_WORKSPACEITEM } from './section-coar-notify-service.resource-type'; +import { LdnService } from '../../../admin/admin-ldn-services/ldn-services-model/ldn-services.model'; +import { SubmissionCoarNotifyConfig } from './submission-coar-notify.config'; + + +/** + * A service responsible for fetching/sending data from/to the REST API on the ldnservices endpoint + */ +@Injectable() +@dataService(COAR_NOTIFY_WORKSPACEITEM) +export class SectionCoarNotifyWorkspaceitemsDataService extends IdentifiableDataService implements FindAllData, DeleteData, PatchData, CreateData { + createData: CreateDataImpl; + private findAllData: FindAllDataImpl; + private deleteData: DeleteDataImpl; + private patchData: PatchDataImpl; + private comparator: ChangeAnalyzer; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + ) { + super('ldnservices', requestService, rdbService, objectCache, halService); + + this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive); + this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint); + this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.comparator, this.responseMsToLive, this.constructIdEndpoint); + this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive); + } + + + create(object: LdnService): Observable> { + return this.createData.create(object); + } + + patch(object: LdnService, operations: Operation[]): Observable> { + return this.patchData.patch(object, operations); + } + + update(object: LdnService): Observable> { + return this.patchData.update(object); + } + + commitUpdates(method?: RestRequestMethod): void { + return this.patchData.commitUpdates(method); + } + + createPatchFromCache(object: LdnService): Observable { + return this.patchData.createPatchFromCache(object); + } + + findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig[]): Observable>> { + return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + } + + public delete(objectId: string, copyVirtualMetadata?: string[]): Observable> { + return this.deleteData.delete(objectId, copyVirtualMetadata); + } + + public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable> { + return this.deleteData.deleteByHref(href, copyVirtualMetadata); + } + + public invoke(serviceName: string, serviceId: string, parameters: ldnServiceConstrain[], files: File[]): Observable> { + const requestId = this.requestService.generateRequestId(); + this.getBrowseEndpoint().pipe( + take(1), + map((endpoint: string) => new URLCombiner(endpoint, serviceName, 'processes', serviceId).toString()), + map((endpoint: string) => { + const body = this.getInvocationFormData(parameters, files); + return new MultipartPostRequest(requestId, endpoint, body); + }) + ).subscribe((request: RestRequest) => this.requestService.send(request)); + + return this.rdbService.buildFromRequestUUID(requestId); + } + + public ldnServiceWithNameExistsAndCanExecute(scriptName: string): Observable { + return this.findById(scriptName).pipe( + getFirstCompletedRemoteData(), + map((rd: RemoteData) => { + return hasValue(rd.payload); + }), + ); + } + + private getInvocationFormData(constrain: ldnServiceConstrain[], files: File[]): FormData { + const form: FormData = new FormData(); + form.set('properties', JSON.stringify(constrain)); + files.forEach((file: File) => { + form.append('file', file); + }); + return form; + } +} diff --git a/src/app/submission/sections/section-coar-notify/section-coar-notify.component.html b/src/app/submission/sections/section-coar-notify/section-coar-notify.component.html new file mode 100644 index 0000000000..4d0d92c714 --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/section-coar-notify.component.html @@ -0,0 +1,31 @@ +
+ + + +
diff --git a/src/app/submission/sections/ldn-service/ldn-service.component.html b/src/app/submission/sections/section-coar-notify/section-coar-notify.component.scss similarity index 100% rename from src/app/submission/sections/ldn-service/ldn-service.component.html rename to src/app/submission/sections/section-coar-notify/section-coar-notify.component.scss diff --git a/src/app/submission/sections/section-coar-notify/section-coar-notify.component.spec.ts b/src/app/submission/sections/section-coar-notify/section-coar-notify.component.spec.ts new file mode 100644 index 0000000000..c83479284a --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/section-coar-notify.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SubmissionSectionCoarNotifyComponent } from './section-coar-notify.component'; + +describe('LdnServiceComponent', () => { + let component: SubmissionSectionCoarNotifyComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SubmissionSectionCoarNotifyComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SubmissionSectionCoarNotifyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/submission/sections/section-coar-notify/section-coar-notify.component.ts b/src/app/submission/sections/section-coar-notify/section-coar-notify.component.ts new file mode 100644 index 0000000000..992eae0b52 --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/section-coar-notify.component.ts @@ -0,0 +1,308 @@ +import { ChangeDetectorRef, Component, Inject, ViewChild } from '@angular/core'; +import { DynamicFormControlEvent, DynamicFormControlModel, DynamicFormLayout } from '@ng-dynamic-forms/core'; +import { Observable, Subscription } from 'rxjs'; +import { SectionModelComponent } from '../models/section.model'; +import { renderSectionFor } from '../sections-decorator'; +import { SectionsType } from '../sections-type'; +import { JsonPatchOperationPathCombiner } from '../../../core/json-patch/builder/json-patch-operation-path-combiner'; +import { FormComponent } from '../../../shared/form/form.component'; +import { CollectionDataService } from '../../../core/data/collection-data.service'; +import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; +import { SectionFormOperationsService } from '../form/section-form-operations.service'; +import { FormService } from '../../../shared/form/form.service'; +import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; +import { SectionsService } from '../sections.service'; +import { SubmissionService } from '../../submission.service'; +import { TranslateService } from '@ngx-translate/core'; +import { SectionDataObject } from '../models/section-data.model'; + +import { hasValue, isNotEmpty } from '../../../shared/empty.util'; + +import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { LdnServicesService } from '../../../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service'; +import { isLoading } from '../../../core/data/request-entry-state.model'; +import { LdnService } from '../../../admin/admin-ldn-services/ldn-services-model/ldn-services.model'; +import { SECTION_COAR_FORM_LAYOUT, SECTION_COAR_FORM_MODEL } from './section-coar-notify-model'; +import { + CoarNotifyConfigDataService +} from './coar-notify-config-data.service'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; +import { SubmissionCoarNotifyConfig } from './submission-coar-notify.config'; +import { FormFieldPreviousValueObject } from '../../../shared/form/builder/models/form-field-previous-value-object'; +import { UntypedFormGroup } from '@angular/forms'; +import { AlertType } from '../../../shared/alert/aletr-type'; + +export interface CoarNotifyDropdownSelector { + ldnService: LdnService; +} + +/** + * This component represents a section that contains the submission section-coar-notify form. + */ +@Component({ + selector: 'ds-submission-section-coar-notify', + templateUrl: './section-coar-notify.component.html', + styleUrls: ['./section-coar-notify.component.scss'] +}) +@renderSectionFor(SectionsType.CoarNotify) + +export class SubmissionSectionCoarNotifyComponent extends SectionModelComponent { + + requestReview: LdnService; + requestEndorsement: LdnService; + requestIngest: LdnService; + + coarNotifyConfigRD$: Observable>>; + + ldnServicesRD$: Observable>>; + + selectedServiceValues: any[] = []; + + /** + * The AlertType enumeration + * @type {AlertType} + */ + public AlertTypeEnum = AlertType; + /** + * The form model + * @type {DynamicFormControlModel[]} + */ + public formModel: DynamicFormControlModel[]; + /** + * The form id + * @type {string} + */ + public formId: string; + /** + * The [[DynamicFormLayout]] object + * @type {DynamicFormLayout} + */ + public formLayout: DynamicFormLayout = SECTION_COAR_FORM_LAYOUT; + /** + * A FormGroup that combines all inputs + */ + formGroup: UntypedFormGroup; + /** + * A boolean representing if div should start collapsed + */ + public isCollapsed = false; + protected readonly AlertType = AlertType; + /** + * The [[JsonPatchOperationPathCombiner]] object + * @type {JsonPatchOperationPathCombiner} + */ + protected pathCombiner: JsonPatchOperationPathCombiner; + /** + * A map representing all field on their way to be removed + * @type {Map} + */ + protected fieldsOnTheirWayToBeRemoved: Map = new Map(); + /** + * The [FormFieldPreviousValueObject] object + * @type {FormFieldPreviousValueObject} + */ + protected previousValue: FormFieldPreviousValueObject = new FormFieldPreviousValueObject(); + /** + * Array to track all subscriptions and unsubscribe them onDestroy + * @type {Array} + */ + protected subs: Subscription[] = []; + protected readonly isLoading = isLoading; + /** + * The FormComponent reference + */ + @ViewChild('formRef') private formRef: FormComponent; + + /** + * Initialize instance variables + * + * @param {ChangeDetectorRef} changeDetectorRef + * @param ldnServicesService + * @param {CollectionDataService} collectionDataService + * @param {FormBuilderService} formBuilderService + * @param {SectionFormOperationsService} formOperationsService + * @param {FormService} formService + * @param {JsonPatchOperationsBuilder} operationsBuilder + * @param {SectionsService} sectionService + * @param {SubmissionService} submissionService + * @param {TranslateService} translateService + * @param {CoarNotifyConfigDataService} coarNotifyConfigDataService + * @param {string} injectedCollectionId + * @param {SectionDataObject} injectedSectionData + * @param {string} injectedSubmissionId + */ + constructor(protected changeDetectorRef: ChangeDetectorRef, + protected ldnServicesService: LdnServicesService, + protected collectionDataService: CollectionDataService, + protected formBuilderService: FormBuilderService, + protected formOperationsService: SectionFormOperationsService, + protected formService: FormService, + protected operationsBuilder: JsonPatchOperationsBuilder, + protected sectionService: SectionsService, + protected submissionService: SubmissionService, + protected translateService: TranslateService, + protected coarNotifyConfigDataService: CoarNotifyConfigDataService, + @Inject('collectionIdProvider') public injectedCollectionId: string, + @Inject('sectionDataProvider') public injectedSectionData: SectionDataObject, + @Inject('submissionIdProvider') public injectedSubmissionId: string) { + super(injectedCollectionId, injectedSectionData, injectedSubmissionId); + } + + /** + * Initialize all instance variables + */ + onSectionInit() { + this.formModel = this.formBuilderService.fromJSON(SECTION_COAR_FORM_MODEL); + this.setCoarNotifyConfig(); + this.fetchLdnServices(); + this.coarNotifyConfigRD$.subscribe(data => { + console.log(data); + }); + this.ldnServicesRD$.subscribe(data => { + console.log(data); + }); + this.pathCombiner = new JsonPatchOperationPathCombiner('sections', this.sectionData.id); + } + + /** + * Method called when section is initialized + * Retriev available NotifyConfigs + */ + setCoarNotifyConfig() { + this.coarNotifyConfigRD$ = this.coarNotifyConfigDataService.findAll().pipe( + getFirstCompletedRemoteData()); + } + + /** + * Handle the customEvent (ex. drag-drop move event). + * The customEvent is stored inside event.$event + * @param event + */ + onCustomEvent(event: DynamicFormControlEvent) { + this.formOperationsService.dispatchOperationsFromEvent( + this.pathCombiner, + event, + this.previousValue, + null); + } + + /** + * Method called when a form dfChange event is fired. + * Dispatch form operations based on changes. + */ + onChange(event: DynamicFormControlEvent) { + const path = this.formOperationsService.getFieldPathSegmentedFromChangeEvent(event); + const value = this.formOperationsService.getFieldValueFromChangeEvent(event); + if (value) { + this.operationsBuilder.add(this.pathCombiner.getPath(path), value.value.toString(), false, true); + this.sectionService.dispatchRemoveSectionErrors(this.submissionId, this.sectionData.id); + } else { + this.operationsBuilder.remove(this.pathCombiner.getPath(path)); + } + } + + /** + * Method called when a form remove event is fired. + * Dispatch form operations based on changes. + * + * @param event + * the [[DynamicFormControlEvent]] emitted + */ + onRemove(event: DynamicFormControlEvent): void { + const fieldId = this.formBuilderService.getId(event.model); + const fieldIndex = this.formOperationsService.getArrayIndexFromEvent(event); + + // Keep track that this field will be removed + if (this.fieldsOnTheirWayToBeRemoved.has(fieldId)) { + const indexes = this.fieldsOnTheirWayToBeRemoved.get(fieldId); + indexes.push(fieldIndex); + this.fieldsOnTheirWayToBeRemoved.set(fieldId, indexes); + } else { + this.fieldsOnTheirWayToBeRemoved.set(fieldId, [fieldIndex]); + } + + this.formOperationsService.dispatchOperationsFromEvent( + this.pathCombiner, + event, + this.previousValue, + this.hasStoredValue(fieldId, fieldIndex)); + + } + + /** + * Check if the specified form field has already a value stored + * + * @param fieldId + * the section data retrieved from the serverù + * @param index + * the section data retrieved from the server + */ + hasStoredValue(fieldId, index): boolean { + if (isNotEmpty(this.sectionData.data)) { + return this.sectionData.data.hasOwnProperty(fieldId) && + isNotEmpty(this.sectionData.data[fieldId][index]) && + !this.isFieldToRemove(fieldId, index); + } else { + return false; + } + } + + /** + * Check if the specified field is on the way to be removed + * + * @param fieldId + * the section data retrieved from the serverù + * @param index + * the section data retrieved from the server + */ + isFieldToRemove(fieldId, index) { + return this.fieldsOnTheirWayToBeRemoved.has(fieldId) && this.fieldsOnTheirWayToBeRemoved.get(fieldId).includes(index); + } + + /** + * Method called when a form dfFocus event is fired. + * Initialize [FormFieldPreviousValueObject] instance. + * + * @param event + * the [[DynamicFormControlEvent]] emitted + */ + onFocus(event: DynamicFormControlEvent): void { + const value = this.formOperationsService.getFieldValueFromChangeEvent(event); + const path = this.formBuilderService.getPath(event.model); + if (this.formBuilderService.hasMappedGroupValue(event.model)) { + this.previousValue.path = path; + this.previousValue.value = this.formOperationsService.getQualdropValueMap(event); + } else if (isNotEmpty(value) && ((typeof value === 'object' && isNotEmpty(value.value)) || (typeof value === 'string'))) { + this.previousValue.path = path; + this.previousValue.value = value; + } + } + + /** + * Unsubscribe from all subscriptions + */ + onSectionDestroy() { + this.subs + .filter((subscription) => hasValue(subscription)) + .forEach((subscription) => subscription.unsubscribe()); + } + + /** + * Method called when section is initialized + * Retriev available NotifyConfigs + */ + fetchLdnServices() { + this.ldnServicesRD$ = this.ldnServicesService.findAll().pipe( + getFirstCompletedRemoteData() + ); + } + + protected getSectionStatus(): Observable { + return undefined; + } + + hasInboundPattern(service: any, patternType: string): boolean { + return service.notifyServiceInboundPatterns.some(pattern => pattern.pattern === patternType); + } +} diff --git a/src/app/submission/sections/section-coar-notify/submission-coar-notify-workspaceitem.model.ts b/src/app/submission/sections/section-coar-notify/submission-coar-notify-workspaceitem.model.ts new file mode 100644 index 0000000000..85374502bc --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/submission-coar-notify-workspaceitem.model.ts @@ -0,0 +1,35 @@ +import { CacheableObject } from '../../../core/cache/cacheable-object.model'; +import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize'; + +import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { COAR_NOTIFY_WORKSPACEITEM } from "./section-coar-notify-service.resource-type"; + + +/** An CoarNotify and its properties. */ +@typedObject +@inheritSerialization(CacheableObject) +export class SubmissionCoarNotifyWorkspaceitemModel extends CacheableObject { + static type = COAR_NOTIFY_WORKSPACEITEM; + + @excludeFromEquals + @autoserialize + endorsement?: number[]; + + @deserializeAs('id') + review?: number[]; + + @autoserialize + ingest?: number[]; + + @deserialize + _links: { + self: { + href: string; + }; + }; + + get self(): string { + return this._links.self.href; + } +} diff --git a/src/app/submission/sections/section-coar-notify/submission-coar-notify.config.ts b/src/app/submission/sections/section-coar-notify/submission-coar-notify.config.ts new file mode 100644 index 0000000000..04973f80c8 --- /dev/null +++ b/src/app/submission/sections/section-coar-notify/submission-coar-notify.config.ts @@ -0,0 +1,39 @@ +import { ResourceType } from '../../../core/shared/resource-type'; +import { CacheableObject } from '../../../core/cache/cacheable-object.model'; +import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize'; + +import { excludeFromEquals } from '../../../core/utilities/equals.decorators'; +import { typedObject } from '../../../core/cache/builders/build-decorators'; +import { SUBMISSION_COAR_NOTIFY_CONFIG } from './section-coar-notify-service.resource-type'; + + +/** A SubmissionCoarNotifyConfig and its properties. */ +@typedObject +@inheritSerialization(CacheableObject) +export class SubmissionCoarNotifyConfig extends CacheableObject { + static type = SUBMISSION_COAR_NOTIFY_CONFIG; + + @excludeFromEquals + @autoserialize + type: ResourceType; + + @autoserialize + id: string; + + @deserializeAs('id') + uuid: string; + + @autoserialize + patterns: string[]; + + @deserialize + _links: { + self: { + href: string; + }; + }; + + get self(): string { + return this._links.self.href; + } +} diff --git a/src/app/submission/sections/sections-type.ts b/src/app/submission/sections/sections-type.ts index 40f6f85e0e..5f71d1731d 100644 --- a/src/app/submission/sections/sections-type.ts +++ b/src/app/submission/sections/sections-type.ts @@ -9,5 +9,5 @@ export enum SectionsType { SherpaPolicies = 'sherpaPolicy', Identifiers = 'identifiers', Collection = 'collection', - LdnService = 'ldn-service' + CoarNotify = 'coarnotify' } diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 8f35bb4420..f4f479e204 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -67,7 +67,11 @@ import { } from './sections/sherpa-policies/metadata-information/metadata-information.component'; import { SectionFormOperationsService } from './sections/form/section-form-operations.service'; import {SubmissionSectionIdentifiersComponent} from './sections/identifiers/section-identifiers.component'; -import { LdnServiceComponent } from './sections/ldn-service/ldn-service.component'; +import { SubmissionSectionCoarNotifyComponent } from './sections/section-coar-notify/section-coar-notify.component'; +import { + CoarNotifyConfigDataService +} from './sections/section-coar-notify/coar-notify-config-data.service'; +import { LdnServicesService } from '../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -77,7 +81,7 @@ const ENTRY_COMPONENTS = [ SubmissionSectionCcLicensesComponent, SubmissionSectionAccessesComponent, SubmissionSectionSherpaPoliciesComponent, - LdnServiceComponent + SubmissionSectionCoarNotifyComponent ]; const DECLARATIONS = [ @@ -138,6 +142,8 @@ const DECLARATIONS = [ SubmissionAccessesConfigDataService, SectionAccessesService, SectionFormOperationsService, + CoarNotifyConfigDataService, + LdnServicesService ] }) diff --git a/src/app/submission/submission.service.ts b/src/app/submission/submission.service.ts index 9eb8cf110a..7057f78c2f 100644 --- a/src/app/submission/submission.service.ts +++ b/src/app/submission/submission.service.ts @@ -298,6 +298,7 @@ export class SubmissionService { sectionObject.id = sectionId; sectionObject.sectionType = sections[sectionId].sectionType; availableSections.push(sectionObject); + console.log(sectionObject); }); return availableSections; }),