diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index a92b52b339..704f6c9e62 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -86,9 +86,6 @@ import { EPersonDataService } from './eperson/eperson-data.service'; import { EpersonResponseParsingService } from './eperson/eperson-response-parsing.service'; import { EPerson } from './eperson/models/eperson.model'; import { Group } from './eperson/models/group.model'; -import { AuthorityService } from './integration/authority.service'; -import { IntegrationResponseParsingService } from './integration/integration-response-parsing.service'; -import { AuthorityValue } from './integration/models/authority.value'; import { JsonPatchOperationsBuilder } from './json-patch/builder/json-patch-operations-builder'; import { MetadataField } from './metadata/metadata-field.model'; import { MetadataSchema } from './metadata/metadata-schema.model'; @@ -148,6 +145,8 @@ import { WorkflowAction } from './tasks/models/workflow-action-object.model'; import { VocabularyEntry } from './submission/vocabularies/models/vocabulary-entry.model'; import { Vocabulary } from './submission/vocabularies/models/vocabulary.model'; import { VocabularyEntriesResponseParsingService } from './submission/vocabularies/vocabulary-entries-response-parsing.service'; +import { VocabularyEntryDetail } from './submission/vocabularies/models/vocabulary-entry-detail.model'; +import { VocabularyService } from './submission/vocabularies/vocabulary.service'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -224,8 +223,6 @@ const PROVIDERS = [ SubmissionResponseParsingService, SubmissionJsonPatchOperationsService, JsonPatchOperationsBuilder, - AuthorityService, - IntegrationResponseParsingService, MetadataschemaParsingService, MetadatafieldParsingService, UploaderService, @@ -276,6 +273,7 @@ const PROVIDERS = [ NotificationsService, FilteredDiscoveryPageResponseParsingService, { provide: NativeWindowService, useFactory: NativeWindowFactory }, + VocabularyService, VocabularyEntriesResponseParsingService ]; @@ -305,7 +303,6 @@ export const models = SubmissionSectionModel, SubmissionUploadsModel, AuthStatus, - AuthorityValue, BrowseEntry, BrowseDefinition, ClaimedTask, @@ -320,7 +317,8 @@ export const models = VersionHistory, WorkflowAction, Vocabulary, - VocabularyEntry + VocabularyEntry, + VocabularyEntryDetail ]; @NgModule({ diff --git a/src/app/core/json-patch/builder/json-patch-operations-builder.ts b/src/app/core/json-patch/builder/json-patch-operations-builder.ts index c45183b4ef..36c82db283 100644 --- a/src/app/core/json-patch/builder/json-patch-operations-builder.ts +++ b/src/app/core/json-patch/builder/json-patch-operations-builder.ts @@ -9,7 +9,7 @@ import { JsonPatchOperationPathObject } from './json-patch-operation-path-combin import { Injectable } from '@angular/core'; import { isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { dateToISOFormat } from '../../../shared/date.util'; -import { AuthorityValue } from '../../integration/models/authority.value'; +import { VocabularyEntry } from '../../submission/vocabularies/models/vocabulary-entry.model'; import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; import { FormFieldLanguageValueObject } from '../../../shared/form/builder/models/form-field-language-value.model'; @@ -106,7 +106,7 @@ export class JsonPatchOperationsBuilder { operationValue = value; } else if (value instanceof Date) { operationValue = new FormFieldMetadataValueObject(dateToISOFormat(value)); - } else if (value instanceof AuthorityValue) { + } else if (value instanceof VocabularyEntry) { operationValue = this.prepareAuthorityValue(value); } else if (value instanceof FormFieldLanguageValueObject) { operationValue = new FormFieldMetadataValueObject(value.value, value.language); @@ -127,8 +127,8 @@ export class JsonPatchOperationsBuilder { protected prepareAuthorityValue(value: any) { let operationValue: any = null; - if (isNotEmpty(value.id)) { - operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.id); + if (isNotEmpty(value.authority)) { + operationValue = new FormFieldMetadataValueObject(value.value, value.language, value.authority); } else { operationValue = new FormFieldMetadataValueObject(value.value, value.language); } diff --git a/src/app/core/integration/models/confidence-type.ts b/src/app/core/shared/confidence-type.ts similarity index 100% rename from src/app/core/integration/models/confidence-type.ts rename to src/app/core/shared/confidence-type.ts diff --git a/src/app/core/submission/vocabularies/vocabulary.service.ts b/src/app/core/submission/vocabularies/vocabulary.service.ts index 13929525d4..bd5c5b6c48 100644 --- a/src/app/core/submission/vocabularies/vocabulary.service.ts +++ b/src/app/core/submission/vocabularies/vocabulary.service.ts @@ -74,9 +74,7 @@ class VocabularyEntryDetailDataServiceImpl extends DataService; @@ -102,7 +102,7 @@ export class Chips { private getChipsIcons(item) { const icons = []; - if (typeof item === 'string' || item instanceof FormFieldMetadataValueObject || item instanceof AuthorityValue) { + if (typeof item === 'string' || item instanceof FormFieldMetadataValueObject || item instanceof VocabularyEntry) { return icons; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts index 4dee6905d2..171ad69f64 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts @@ -44,7 +44,7 @@ import { SharedModule } from '../../../shared.module'; import { DynamicDsDatePickerModel } from './models/date-picker/date-picker.model'; import { DynamicRelationGroupModel } from './models/relation-group/dynamic-relation-group.model'; import { DynamicListCheckboxGroupModel } from './models/list/dynamic-list-checkbox-group.model'; -import { AuthorityOptions } from '../../../../core/integration/models/authority-options.model'; +import { VocabularyOptions } from '../../../../core/submission/vocabularies/models/vocabulary-options.model'; import { DynamicListRadioGroupModel } from './models/list/dynamic-list-radio-group.model'; import { DynamicLookupModel } from './models/lookup/dynamic-lookup.model'; import { DynamicScrollableDropdownModel } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; @@ -74,7 +74,7 @@ import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; describe('DsDynamicFormControlContainerComponent test suite', () => { - const authorityOptions: AuthorityOptions = { + const vocabularyOptions: VocabularyOptions = { closed: false, metadata: 'list', name: 'type_programme', @@ -104,7 +104,7 @@ describe('DsDynamicFormControlContainerComponent test suite', () => { new DynamicTypeaheadModel({ id: 'typeahead', metadataFields: [], repeatable: false, submissionId: '1234' }), new DynamicScrollableDropdownModel({ id: 'scrollableDropdown', - authorityOptions: authorityOptions, + vocabularyOptions: vocabularyOptions, metadataFields: [], repeatable: false, submissionId: '1234' @@ -112,12 +112,12 @@ describe('DsDynamicFormControlContainerComponent test suite', () => { new DynamicTagModel({ id: 'tag', metadataFields: [], repeatable: false, submissionId: '1234' }), new DynamicListCheckboxGroupModel({ id: 'checkboxList', - authorityOptions: authorityOptions, + vocabularyOptions: vocabularyOptions, repeatable: true }), new DynamicListRadioGroupModel({ id: 'radioList', - authorityOptions: authorityOptions, + vocabularyOptions: vocabularyOptions, repeatable: false }), new DynamicRelationGroupModel({ diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts index 78c2d5d217..e08116e5fa 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts @@ -8,10 +8,6 @@ import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dyna import { DsDatePickerComponent } from './date-picker.component'; import { DynamicDsDatePickerModel } from './date-picker.model'; -import { FormBuilderService } from '../../../form-builder.service'; - -import { FormComponent } from '../../../../form.component'; -import { FormService } from '../../../../form.service'; import { createTestComponent } from '../../../../../testing/utils.test'; export const DATE_TEST_GROUP = new FormGroup({ @@ -20,7 +16,7 @@ export const DATE_TEST_GROUP = new FormGroup({ export const DATE_TEST_MODEL_CONFIG = { disabled: false, - errorMessages: {required: 'You must enter at least the year.'}, + errorMessages: { required: 'You must enter at least the year.' }, id: 'date', label: 'Date', name: 'date', @@ -52,8 +48,8 @@ describe('DsDatePickerComponent test suite', () => { providers: [ ChangeDetectorRef, DsDatePickerComponent, - {provide: DynamicFormLayoutService, useValue: {}}, - {provide: DynamicFormValidationService, useValue: {}} + { provide: DynamicFormLayoutService, useValue: {} }, + { provide: DynamicFormValidationService, useValue: {} } ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts index 2e22f314ed..8523c60cf0 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts @@ -20,10 +20,6 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement @Input() bindId = true; @Input() group: FormGroup; @Input() model: DynamicDsDatePickerModel; - // @Input() - // minDate; - // @Input() - // maxDate; @Output() selected = new EventEmitter(); @Output() remove = new EventEmitter(); @@ -65,7 +61,7 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement this.initialMonth = now.getMonth() + 1; this.initialDay = now.getDate(); - if (this.model.value && this.model.value !== null) { + if (this.model && this.model.value !== null) { const values = this.model.value.toString().split(DS_DATE_PICKER_SEPARATOR); if (values.length > 0) { this.initialYear = parseInt(values[0], 10); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts index 3827df7be6..b723f7aa40 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts @@ -2,13 +2,13 @@ import { DynamicFormControlLayout, DynamicInputModel, DynamicInputModelConfig, s import { Subject } from 'rxjs'; import { LanguageCode } from '../../models/form-field-language-value.model'; -import { AuthorityOptions } from '../../../../../core/integration/models/authority-options.model'; +import { VocabularyOptions } from '../../../../../core/submission/vocabularies/models/vocabulary-options.model'; import { hasValue } from '../../../../empty.util'; import { FormFieldMetadataValueObject } from '../../models/form-field-metadata-value.model'; import { RelationshipOptions } from '../../models/relationship-options.model'; export interface DsDynamicInputModelConfig extends DynamicInputModelConfig { - authorityOptions?: AuthorityOptions; + vocabularyOptions?: VocabularyOptions; languageCodes?: LanguageCode[]; language?: string; value?: any; @@ -20,7 +20,7 @@ export interface DsDynamicInputModelConfig extends DynamicInputModelConfig { export class DsDynamicInputModel extends DynamicInputModel { - @serializable() authorityOptions: AuthorityOptions; + @serializable() vocabularyOptions: VocabularyOptions; @serializable() private _languageCodes: LanguageCode[]; @serializable() private _language: string; @serializable() languageUpdates: Subject; @@ -58,11 +58,11 @@ export class DsDynamicInputModel extends DynamicInputModel { this.language = lang; }); - this.authorityOptions = config.authorityOptions; + this.vocabularyOptions = config.vocabularyOptions; } get hasAuthority(): boolean { - return this.authorityOptions && hasValue(this.authorityOptions.name); + return this.vocabularyOptions && hasValue(this.vocabularyOptions.name); } get hasLanguages(): boolean { @@ -83,7 +83,7 @@ export class DsDynamicInputModel extends DynamicInputModel { set languageCodes(languageCodes: LanguageCode[]) { this._languageCodes = languageCodes; - if (!this.language || this.language === null || this.language === '') { + if (!this.language || this.language === '') { this.language = this.languageCodes ? this.languageCodes[0].code : null; } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts index f6b58c1504..fab371483c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts @@ -1,16 +1,17 @@ import { Subject } from 'rxjs'; - import { - DynamicCheckboxGroupModel, DynamicFormControlLayout, + DynamicCheckboxGroupModel, + DynamicFormControlLayout, DynamicFormGroupModelConfig, serializable } from '@ng-dynamic-forms/core'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; + +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; import { hasValue } from '../../../../../empty.util'; export interface DynamicListCheckboxGroupModelConfig extends DynamicFormGroupModelConfig { - authorityOptions: AuthorityOptions; + vocabularyOptions: VocabularyOptions; groupLength?: number; repeatable: boolean; value?: any; @@ -18,43 +19,44 @@ export interface DynamicListCheckboxGroupModelConfig extends DynamicFormGroupMod export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel { - @serializable() authorityOptions: AuthorityOptions; + @serializable() vocabularyOptions: VocabularyOptions; @serializable() repeatable: boolean; @serializable() groupLength: number; - @serializable() _value: AuthorityValue[]; isListGroup = true; valueUpdates: Subject; constructor(config: DynamicListCheckboxGroupModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); - this.authorityOptions = config.authorityOptions; + this.vocabularyOptions = config.vocabularyOptions; this.groupLength = config.groupLength || 5; this._value = []; this.repeatable = config.repeatable; this.valueUpdates = new Subject(); - this.valueUpdates.subscribe((value: AuthorityValue | AuthorityValue[]) => this.value = value); + this.valueUpdates.subscribe((value: VocabularyEntry | VocabularyEntry[]) => this.value = value); this.valueUpdates.next(config.value); } - get hasAuthority(): boolean { - return this.authorityOptions && hasValue(this.authorityOptions.name); - } + @serializable() _value: VocabularyEntry[]; get value() { return this._value; } - set value(value: AuthorityValue | AuthorityValue[]) { + set value(value: VocabularyEntry | VocabularyEntry[]) { if (value) { if (Array.isArray(value)) { this._value = value; } else { // _value is non extendible so assign it a new array - const newValue = (this.value as AuthorityValue[]).concat([value]); + const newValue = (this.value as VocabularyEntry[]).concat([value]); this._value = newValue } } } + + get hasAuthority(): boolean { + return this.vocabularyOptions && hasValue(this.vocabularyOptions.name); + } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts index 287c10f3fe..a4a0fb7749 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts @@ -4,11 +4,11 @@ import { DynamicRadioGroupModelConfig, serializable } from '@ng-dynamic-forms/core'; -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; import { hasValue } from '../../../../../empty.util'; export interface DynamicListModelConfig extends DynamicRadioGroupModelConfig { - authorityOptions: AuthorityOptions; + vocabularyOptions: VocabularyOptions; groupLength?: number; repeatable: boolean; value?: any; @@ -16,7 +16,7 @@ export interface DynamicListModelConfig extends DynamicRadioGroupModelConfig { - @serializable() authorityOptions: AuthorityOptions; + @serializable() vocabularyOptions: VocabularyOptions; @serializable() repeatable: boolean; @serializable() groupLength: number; isListGroup = true; @@ -24,13 +24,13 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel { constructor(config: DynamicListModelConfig, layout?: DynamicFormControlLayout) { super(config, layout); - this.authorityOptions = config.authorityOptions; + this.vocabularyOptions = config.vocabularyOptions; this.groupLength = config.groupLength || 5; this.repeatable = config.repeatable; this.valueUpdates.next(config.value); } get hasAuthority(): boolean { - return this.authorityOptions && hasValue(this.authorityOptions.name); + return this.vocabularyOptions && hasValue(this.vocabularyOptions.name); } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts index 63ae56e59a..b35ee10e8a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts @@ -2,25 +2,25 @@ import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { async, ComponentFixture, inject, TestBed, } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; - -import { DsDynamicListComponent } from './dynamic-list.component'; -import { DynamicListCheckboxGroupModel } from './dynamic-list-checkbox-group.model'; -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; -import { FormBuilderService } from '../../../form-builder.service'; +import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; import { DynamicFormControlLayout, DynamicFormLayoutService, DynamicFormsCoreModule, DynamicFormValidationService } from '@ng-dynamic-forms/core'; -import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { AuthorityServiceStub } from '../../../../../testing/authority-service.stub'; + +import { DsDynamicListComponent } from './dynamic-list.component'; +import { DynamicListCheckboxGroupModel } from './dynamic-list-checkbox-group.model'; +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; +import { FormBuilderService } from '../../../form-builder.service'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub'; import { DynamicListRadioGroupModel } from './dynamic-list-radio-group.model'; -import { By } from '@angular/platform-browser'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { createTestComponent } from '../../../../../testing/utils.test'; export const LAYOUT_TEST = { @@ -35,12 +35,12 @@ export const LIST_TEST_GROUP = new FormGroup({ }); export const LIST_CHECKBOX_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'listCheckbox', name: 'type_programme', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, id: 'listCheckbox', label: 'Programme', @@ -52,12 +52,12 @@ export const LIST_CHECKBOX_TEST_MODEL_CONFIG = { }; export const LIST_RADIO_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'listRadio', name: 'type_programme', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, id: 'listRadio', label: 'Programme', @@ -77,7 +77,7 @@ describe('DsDynamicListComponent test suite', () => { let html; let modelValue; - const authorityServiceStub = new AuthorityServiceStub(); + const vocabularyServiceStub = new VocabularyServiceStub(); // async beforeEach beforeEach(async(() => { @@ -99,9 +99,9 @@ describe('DsDynamicListComponent test suite', () => { DsDynamicListComponent, DynamicFormValidationService, FormBuilderService, - {provide: AuthorityService, useValue: authorityServiceStub}, - {provide: DynamicFormLayoutService, useValue: {}}, - {provide: DynamicFormValidationService, useValue: {}} + { provide: VocabularyService, useValue: vocabularyServiceStub }, + { provide: DynamicFormLayoutService, useValue: {} }, + { provide: DynamicFormValidationService, useValue: {} } ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }); @@ -147,20 +147,16 @@ describe('DsDynamicListComponent test suite', () => { }); it('should init component properly', () => { - const results$ = authorityServiceStub.getEntriesByName({} as any); - - results$.subscribe((results) => { - expect((listComp as any).optionsList).toEqual(results.payload); - expect(listComp.items.length).toBe(1); - expect(listComp.items[0].length).toBe(2); - }) + expect((listComp as any).optionsList).toEqual(vocabularyServiceStub.getList()); + expect(listComp.items.length).toBe(1); + expect(listComp.items[0].length).toBe(2); }); it('should set model value properly when a checkbox option is selected', () => { const de = listFixture.debugElement.queryAll(By.css('div.custom-checkbox')); const items = de[0].queryAll(By.css('input.custom-control-input')); const item = items[0]; - modelValue = [Object.assign(new AuthorityValue(), {id: 1, display: 'one', value: 1})]; + modelValue = [Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 })]; item.nativeElement.click(); @@ -187,7 +183,7 @@ describe('DsDynamicListComponent test suite', () => { listComp = listFixture.componentInstance; // FormComponent test instance listComp.group = LIST_TEST_GROUP; listComp.model = new DynamicListCheckboxGroupModel(LIST_CHECKBOX_TEST_MODEL_CONFIG, LAYOUT_TEST); - modelValue = [Object.assign(new AuthorityValue(), {id: 1, display: 'one', value: 1})]; + modelValue = [Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 })]; listComp.model.value = modelValue; listFixture.detectChanges(); }); @@ -198,13 +194,9 @@ describe('DsDynamicListComponent test suite', () => { }); it('should init component properly', () => { - const results$ = authorityServiceStub.getEntriesByName({} as any); - - results$.subscribe((results) => { - expect((listComp as any).optionsList).toEqual(results.payload); - expect(listComp.model.value).toEqual(modelValue); - expect((listComp.model as DynamicListCheckboxGroupModel).group[0].value).toBeTruthy(); - }) + expect((listComp as any).optionsList).toEqual(vocabularyServiceStub.getList()); + expect(listComp.model.value).toEqual(modelValue); + expect((listComp.model as DynamicListCheckboxGroupModel).group[0].value).toBeTruthy(); }); it('should set model value properly when a checkbox option is deselected', () => { @@ -237,20 +229,16 @@ describe('DsDynamicListComponent test suite', () => { }); it('should init component properly', () => { - const results$ = authorityServiceStub.getEntriesByName({} as any); - - results$.subscribe((results) => { - expect((listComp as any).optionsList).toEqual(results.payload); - expect(listComp.items.length).toBe(1); - expect(listComp.items[0].length).toBe(2); - }) + expect((listComp as any).optionsList).toEqual(vocabularyServiceStub.getList()); + expect(listComp.items.length).toBe(1); + expect(listComp.items[0].length).toBe(2); }); it('should set model value when a radio option is selected', () => { const de = listFixture.debugElement.queryAll(By.css('div.custom-radio')); const items = de[0].queryAll(By.css('input.custom-control-input')); const item = items[0]; - modelValue = Object.assign(new AuthorityValue(), {id: 1, display: 'one', value: 1}); + modelValue = Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 }); item.nativeElement.click(); @@ -265,7 +253,7 @@ describe('DsDynamicListComponent test suite', () => { listComp = listFixture.componentInstance; // FormComponent test instance listComp.group = LIST_TEST_GROUP; listComp.model = new DynamicListRadioGroupModel(LIST_RADIO_TEST_MODEL_CONFIG, LAYOUT_TEST); - modelValue = Object.assign(new AuthorityValue(), {id: 1, display: 'one', value: 1}); + modelValue = Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 }); listComp.model.value = modelValue; listFixture.detectChanges(); }); @@ -276,13 +264,9 @@ describe('DsDynamicListComponent test suite', () => { }); it('should init component properly', () => { - const results$ = authorityServiceStub.getEntriesByName({} as any); - - results$.subscribe((results) => { - expect((listComp as any).optionsList).toEqual(results.payload); - expect(listComp.model.value).toEqual(modelValue); - expect((listComp.model as DynamicListRadioGroupModel).options[0].value).toBeTruthy(); - }) + expect((listComp as any).optionsList).toEqual(vocabularyServiceStub.getList()); + expect(listComp.model.value).toEqual(modelValue); + expect((listComp.model as DynamicListRadioGroupModel).options[0].value).toBeTruthy(); }); }); }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts index d023f1c583..3c5a86f362 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts @@ -1,20 +1,23 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormGroup } from '@angular/forms'; + +import { + DynamicCheckboxModel, + DynamicFormControlComponent, + DynamicFormLayoutService, + DynamicFormValidationService +} from '@ng-dynamic-forms/core'; import { findKey } from 'lodash'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model'; +import { VocabularyFindOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; import { hasValue, isNotEmpty } from '../../../../../empty.util'; import { DynamicListCheckboxGroupModel } from './dynamic-list-checkbox-group.model'; import { FormBuilderService } from '../../../form-builder.service'; -import { - DynamicCheckboxModel, - DynamicFormControlComponent, DynamicFormLayoutService, - DynamicFormValidationService -} from '@ng-dynamic-forms/core'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; import { DynamicListRadioGroupModel } from './dynamic-list-radio-group.model'; -import { IntegrationData } from '../../../../../../core/integration/integration-data'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators'; +import { PaginatedList } from '../../../../../../core/data/paginated-list'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; export interface ListItem { id: string, @@ -39,10 +42,10 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen @Output() focus: EventEmitter = new EventEmitter(); public items: ListItem[][] = []; - protected optionsList: AuthorityValue[]; - protected searchOptions: IntegrationSearchOptions; + protected optionsList: VocabularyEntry[]; + protected searchOptions: VocabularyFindOptions; - constructor(private authorityService: AuthorityService, + constructor(private vocabularyService: VocabularyService, private cdr: ChangeDetectorRef, private formBuilderService: FormBuilderService, protected layoutService: DynamicFormLayoutService, @@ -54,10 +57,10 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen ngOnInit() { if (this.hasAuthorityOptions()) { // TODO Replace max elements 1000 with a paginated request when pagination bug is resolved - this.searchOptions = new IntegrationSearchOptions( - this.model.authorityOptions.scope, - this.model.authorityOptions.name, - this.model.authorityOptions.metadata, + this.searchOptions = new VocabularyFindOptions( + this.model.vocabularyOptions.scope, + this.model.vocabularyOptions.name, + this.model.vocabularyOptions.metadata, '', 1000, // Max elements 1);// Current Page @@ -77,13 +80,13 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen const target = event.target as any; if (this.model.repeatable) { // Target tabindex coincide with the array index of the value into the authority list - const authorityValue: AuthorityValue = this.optionsList[target.tabIndex]; + const entry: VocabularyEntry = this.optionsList[target.tabIndex]; if (target.checked) { - this.model.valueUpdates.next(authorityValue); + this.model.valueUpdates.next(entry); } else { const newValue = []; this.model.value - .filter((item) => item.value !== authorityValue.value) + .filter((item) => item.value !== entry.value) .forEach((item) => newValue.push(item)); this.model.valueUpdates.next(newValue); } @@ -94,16 +97,18 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen } protected setOptionsFromAuthority() { - if (this.model.authorityOptions.name && this.model.authorityOptions.name.length > 0) { + if (this.model.vocabularyOptions.name && this.model.vocabularyOptions.name.length > 0) { const listGroup = this.group.controls[this.model.id] as FormGroup; - this.authorityService.getEntriesByName(this.searchOptions).subscribe((authorities: IntegrationData) => { + this.vocabularyService.getVocabularyEntries(this.searchOptions).pipe( + getFirstSucceededRemoteDataPayload() + ).subscribe((entries: PaginatedList) => { let groupCounter = 0; let itemsPerGroup = 0; let tempList: ListItem[] = []; - this.optionsList = authorities.payload as AuthorityValue[]; + this.optionsList = entries.page; // Make a list of available options (checkbox/radio) and split in groups of 'model.groupLength' - (authorities.payload as AuthorityValue[]).forEach((option, key) => { - const value = option.id || option.value; + entries.page.forEach((option, key) => { + const value = option.authority || option.value; const checked: boolean = isNotEmpty(findKey( this.model.value, (v) => v.value === option.value)); @@ -138,8 +143,8 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen } protected hasAuthorityOptions() { - return (hasValue(this.model.authorityOptions.scope) - && hasValue(this.model.authorityOptions.name) - && hasValue(this.model.authorityOptions.metadata)); + return (hasValue(this.model.vocabularyOptions.scope) + && hasValue(this.model.vocabularyOptions.name) + && hasValue(this.model.vocabularyOptions.metadata)); } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts index c77aabfeed..ce45e113a0 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts @@ -2,33 +2,33 @@ import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { async, ComponentFixture, fakeAsync, inject, TestBed, tick, } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; - -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; -import { DynamicFormLayoutService, DynamicFormsCoreModule, DynamicFormValidationService } from '@ng-dynamic-forms/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { AuthorityServiceStub } from '../../../../../testing/authority-service.stub'; +import { DynamicFormLayoutService, DynamicFormsCoreModule, DynamicFormValidationService } from '@ng-dynamic-forms/core'; + +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub'; import { DsDynamicLookupComponent } from './dynamic-lookup.component'; import { DynamicLookupModel } from './dynamic-lookup.model'; -import { InfiniteScrollModule } from 'ngx-infinite-scroll'; -import { TranslateModule } from '@ngx-translate/core'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; -import { By } from '@angular/platform-browser'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { createTestComponent } from '../../../../../testing/utils.test'; import { DynamicLookupNameModel } from './dynamic-lookup-name.model'; import { AuthorityConfidenceStateDirective } from '../../../../../authority-confidence/authority-confidence-state.directive'; import { ObjNgFor } from '../../../../../utils/object-ngfor.pipe'; let LOOKUP_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'lookup', name: 'RPAuthority', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, errorMessages: { required: 'Required field.' }, id: 'lookup', @@ -47,12 +47,12 @@ let LOOKUP_TEST_MODEL_CONFIG = { }; let LOOKUP_NAME_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'lookup-name', name: 'RPAuthority', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, errorMessages: { required: 'Required field.' }, id: 'lookupName', @@ -78,12 +78,12 @@ let LOOKUP_TEST_GROUP = new FormGroup({ describe('Dynamic Lookup component', () => { function init() { LOOKUP_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'lookup', name: 'RPAuthority', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, errorMessages: { required: 'Required field.' }, id: 'lookup', @@ -102,12 +102,12 @@ describe('Dynamic Lookup component', () => { }; LOOKUP_NAME_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'lookup-name', name: 'RPAuthority', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, errorMessages: { required: 'Required field.' }, id: 'lookupName', @@ -137,12 +137,11 @@ describe('Dynamic Lookup component', () => { let testFixture: ComponentFixture; let lookupFixture: ComponentFixture; let html; + let vocabularyServiceStub: VocabularyServiceStub; - let authorityServiceStub; // async beforeEach beforeEach(async(() => { - const authorityService = new AuthorityServiceStub(); - authorityServiceStub = authorityService; + vocabularyServiceStub = new VocabularyServiceStub(); TestBed.configureTestingModule({ imports: [ DynamicFormsCoreModule, @@ -162,7 +161,7 @@ describe('Dynamic Lookup component', () => { providers: [ ChangeDetectorRef, DsDynamicLookupComponent, - { provide: AuthorityService, useValue: authorityService }, + { provide: VocabularyService, useValue: vocabularyServiceStub }, { provide: DynamicFormLayoutService, useValue: {} }, { provide: DynamicFormValidationService, useValue: {} } ], @@ -247,7 +246,7 @@ describe('Dynamic Lookup component', () => { it('should return search results', fakeAsync(() => { const de = lookupFixture.debugElement.queryAll(By.css('button')); const btnEl = de[0].nativeElement; - const results$ = authorityServiceStub.getEntriesByName({} as any); + const results = vocabularyServiceStub.getList(); lookupComp.firstInputValue = 'test'; lookupFixture.detectChanges(); @@ -255,17 +254,15 @@ describe('Dynamic Lookup component', () => { btnEl.click(); tick(); lookupFixture.detectChanges(); - results$.subscribe((results) => { - expect(lookupComp.optionsList).toEqual(results.payload); - }); + expect(lookupComp.optionsList).toEqual(results); })); it('should select a results entry properly', fakeAsync(() => { let de = lookupFixture.debugElement.queryAll(By.css('button')); const btnEl = de[0].nativeElement; - const selectedValue = Object.assign(new AuthorityValue(), { - id: 1, + const selectedValue = Object.assign(new VocabularyEntry(), { + authority: 1, display: 'one', value: 1 }); @@ -284,7 +281,7 @@ describe('Dynamic Lookup component', () => { expect(lookupComp.change.emit).toHaveBeenCalled(); })); - it('should set model.value on input type when AuthorityOptions.closed is false', fakeAsync(() => { + it('should set model.value on input type when VocabularyOptions.closed is false', fakeAsync(() => { lookupComp.firstInputValue = 'test'; lookupFixture.detectChanges(); @@ -293,8 +290,8 @@ describe('Dynamic Lookup component', () => { })); - it('should not set model.value on input type when AuthorityOptions.closed is true', () => { - lookupComp.model.authorityOptions.closed = true; + it('should not set model.value on input type when VocabularyOptions.closed is true', () => { + lookupComp.model.vocabularyOptions.closed = true; lookupComp.firstInputValue = 'test'; lookupFixture.detectChanges(); @@ -389,26 +386,26 @@ describe('Dynamic Lookup component', () => { it('should select a results entry properly', fakeAsync(() => { const payload = [ - Object.assign(new AuthorityValue(), { - id: 1, + Object.assign(new VocabularyEntry(), { + authority: 1, display: 'Name, Lastname', value: 1 }), - Object.assign(new AuthorityValue(), { - id: 2, + Object.assign(new VocabularyEntry(), { + authority: 2, display: 'NameTwo, LastnameTwo', value: 2 }), ]; let de = lookupFixture.debugElement.queryAll(By.css('button')); const btnEl = de[0].nativeElement; - const selectedValue = Object.assign(new AuthorityValue(), { - id: 1, + const selectedValue = Object.assign(new VocabularyEntry(), { + authority: 1, display: 'Name, Lastname', value: 1 }); spyOn(lookupComp.change, 'emit'); - authorityServiceStub.setNewPayload(payload); + vocabularyServiceStub.setNewPayload(payload); lookupComp.firstInputValue = 'test'; lookupFixture.detectChanges(); btnEl.click(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts index d5516df6d9..24422e891a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts @@ -1,8 +1,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { Subscription } from 'rxjs'; -import { of as observableOf } from 'rxjs'; +import { of as observableOf, Subscription } from 'rxjs'; import { catchError, distinctUntilChanged } from 'rxjs/operators'; import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; import { @@ -11,16 +10,16 @@ import { DynamicFormValidationService } from '@ng-dynamic-forms/core'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { DynamicLookupModel } from './dynamic-lookup.model'; -import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyFindOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; import { hasValue, isEmpty, isNotEmpty, isNull, isUndefined } from '../../../../../empty.util'; -import { IntegrationData } from '../../../../../../core/integration/integration-data'; import { PageInfo } from '../../../../../../core/shared/page-info.model'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { DynamicLookupNameModel } from './dynamic-lookup-name.model'; -import { ConfidenceType } from '../../../../../../core/integration/models/confidence-type'; +import { ConfidenceType } from '../../../../../../core/shared/confidence-type'; +import { PaginatedList } from '../../../../../../core/data/paginated-list'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators'; @Component({ selector: 'ds-dynamic-lookup', @@ -43,10 +42,10 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem public pageInfo: PageInfo; public optionsList: any; - protected searchOptions: IntegrationSearchOptions; + protected searchOptions: VocabularyFindOptions; protected subs: Subscription[] = []; - constructor(private authorityService: AuthorityService, + constructor(private vocabularyService: VocabularyService, private cdr: ChangeDetectorRef, protected layoutService: DynamicFormLayoutService, protected validationService: DynamicFormValidationService @@ -59,10 +58,10 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem }; ngOnInit() { - this.searchOptions = new IntegrationSearchOptions( - this.model.authorityOptions.scope, - this.model.authorityOptions.name, - this.model.authorityOptions.metadata, + this.searchOptions = new VocabularyFindOptions( + this.model.vocabularyOptions.scope, + this.model.vocabularyOptions.name, + this.model.vocabularyOptions.metadata, '', this.model.maxOptions, 1); @@ -79,6 +78,148 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem })); } + public formatItemForInput(item: any, field: number): string { + if (isUndefined(item) || isNull(item)) { + return ''; + } + return (typeof item === 'string') ? item : this.inputFormatter(item, field); + } + + public hasAuthorityValue() { + return hasValue(this.model.value) + && this.model.value.hasAuthority(); + } + + public hasEmptyValue() { + return isNotEmpty(this.getCurrentValue()); + } + + public clearFields() { + // Clear inputs whether there is no results and authority is closed + if (this.model.vocabularyOptions.closed) { + this.resetFields(); + } + } + + public isEditDisabled() { + return !this.hasAuthorityValue(); + } + + public isInputDisabled() { + return (this.model.vocabularyOptions.closed && this.hasAuthorityValue() && !this.editMode); + } + + public isLookupName() { + return (this.model instanceof DynamicLookupNameModel); + } + + public isSearchDisabled() { + return isEmpty(this.firstInputValue) || this.editMode; + } + + public onBlurEvent(event: Event) { + this.blur.emit(event); + } + + public onFocusEvent(event) { + this.focus.emit(event); + } + + public onChange(event) { + event.preventDefault(); + if (!this.model.vocabularyOptions.closed) { + if (isNotEmpty(this.getCurrentValue())) { + const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue()); + if (!this.editMode) { + this.updateModel(currentValue); + } + } else { + this.remove(); + } + } + } + + public onScroll() { + if (!this.loading && this.pageInfo.currentPage <= this.pageInfo.totalPages) { + this.searchOptions.currentPage++; + this.search(); + } + } + + public onSelect(event) { + this.updateModel(event); + } + + public openChange(isOpened: boolean) { + if (!isOpened) { + if (this.model.vocabularyOptions.closed && !this.hasAuthorityValue()) { + this.setInputsValue(''); + } + } + } + + public remove() { + this.group.markAsPristine(); + this.model.valueUpdates.next(null); + this.change.emit(null); + } + + public saveChanges() { + if (isNotEmpty(this.getCurrentValue())) { + const newValue = Object.assign(new VocabularyEntry(), this.model.value, { + display: this.getCurrentValue(), + value: this.getCurrentValue() + }); + this.updateModel(newValue); + } else { + this.remove(); + } + this.switchEditMode(); + } + + public search() { + this.optionsList = null; + this.pageInfo = null; + + // Query + this.searchOptions.query = this.getCurrentValue(); + + this.loading = true; + this.subs.push(this.vocabularyService.getVocabularyEntries(this.searchOptions).pipe( + getFirstSucceededRemoteDataPayload(), + catchError(() => + observableOf(new PaginatedList( + new PageInfo(), + [] + )) + ), + distinctUntilChanged()) + .subscribe((list: PaginatedList) => { + console.log(list); + this.optionsList = list.page; + this.pageInfo = list.pageInfo; + this.loading = false; + this.cdr.detectChanges(); + })); + } + + public switchEditMode() { + this.editMode = !this.editMode; + } + + public whenClickOnConfidenceNotAccepted(sdRef: NgbDropdown, confidence: ConfidenceType) { + if (!this.model.readOnly) { + sdRef.open(); + this.search(); + } + } + + ngOnDestroy() { + this.subs + .filter((sub) => hasValue(sub)) + .forEach((sub) => sub.unsubscribe()); + } + protected getCurrentValue(): string { let result = ''; if (!this.isLookupName()) { @@ -106,7 +247,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem protected setInputsValue(value) { if (hasValue(value)) { let displayValue = value; - if (value instanceof FormFieldMetadataValueObject || value instanceof AuthorityValue) { + if (value instanceof FormFieldMetadataValueObject || value instanceof VocabularyEntry) { displayValue = value.display; } @@ -131,145 +272,4 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem this.optionsList = null; this.pageInfo = null; } - - public formatItemForInput(item: any, field: number): string { - if (isUndefined(item) || isNull(item)) { - return ''; - } - return (typeof item === 'string') ? item : this.inputFormatter(item, field); - } - - public hasAuthorityValue() { - return hasValue(this.model.value) - && this.model.value.hasAuthority(); - } - - public hasEmptyValue() { - return isNotEmpty(this.getCurrentValue()); - } - - public clearFields() { - // Clear inputs whether there is no results and authority is closed - if (this.model.authorityOptions.closed) { - this.resetFields(); - } - } - - public isEditDisabled() { - return !this.hasAuthorityValue(); - } - - public isInputDisabled() { - return (this.model.authorityOptions.closed && this.hasAuthorityValue() && !this.editMode); - } - - public isLookupName() { - return (this.model instanceof DynamicLookupNameModel); - } - - public isSearchDisabled() { - return isEmpty(this.firstInputValue) || this.editMode; - } - - public onBlurEvent(event: Event) { - this.blur.emit(event); - } - - public onFocusEvent(event) { - this.focus.emit(event); - } - - public onChange(event) { - event.preventDefault(); - if (!this.model.authorityOptions.closed) { - if (isNotEmpty(this.getCurrentValue())) { - const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue()); - if (!this.editMode) { - this.updateModel(currentValue); - } - } else { - this.remove(); - } - } - } - - public onScroll() { - if (!this.loading && this.pageInfo.currentPage <= this.pageInfo.totalPages) { - this.searchOptions.currentPage++; - this.search(); - } - } - - public onSelect(event) { - this.updateModel(event); - } - - public openChange(isOpened: boolean) { - if (!isOpened) { - if (this.model.authorityOptions.closed && !this.hasAuthorityValue()) { - this.setInputsValue(''); - } - } - } - - public remove() { - this.group.markAsPristine(); - this.model.valueUpdates.next(null); - this.change.emit(null); - } - - public saveChanges() { - if (isNotEmpty(this.getCurrentValue())) { - const newValue = Object.assign(new AuthorityValue(), this.model.value, { - display: this.getCurrentValue(), - value: this.getCurrentValue() - }); - this.updateModel(newValue); - } else { - this.remove(); - } - this.switchEditMode(); - } - - public search() { - this.optionsList = null; - this.pageInfo = null; - - // Query - this.searchOptions.query = this.getCurrentValue(); - - this.loading = true; - this.subs.push(this.authorityService.getEntriesByName(this.searchOptions).pipe( - catchError(() => { - const emptyResult = new IntegrationData( - new PageInfo(), - [] - ); - return observableOf(emptyResult); - }), - distinctUntilChanged()) - .subscribe((object: IntegrationData) => { - this.optionsList = object.payload; - this.pageInfo = object.pageInfo; - this.loading = false; - this.cdr.detectChanges(); - })); - } - - public switchEditMode() { - this.editMode = !this.editMode; - } - - public whenClickOnConfidenceNotAccepted(sdRef: NgbDropdown, confidence: ConfidenceType) { - if (!this.model.readOnly) { - sdRef.open(); - this.search(); - } - } - - ngOnDestroy() { - this.subs - .filter((sub) => hasValue(sub)) - .forEach((sub) => sub.unsubscribe()); - } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts index bcddb52123..64cf658d47 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts @@ -2,9 +2,12 @@ import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, inject, TestBed, } from '@angular/core/testing'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Store, StoreModule } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { DsDynamicRelationGroupComponent } from './dynamic-relation-group.components'; import { DynamicRelationGroupModel, DynamicRelationGroupModelConfig } from './dynamic-relation-group.model'; @@ -13,18 +16,14 @@ import { FormFieldModel } from '../../../models/form-field.model'; import { FormBuilderService } from '../../../form-builder.service'; import { FormService } from '../../../../form.service'; import { FormComponent } from '../../../../form.component'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Chips } from '../../../../../chips/models/chips.model'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; import { DsDynamicInputModel } from '../ds-dynamic-input.model'; import { createTestComponent } from '../../../../../testing/utils.test'; -import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { AuthorityServiceStub } from '../../../../../testing/authority-service.stub'; -import { Store, StoreModule } from '@ngrx/store'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub'; import { StoreMock } from '../../../../../testing/store.mock'; import { FormRowModel } from '../../../../../../core/config/models/config-submission-form.model'; -import { GlobalConfig } from '../../../../../../../config/global-config.interface'; import { storeModuleConfig } from '../../../../../../app.reducer'; export let FORM_GROUP_TEST_MODEL_CONFIG; @@ -47,7 +46,7 @@ function init() { mandatoryMessage: 'Required field!', repeatable: false, selectableMetadata: [{ - authority: 'RPAuthority', + controlledVocabulary: 'RPAuthority', closed: false, metadata: 'dc.contributor.author' }], @@ -61,7 +60,7 @@ function init() { mandatory: 'false', repeatable: false, selectableMetadata: [{ - authority: 'OUAuthority', + controlledVocabulary: 'OUAuthority', closed: false, metadata: 'local.contributor.affiliation' }] @@ -128,7 +127,7 @@ describe('DsDynamicRelationGroupComponent test suite', () => { FormBuilderService, FormComponent, FormService, - { provide: AuthorityService, useValue: new AuthorityServiceStub() }, + { provide: VocabularyService, useValue: new VocabularyServiceStub() }, { provide: Store, useClass: StoreMock } ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts index 11085a1bc3..21606d7abc 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts @@ -1,14 +1,4 @@ -import { - ChangeDetectorRef, - Component, - EventEmitter, - Inject, - Input, - OnDestroy, - OnInit, - Output, - ViewChild -} from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { combineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; @@ -33,12 +23,12 @@ import { hasValue, isEmpty, isNotEmpty, isNotNull } from '../../../../../empty.u import { shrinkInOut } from '../../../../../animations/shrink'; import { ChipsItem } from '../../../../../chips/models/chips-item.model'; import { hasOnlyEmptyProperties } from '../../../../../object.util'; -import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { IntegrationData } from '../../../../../../core/integration/integration-data'; +import { VocabularyFindOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; import { environment } from '../../../../../../../environments/environment'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators'; +import { VocabularyEntryDetail } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; @Component({ selector: 'ds-dynamic-relation-group', @@ -64,9 +54,9 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent private selectedChipItem: ChipsItem; private subs: Subscription[] = []; - @ViewChild('formRef', {static: false}) private formRef: FormComponent; + @ViewChild('formRef', { static: false }) private formRef: FormComponent; - constructor(private authorityService: AuthorityService, + constructor(private vocabularyService: VocabularyService, private formBuilderService: FormBuilderService, private formService: FormService, private cdr: ChangeDetectorRef, @@ -177,6 +167,12 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent this.clear(); } + ngOnDestroy(): void { + this.subs + .filter((sub) => hasValue(sub)) + .forEach((sub) => sub.unsubscribe()); + } + private addToChips() { if (!this.formRef.formGroup.valid) { this.formService.validateAllFormFields(this.formRef.formGroup); @@ -235,20 +231,16 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent if (isObject(valueObj[fieldName]) && valueObj[fieldName].hasAuthority() && isNotEmpty(valueObj[fieldName].authority)) { const fieldId = fieldName.replace(/\./g, '_'); const model = this.formBuilderService.findById(fieldId, this.formModel); - const searchOptions: IntegrationSearchOptions = new IntegrationSearchOptions( - (model as any).authorityOptions.scope, - (model as any).authorityOptions.name, - (model as any).authorityOptions.metadata, + return$ = this.vocabularyService.findEntryDetailByValue( valueObj[fieldName].authority, - (model as any).maxOptions, - 1); - - return$ = this.authorityService.getEntryByValue(searchOptions).pipe( - map((result: IntegrationData) => Object.assign( + (model as any).vocabularyOptions.name + ).pipe( + getFirstSucceededRemoteDataPayload(), + map((entryDetail: VocabularyEntryDetail) => Object.assign( new FormFieldMetadataValueObject(), valueObj[fieldName], { - otherInformation: (result.payload[0] as AuthorityValue).otherInformation + otherInformation: entryDetail.otherInformation }) )); } else { @@ -315,10 +307,4 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent } } - ngOnDestroy(): void { - this.subs - .filter((sub) => hasValue(sub)) - .forEach((sub) => sub.unsubscribe()); - } - } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts index 6086444264..c06908bdc5 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts @@ -9,12 +9,12 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { DynamicFormLayoutService, DynamicFormsCoreModule, DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { AuthorityServiceStub } from '../../../../../testing/authority-service.stub'; +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub'; import { DsDynamicScrollableDropdownComponent } from './dynamic-scrollable-dropdown.component'; import { DynamicScrollableDropdownModel } from './dynamic-scrollable-dropdown.model'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { createTestComponent, hasClass } from '../../../../../testing/utils.test'; export const SD_TEST_GROUP = new FormGroup({ @@ -22,14 +22,14 @@ export const SD_TEST_GROUP = new FormGroup({ }); export const SD_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'dropdown', name: 'common_iso_languages', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, - errorMessages: {required: 'Required field.'}, + errorMessages: { required: 'Required field.' }, id: 'dropdown', label: 'Language', maxOptions: 10, @@ -52,7 +52,7 @@ describe('Dynamic Dynamic Scrollable Dropdown component', () => { let html; let modelValue; - const authorityServiceStub = new AuthorityServiceStub(); + const vocabularyServiceStub = new VocabularyServiceStub(); // async beforeEach beforeEach(async(() => { @@ -74,9 +74,9 @@ describe('Dynamic Dynamic Scrollable Dropdown component', () => { providers: [ ChangeDetectorRef, DsDynamicScrollableDropdownComponent, - {provide: AuthorityService, useValue: authorityServiceStub}, - {provide: DynamicFormLayoutService, useValue: {}}, - {provide: DynamicFormValidationService, useValue: {}} + { provide: VocabularyService, useValue: vocabularyServiceStub }, + { provide: DynamicFormLayoutService, useValue: {} }, + { provide: DynamicFormValidationService, useValue: {} } ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }); @@ -121,11 +121,8 @@ describe('Dynamic Dynamic Scrollable Dropdown component', () => { }); it('should init component properly', () => { - const results$ = authorityServiceStub.getEntriesByName({} as any); expect(scrollableDropdownComp.optionsList).toBeDefined(); - results$.subscribe((results) => { - expect(scrollableDropdownComp.optionsList).toEqual(results.payload); - }) + expect(scrollableDropdownComp.optionsList).toEqual(vocabularyServiceStub.getList()); }); it('should display dropdown menu entries', () => { @@ -154,7 +151,7 @@ describe('Dynamic Dynamic Scrollable Dropdown component', () => { })); it('should select a results entry properly', fakeAsync(() => { - const selectedValue = Object.assign(new AuthorityValue(), {id: 1, display: 'one', value: 1}); + const selectedValue = Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 }); let de: any = scrollableDropdownFixture.debugElement.query(By.css('button.ds-form-input-btn')); let btnEl = de.nativeElement; @@ -192,7 +189,7 @@ describe('Dynamic Dynamic Scrollable Dropdown component', () => { scrollableDropdownFixture = TestBed.createComponent(DsDynamicScrollableDropdownComponent); scrollableDropdownComp = scrollableDropdownFixture.componentInstance; // FormComponent test instance scrollableDropdownComp.group = SD_TEST_GROUP; - modelValue = Object.assign(new AuthorityValue(), {id: 1, display: 'one', value: 1}); + modelValue = Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 }); scrollableDropdownComp.model = new DynamicScrollableDropdownModel(SD_TEST_MODEL_CONFIG); scrollableDropdownComp.model.value = modelValue; scrollableDropdownFixture.detectChanges(); @@ -204,12 +201,9 @@ describe('Dynamic Dynamic Scrollable Dropdown component', () => { }); it('should init component properly', () => { - const results$ = authorityServiceStub.getEntriesByName({} as any); expect(scrollableDropdownComp.optionsList).toBeDefined(); - results$.subscribe((results) => { - expect(scrollableDropdownComp.optionsList).toEqual(results.payload); - expect(scrollableDropdownComp.model.value).toEqual(modelValue); - }) + expect(scrollableDropdownComp.optionsList).toEqual(vocabularyServiceStub.getList()); + expect(scrollableDropdownComp.model.value).toEqual(modelValue); }); }); }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts index 5eda1372eb..f6442407c4 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro import { FormGroup } from '@angular/forms'; import { Observable, of as observableOf } from 'rxjs'; -import { catchError, distinctUntilChanged, first, tap } from 'rxjs/operators'; +import { catchError, distinctUntilChanged, tap } from 'rxjs/operators'; import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; import { DynamicFormControlComponent, @@ -10,13 +10,14 @@ import { DynamicFormValidationService } from '@ng-dynamic-forms/core'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { DynamicScrollableDropdownModel } from './dynamic-scrollable-dropdown.model'; import { PageInfo } from '../../../../../../core/shared/page-info.model'; import { isNull, isUndefined } from '../../../../../empty.util'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model'; -import { IntegrationData } from '../../../../../../core/integration/integration-data'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyFindOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators'; +import { PaginatedList } from '../../../../../../core/data/paginated-list'; @Component({ selector: 'ds-dynamic-scrollable-dropdown', @@ -37,9 +38,9 @@ export class DsDynamicScrollableDropdownComponent extends DynamicFormControlComp public pageInfo: PageInfo; public optionsList: any; - protected searchOptions: IntegrationSearchOptions; + protected searchOptions: VocabularyFindOptions; - constructor(private authorityService: AuthorityService, + constructor(private vocabularyService: VocabularyService, private cdr: ChangeDetectorRef, protected layoutService: DynamicFormLayoutService, protected validationService: DynamicFormValidationService @@ -48,28 +49,26 @@ export class DsDynamicScrollableDropdownComponent extends DynamicFormControlComp } ngOnInit() { - this.searchOptions = new IntegrationSearchOptions( - this.model.authorityOptions.scope, - this.model.authorityOptions.name, - this.model.authorityOptions.metadata, + this.searchOptions = new VocabularyFindOptions( + this.model.vocabularyOptions.scope, + this.model.vocabularyOptions.name, + this.model.vocabularyOptions.metadata, '', this.model.maxOptions, 1); - this.authorityService.getEntriesByName(this.searchOptions).pipe( - catchError(() => { - const emptyResult = new IntegrationData( - new PageInfo(), - [] - ); - return observableOf(emptyResult); - }), - first()) - .subscribe((object: IntegrationData) => { - this.optionsList = object.payload; + this.vocabularyService.getVocabularyEntries(this.searchOptions).pipe( + getFirstSucceededRemoteDataPayload(), + catchError(() => observableOf(new PaginatedList( + new PageInfo(), + [] + )) + )) + .subscribe((list: PaginatedList) => { + this.optionsList = list.page; if (this.model.value) { this.setCurrentValue(this.model.value); } - this.pageInfo = object.pageInfo; + this.pageInfo = list.pageInfo; this.cdr.detectChanges(); }); @@ -80,7 +79,7 @@ export class DsDynamicScrollableDropdownComponent extends DynamicFormControlComp } - inputFormatter = (x: AuthorityValue): string => x.display || x.value; + inputFormatter = (x: VocabularyEntry): string => x.display || x.value; openDropdown(sdRef: NgbDropdown) { if (!this.model.readOnly) { @@ -92,18 +91,17 @@ export class DsDynamicScrollableDropdownComponent extends DynamicFormControlComp if (!this.loading && this.pageInfo.currentPage <= this.pageInfo.totalPages) { this.loading = true; this.searchOptions.currentPage++; - this.authorityService.getEntriesByName(this.searchOptions).pipe( - catchError(() => { - const emptyResult = new IntegrationData( - new PageInfo(), - [] - ); - return observableOf(emptyResult); - }), + this.vocabularyService.getVocabularyEntries(this.searchOptions).pipe( + getFirstSucceededRemoteDataPayload(), + catchError(() => observableOf(new PaginatedList( + new PageInfo(), + [] + )) + ), tap(() => this.loading = false)) - .subscribe((object: IntegrationData) => { - this.optionsList = this.optionsList.concat(object.payload); - this.pageInfo = object.pageInfo; + .subscribe((list: PaginatedList) => { + this.optionsList = this.optionsList.concat(list.page); + this.pageInfo = list.pageInfo; this.cdr.detectChanges(); }) } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts index 8ab497ec6e..a9974717e4 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts @@ -1,11 +1,11 @@ import { AUTOCOMPLETE_OFF, DynamicFormControlLayout, serializable } from '@ng-dynamic-forms/core'; import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model'; -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; export const DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN = 'SCROLLABLE_DROPDOWN'; export interface DynamicScrollableDropdownModelConfig extends DsDynamicInputModelConfig { - authorityOptions: AuthorityOptions; + vocabularyOptions: VocabularyOptions; maxOptions?: number; value?: any; } @@ -20,7 +20,7 @@ export class DynamicScrollableDropdownModel extends DsDynamicInputModel { super(config, layout); this.autoComplete = AUTOCOMPLETE_OFF; - this.authorityOptions = config.authorityOptions; + this.vocabularyOptions = config.vocabularyOptions; this.maxOptions = config.maxOptions || 10; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts index 79d652623f..a85c215b5f 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts @@ -12,15 +12,14 @@ import { import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; import { NgbModule, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { AuthorityServiceStub } from '../../../../../testing/authority-service.stub'; +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub'; import { DsDynamicTagComponent } from './dynamic-tag.component'; import { DynamicTagModel } from './dynamic-tag.model'; -import { GlobalConfig } from '../../../../../../../config/global-config.interface'; import { Chips } from '../../../../../chips/models/chips.model'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; -import { AuthorityValue } from '../../../../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { createTestComponent } from '../../../../../testing/utils.test'; function createKeyUpEvent(key: number) { @@ -45,12 +44,12 @@ function init() { }); TAG_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'tag', name: 'common_iso_languages', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, id: 'tag', label: 'Keywords', @@ -75,7 +74,7 @@ describe('DsDynamicTagComponent test suite', () => { // async beforeEach beforeEach(async(() => { - const authorityServiceStub = new AuthorityServiceStub(); + const vocabularyServiceStub = new VocabularyServiceStub(); init(); TestBed.configureTestingModule({ imports: [ @@ -92,7 +91,7 @@ describe('DsDynamicTagComponent test suite', () => { providers: [ ChangeDetectorRef, DsDynamicTagComponent, - { provide: AuthorityService, useValue: authorityServiceStub }, + { provide: VocabularyService, useValue: vocabularyServiceStub }, { provide: DynamicFormLayoutService, useValue: {} }, { provide: DynamicFormValidationService, useValue: {} } ], @@ -124,7 +123,7 @@ describe('DsDynamicTagComponent test suite', () => { })); }); - describe('when authorityOptions are set', () => { + describe('when vocabularyOptions are set', () => { describe('and init model value is empty', () => { beforeEach(() => { @@ -148,20 +147,20 @@ describe('DsDynamicTagComponent test suite', () => { }); it('should search when 3+ characters typed', fakeAsync(() => { - spyOn((tagComp as any).authorityService, 'getEntriesByName').and.callThrough(); + spyOn((tagComp as any).vocabularyService, 'getVocabularyEntries').and.callThrough(); tagComp.search(observableOf('test')).subscribe(() => { - expect((tagComp as any).authorityService.getEntriesByName).toHaveBeenCalled(); + expect((tagComp as any).vocabularyService.getVocabularyEntries).toHaveBeenCalled(); }); })); it('should select a results entry properly', fakeAsync(() => { modelValue = [ - Object.assign(new AuthorityValue(), { id: 1, display: 'Name, Lastname', value: 1 }) + Object.assign(new VocabularyEntry(), { authority: 1, display: 'Name, Lastname', value: 1 }) ]; const event: NgbTypeaheadSelectItemEvent = { - item: Object.assign(new AuthorityValue(), { - id: 1, + item: Object.assign(new VocabularyEntry(), { + authority: 1, display: 'Name, Lastname', value: 1 }), @@ -239,7 +238,7 @@ describe('DsDynamicTagComponent test suite', () => { }); - describe('when authorityOptions are not set', () => { + describe('when vocabularyOptions are not set', () => { describe('and init model value is empty', () => { beforeEach(() => { @@ -247,7 +246,7 @@ describe('DsDynamicTagComponent test suite', () => { tagComp = tagFixture.componentInstance; // FormComponent test instance tagComp.group = TAG_TEST_GROUP; const config = TAG_TEST_MODEL_CONFIG; - config.authorityOptions = null; + config.vocabularyOptions = null; tagComp.model = new DynamicTagModel(config); tagFixture.detectChanges(); }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts index 69a7a5a8c2..85d791da6d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, EventEmitter, Inject, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { @@ -6,17 +6,21 @@ import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; -import { of as observableOf, Observable } from 'rxjs'; -import { catchError, debounceTime, distinctUntilChanged, tap, switchMap, map, merge } from 'rxjs/operators'; +import { Observable, of as observableOf } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, map, merge, switchMap, tap } from 'rxjs/operators'; import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; import { isEqual } from 'lodash'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; import { DynamicTagModel } from './dynamic-tag.model'; -import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model'; +import { VocabularyFindOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; import { Chips } from '../../../../../chips/models/chips.model'; import { hasValue, isNotEmpty } from '../../../../../empty.util'; import { environment } from '../../../../../../../environments/environment'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators'; +import { PaginatedList } from '../../../../../../core/data/paginated-list'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; +import { PageInfo } from '../../../../../../core/shared/page-info.model'; @Component({ selector: 'ds-dynamic-tag', @@ -32,17 +36,25 @@ export class DsDynamicTagComponent extends DynamicFormControlComponent implement @Output() change: EventEmitter = new EventEmitter(); @Output() focus: EventEmitter = new EventEmitter(); - @ViewChild('instance', {static: false}) instance: NgbTypeahead; + @ViewChild('instance', { static: false }) instance: NgbTypeahead; chips: Chips; hasAuthority: boolean; searching = false; - searchOptions: IntegrationSearchOptions; + searchOptions: VocabularyFindOptions; searchFailed = false; hideSearchingWhenUnsubscribed = new Observable(() => () => this.changeSearchingStatus(false)); currentValue: any; + constructor(private vocabularyService: VocabularyService, + private cdr: ChangeDetectorRef, + protected layoutService: DynamicFormLayoutService, + protected validationService: DynamicFormValidationService + ) { + super(layoutService, validationService); + } + formatter = (x: { display: string }) => x.display; search = (text$: Observable) => @@ -52,44 +64,33 @@ export class DsDynamicTagComponent extends DynamicFormControlComponent implement tap(() => this.changeSearchingStatus(true)), switchMap((term) => { if (term === '' || term.length < this.model.minChars) { - return observableOf({list: []}); + return observableOf({ list: [] }); } else { this.searchOptions.query = term; - return this.authorityService.getEntriesByName(this.searchOptions).pipe( - map((authorities) => { - // @TODO Pagination for authority is not working, to refactor when it will be fixed - return { - list: authorities.payload, - pageInfo: authorities.pageInfo - }; - }), + return this.vocabularyService.getVocabularyEntries(this.searchOptions).pipe( + getFirstSucceededRemoteDataPayload(), tap(() => this.searchFailed = false), catchError(() => { this.searchFailed = true; - return observableOf({list: []}); + return observableOf(new PaginatedList( + new PageInfo(), + [] + )); })); } }), - map((results) => results.list), + map((list: PaginatedList) => list.page), tap(() => this.changeSearchingStatus(false)), merge(this.hideSearchingWhenUnsubscribed)); - constructor(private authorityService: AuthorityService, - private cdr: ChangeDetectorRef, - protected layoutService: DynamicFormLayoutService, - protected validationService: DynamicFormValidationService - ) { - super(layoutService, validationService); - } - ngOnInit() { - this.hasAuthority = this.model.authorityOptions && hasValue(this.model.authorityOptions.name); + this.hasAuthority = this.model.vocabularyOptions && hasValue(this.model.vocabularyOptions.name); if (this.hasAuthority) { - this.searchOptions = new IntegrationSearchOptions( - this.model.authorityOptions.scope, - this.model.authorityOptions.name, - this.model.authorityOptions.metadata); + this.searchOptions = new VocabularyFindOptions( + this.model.vocabularyOptions.scope, + this.model.vocabularyOptions.name, + this.model.vocabularyOptions.metadata); } this.chips = new Chips( @@ -166,7 +167,7 @@ export class DsDynamicTagComponent extends DynamicFormControlComponent implement } private addTagsToChips() { - if (hasValue(this.currentValue) && (!this.hasAuthority || !this.model.authorityOptions.closed)) { + if (hasValue(this.currentValue) && (!this.hasAuthority || !this.model.vocabularyOptions.closed)) { let res: string[] = []; res = this.currentValue.split(','); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.spec.ts index f31f0eeff9..48456230bf 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.spec.ts @@ -10,10 +10,9 @@ import { DynamicFormLayoutService, DynamicFormsCoreModule, DynamicFormValidation import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; -import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; -import { AuthorityServiceStub } from '../../../../../testing/authority-service.stub'; -import { GlobalConfig } from '../../../../../../../config/global-config.interface'; +import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub'; import { DsDynamicTypeaheadComponent } from './dynamic-typeahead.component'; import { DynamicTypeaheadModel } from './dynamic-typeahead.model'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; @@ -31,12 +30,12 @@ function init() { }); TYPEAHEAD_TEST_MODEL_CONFIG = { - authorityOptions: { + vocabularyOptions: { closed: false, metadata: 'typeahead', name: 'EVENTAuthority', scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' - } as AuthorityOptions, + } as VocabularyOptions, disabled: false, id: 'typeahead', label: 'Conference', @@ -49,6 +48,7 @@ function init() { value: undefined }; } + describe('DsDynamicTypeaheadComponent test suite', () => { let testComp: TestComponent; @@ -59,7 +59,7 @@ describe('DsDynamicTypeaheadComponent test suite', () => { // async beforeEach beforeEach(async(() => { - const authorityServiceStub = new AuthorityServiceStub(); + const vocabularyServiceStub = new VocabularyServiceStub(); init(); TestBed.configureTestingModule({ imports: [ @@ -79,7 +79,7 @@ describe('DsDynamicTypeaheadComponent test suite', () => { providers: [ ChangeDetectorRef, DsDynamicTypeaheadComponent, - { provide: AuthorityService, useValue: authorityServiceStub }, + { provide: VocabularyService, useValue: vocabularyServiceStub }, { provide: DynamicFormLayoutService, useValue: {} }, { provide: DynamicFormValidationService, useValue: {} } ], @@ -134,17 +134,17 @@ describe('DsDynamicTypeaheadComponent test suite', () => { it('should search when 3+ characters typed', fakeAsync(() => { - spyOn((typeaheadComp as any).authorityService, 'getEntriesByName').and.callThrough(); + spyOn((typeaheadComp as any).vocabularyService, 'getVocabularyEntries').and.callThrough(); typeaheadComp.search(observableOf('test')).subscribe(); tick(300); typeaheadFixture.detectChanges(); - expect((typeaheadComp as any).authorityService.getEntriesByName).toHaveBeenCalled(); + expect((typeaheadComp as any).vocabularyService.getVocabularyEntries).toHaveBeenCalled(); })); - it('should set model.value on input type when AuthorityOptions.closed is false', () => { + it('should set model.value on input type when VocabularyOptions.closed is false', () => { const inputDe = typeaheadFixture.debugElement.query(By.css('input.form-control')); const inputElement = inputDe.nativeElement; @@ -155,8 +155,8 @@ describe('DsDynamicTypeaheadComponent test suite', () => { }); - it('should not set model.value on input type when AuthorityOptions.closed is true', () => { - typeaheadComp.model.authorityOptions.closed = true; + it('should not set model.value on input type when VocabularyOptions.closed is true', () => { + typeaheadComp.model.vocabularyOptions.closed = true; typeaheadFixture.detectChanges(); const inputDe = typeaheadFixture.debugElement.query(By.css('input.form-control')); const inputElement = inputDe.nativeElement; @@ -184,18 +184,18 @@ describe('DsDynamicTypeaheadComponent test suite', () => { expect(typeaheadComp.blur.emit).not.toHaveBeenCalled(); }); - it('should emit change Event onBlur when AuthorityOptions.closed is false and inputValue is changed', () => { + it('should emit change Event onBlur when VocabularyOptions.closed is false and inputValue is changed', () => { typeaheadComp.inputValue = 'test value'; typeaheadFixture.detectChanges(); spyOn(typeaheadComp.blur, 'emit'); spyOn(typeaheadComp.change, 'emit'); spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false); - typeaheadComp.onBlur(new Event('blur', )); + typeaheadComp.onBlur(new Event('blur',)); expect(typeaheadComp.change.emit).toHaveBeenCalled(); expect(typeaheadComp.blur.emit).toHaveBeenCalled(); }); - it('should not emit change Event onBlur when AuthorityOptions.closed is false and inputValue is not changed', () => { + it('should not emit change Event onBlur when VocabularyOptions.closed is false and inputValue is not changed', () => { typeaheadComp.inputValue = 'test value'; typeaheadComp.model = new DynamicTypeaheadModel(TYPEAHEAD_TEST_MODEL_CONFIG); (typeaheadComp.model as any).value = 'test value'; @@ -203,12 +203,12 @@ describe('DsDynamicTypeaheadComponent test suite', () => { spyOn(typeaheadComp.blur, 'emit'); spyOn(typeaheadComp.change, 'emit'); spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false); - typeaheadComp.onBlur(new Event('blur', )); + typeaheadComp.onBlur(new Event('blur',)); expect(typeaheadComp.change.emit).not.toHaveBeenCalled(); expect(typeaheadComp.blur.emit).toHaveBeenCalled(); }); - it('should not emit change Event onBlur when AuthorityOptions.closed is false and inputValue is null', () => { + it('should not emit change Event onBlur when VocabularyOptions.closed is false and inputValue is null', () => { typeaheadComp.inputValue = null; typeaheadComp.model = new DynamicTypeaheadModel(TYPEAHEAD_TEST_MODEL_CONFIG); (typeaheadComp.model as any).value = 'test value'; @@ -216,7 +216,7 @@ describe('DsDynamicTypeaheadComponent test suite', () => { spyOn(typeaheadComp.blur, 'emit'); spyOn(typeaheadComp.change, 'emit'); spyOn(typeaheadComp.instance, 'isPopupOpen').and.returnValue(false); - typeaheadComp.onBlur(new Event('blur', )); + typeaheadComp.onBlur(new Event('blur',)); expect(typeaheadComp.change.emit).not.toHaveBeenCalled(); expect(typeaheadComp.blur.emit).toHaveBeenCalled(); }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.ts index 791704a7ca..67030682a5 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component.ts @@ -10,12 +10,16 @@ import { catchError, debounceTime, distinctUntilChanged, filter, map, merge, swi import { Observable, of as observableOf, Subject } from 'rxjs'; import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; -import { AuthorityService } from '../../../../../../core/integration/authority.service'; +import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; import { DynamicTypeaheadModel } from './dynamic-typeahead.model'; -import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model'; +import { VocabularyFindOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; import { isEmpty, isNotEmpty, isNotNull } from '../../../../../empty.util'; import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model'; -import { ConfidenceType } from '../../../../../../core/integration/models/confidence-type'; +import { ConfidenceType } from '../../../../../../core/shared/confidence-type'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../../core/shared/operators'; +import { PaginatedList } from '../../../../../../core/data/paginated-list'; +import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model'; +import { PageInfo } from '../../../../../../core/shared/page-info.model'; @Component({ selector: 'ds-dynamic-typeahead', @@ -31,16 +35,24 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp @Output() change: EventEmitter = new EventEmitter(); @Output() focus: EventEmitter = new EventEmitter(); - @ViewChild('instance', {static: false}) instance: NgbTypeahead; + @ViewChild('instance', { static: false }) instance: NgbTypeahead; searching = false; - searchOptions: IntegrationSearchOptions; + searchOptions: VocabularyFindOptions; searchFailed = false; hideSearchingWhenUnsubscribed$ = new Observable(() => () => this.changeSearchingStatus(false)); click$ = new Subject(); currentValue: any; inputValue: any; + constructor(private vocabularyService: VocabularyService, + private cdr: ChangeDetectorRef, + protected layoutService: DynamicFormLayoutService, + protected validationService: DynamicFormValidationService + ) { + super(layoutService, validationService); + } + formatter = (x: { display: string }) => { return (typeof x === 'object') ? x.display : x }; @@ -53,44 +65,33 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp tap(() => this.changeSearchingStatus(true)), switchMap((term) => { if (term === '' || term.length < this.model.minChars) { - return observableOf({list: []}); + return observableOf({ list: [] }); } else { this.searchOptions.query = term; - return this.authorityService.getEntriesByName(this.searchOptions).pipe( - map((authorities) => { - // @TODO Pagination for authority is not working, to refactor when it will be fixed - return { - list: authorities.payload, - pageInfo: authorities.pageInfo - }; - }), + return this.vocabularyService.getVocabularyEntries(this.searchOptions).pipe( + getFirstSucceededRemoteDataPayload(), tap(() => this.searchFailed = false), catchError(() => { this.searchFailed = true; - return observableOf({list: []}); + return observableOf(new PaginatedList( + new PageInfo(), + [] + )); })); } }), - map((results) => results.list), + map((list: PaginatedList) => list.page), tap(() => this.changeSearchingStatus(false)), merge(this.hideSearchingWhenUnsubscribed$) ) }; - constructor(private authorityService: AuthorityService, - private cdr: ChangeDetectorRef, - protected layoutService: DynamicFormLayoutService, - protected validationService: DynamicFormValidationService - ) { - super(layoutService, validationService); - } - ngOnInit() { this.currentValue = this.model.value; - this.searchOptions = new IntegrationSearchOptions( - this.model.authorityOptions.scope, - this.model.authorityOptions.name, - this.model.authorityOptions.metadata); + this.searchOptions = new VocabularyFindOptions( + this.model.vocabularyOptions.scope, + this.model.vocabularyOptions.name, + this.model.vocabularyOptions.metadata); this.group.get(this.model.id).valueChanges.pipe( filter((value) => this.currentValue !== value)) .subscribe((value) => { @@ -104,14 +105,14 @@ export class DsDynamicTypeaheadComponent extends DynamicFormControlComponent imp } onInput(event) { - if (!this.model.authorityOptions.closed && isNotEmpty(event.target.value)) { + if (!this.model.vocabularyOptions.closed && isNotEmpty(event.target.value)) { this.inputValue = new FormFieldMetadataValueObject(event.target.value); } } onBlur(event: Event) { if (!this.instance.isPopupOpen()) { - if (!this.model.authorityOptions.closed && isNotEmpty(this.inputValue)) { + if (!this.model.vocabularyOptions.closed && isNotEmpty(this.inputValue)) { if (isNotNull(this.inputValue) && this.model.value !== this.inputValue) { this.model.valueUpdates.next(this.inputValue); this.change.emit(this.inputValue); diff --git a/src/app/shared/form/builder/form-builder.service.spec.ts b/src/app/shared/form/builder/form-builder.service.spec.ts index 972abb68b5..434fe6a2e1 100644 --- a/src/app/shared/form/builder/form-builder.service.spec.ts +++ b/src/app/shared/form/builder/form-builder.service.spec.ts @@ -36,7 +36,7 @@ import { DynamicLookupModel } from './ds-dynamic-form-ui/models/lookup/dynamic-l import { DynamicDsDatePickerModel } from './ds-dynamic-form-ui/models/date-picker/date-picker.model'; import { DynamicTypeaheadModel } from './ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.model'; import { DynamicListRadioGroupModel } from './ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model'; -import { AuthorityOptions } from '../../../core/integration/models/authority-options.model'; +import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model'; import { FormFieldModel } from './models/form-field.model'; import { SubmissionFormsModel @@ -78,7 +78,7 @@ describe('FormBuilderService test suite', () => { ] }); - const authorityOptions: AuthorityOptions = { + const vocabularyOptions: VocabularyOptions = { closed: false, metadata: 'list', name: 'type_programme', @@ -197,13 +197,13 @@ describe('FormBuilderService test suite', () => { new DynamicTypeaheadModel({id: 'testTypeahead', repeatable: false, metadataFields: [], submissionId: '1234'}), - new DynamicScrollableDropdownModel({id: 'testScrollableDropdown', authorityOptions: authorityOptions, repeatable: false, metadataFields: [], submissionId: '1234'}), + new DynamicScrollableDropdownModel({id: 'testScrollableDropdown', vocabularyOptions: vocabularyOptions, repeatable: false, metadataFields: [], submissionId: '1234'}), new DynamicTagModel({id: 'testTag', repeatable: false, metadataFields: [], submissionId: '1234'}), - new DynamicListCheckboxGroupModel({id: 'testCheckboxList', authorityOptions: authorityOptions, repeatable: true}), + new DynamicListCheckboxGroupModel({id: 'testCheckboxList', vocabularyOptions: vocabularyOptions, repeatable: true}), - new DynamicListRadioGroupModel({id: 'testRadioList', authorityOptions: authorityOptions, repeatable: false}), + new DynamicListRadioGroupModel({id: 'testRadioList', vocabularyOptions: vocabularyOptions, repeatable: false}), new DynamicRelationGroupModel({ submissionId, @@ -218,7 +218,7 @@ describe('FormBuilderService test suite', () => { mandatoryMessage: 'Required field!', repeatable: false, selectableMetadata: [{ - authority: 'RPAuthority', + controlledVocabulary: 'RPAuthority', closed: false, metadata: 'dc.contributor.author' }], @@ -232,7 +232,7 @@ describe('FormBuilderService test suite', () => { mandatory: 'false', repeatable: false, selectableMetadata: [{ - authority: 'OUAuthority', + controlledVocabulary: 'OUAuthority', closed: false, metadata: 'local.contributor.affiliation' }] @@ -284,7 +284,7 @@ describe('FormBuilderService test suite', () => { selectableMetadata: [ { metadata: 'journal', - authority: 'JOURNALAuthority', + controlledVocabulary: 'JOURNALAuthority', closed: false } ], @@ -364,7 +364,7 @@ describe('FormBuilderService test suite', () => { selectableMetadata: [ { metadata: 'conference', - authority: 'EVENTAuthority', + controlledVocabulary: 'EVENTAuthority', closed: false } ], diff --git a/src/app/shared/form/builder/models/form-field-metadata-value.model.ts b/src/app/shared/form/builder/models/form-field-metadata-value.model.ts index 45489e3618..ff0afe97fd 100644 --- a/src/app/shared/form/builder/models/form-field-metadata-value.model.ts +++ b/src/app/shared/form/builder/models/form-field-metadata-value.model.ts @@ -1,5 +1,5 @@ import { isEmpty, isNotEmpty, isNotNull } from '../../../empty.util'; -import { ConfidenceType } from '../../../../core/integration/models/confidence-type'; +import { ConfidenceType } from '../../../../core/shared/confidence-type'; import { PLACEHOLDER_PARENT_METADATA } from '../ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { MetadataValueInterface } from '../../../../core/shared/metadata.models'; @@ -7,15 +7,16 @@ export interface OtherInformation { [name: string]: string } +/** + * A class representing a specific input-form field's value + */ export class FormFieldMetadataValueObject implements MetadataValueInterface { - metadata?: string; value: any; display: string; language: any; authority: string; confidence: ConfidenceType; place: number; - closed: boolean; label: string; otherInformation: OtherInformation; @@ -42,9 +43,6 @@ export class FormFieldMetadataValueObject implements MetadataValueInterface { } this.place = place; - if (isNotEmpty(metadata)) { - this.metadata = metadata; - } this.otherInformation = otherInformation; } diff --git a/src/app/shared/form/builder/models/form-field.model.ts b/src/app/shared/form/builder/models/form-field.model.ts index 718b3f4f0d..95ee980aeb 100644 --- a/src/app/shared/form/builder/models/form-field.model.ts +++ b/src/app/shared/form/builder/models/form-field.model.ts @@ -1,50 +1,121 @@ import { autoserialize } from 'cerialize'; + import { LanguageCode } from './form-field-language-value.model'; -import { FormFieldMetadataValueObject } from './form-field-metadata-value.model'; import { RelationshipOptions } from './relationship-options.model'; import { FormRowModel } from '../../../../core/config/models/config-submission-form.model'; +/** + * Representing SelectableMetadata properties + */ +export interface SelectableMetadata { + /** + * The key of the metadata field to use to store the input + */ + metadata: string; + + /** + * The label of the metadata field to use to store the input + */ + label: string; + + /** + * The name of the controlled vocabulary used to retrieve value for the input see controlled vocabularies + */ + controlledVocabulary: string; + + /** + * A boolean representing if value is closely related to the controlled vocabulary entry or not + */ + closed: boolean; +} + +/** + * A class representing a specific input-form field + */ export class FormFieldModel { + /** + * The hints for this metadata field to display on form + */ @autoserialize hints: string; + /** + * The label for this metadata field to display on form + */ @autoserialize label: string; + /** + * The languages available for this metadata field to display on form + */ @autoserialize languageCodes: LanguageCode[]; + /** + * The error message for this metadata field to display on form in case of field is required + */ @autoserialize mandatoryMessage: string; + /** + * Representing if this metadata field is mandatory or not + */ @autoserialize mandatory: string; + /** + * Representing if this metadata field is repeatable or not + */ @autoserialize repeatable: boolean; + /** + * Containing additional properties for this metadata field + */ @autoserialize input: { + /** + * Representing the type for this metadata field + */ type: string; + + /** + * Containing regex to use for field validation + */ regex?: string; }; + /** + * Representing additional vocabulary configuration for this metadata field + */ @autoserialize - selectableMetadata: FormFieldMetadataValueObject[]; + selectableMetadata: SelectableMetadata[]; + /** + * Representing additional relationship configuration for this metadata field + */ @autoserialize selectableRelationship: RelationshipOptions; @autoserialize rows: FormRowModel[]; + /** + * Representing the scope for this metadata field + */ @autoserialize scope: string; + /** + * Containing additional css classes for this metadata field to use on form + */ @autoserialize style: string; + /** + * Containing the value for this metadata field + */ @autoserialize value: any; } diff --git a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts index efa4f3cdb5..b9adf3ed65 100644 --- a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('DateFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, - authorityUuid: null + collectionUUID: null }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts b/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts index c885b737c2..e7e64feb7b 100644 --- a/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('DisabledFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, - authorityUuid: null + collectionUUID: null }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts index 8dbd68e05a..82d2aeac63 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts @@ -11,7 +11,7 @@ describe('DropdownFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { @@ -26,7 +26,7 @@ describe('DropdownFieldParser test suite', () => { selectableMetadata: [ { metadata: 'type', - authority: 'common_types_dataset', + controlledVocabulary: 'common_types_dataset', closed: false } ], @@ -50,7 +50,7 @@ describe('DropdownFieldParser test suite', () => { }); it('should throw when authority is not passed', () => { - field.selectableMetadata[0].authority = null; + field.selectableMetadata[0].controlledVocabulary = null; const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions); expect(() => parser.parse()) diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts index 4816a2a073..7fb5fb206c 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts @@ -31,8 +31,8 @@ export class DropdownFieldParser extends FieldParser { const dropdownModelConfig: DynamicScrollableDropdownModelConfig = this.initModel(null, label); let layout: DynamicFormControlLayout; - if (isNotEmpty(this.configData.selectableMetadata[0].authority)) { - this.setAuthorityOptions(dropdownModelConfig, this.parserOptions.authorityUuid); + if (isNotEmpty(this.configData.selectableMetadata[0].controlledVocabulary)) { + this.setVocabularyOptions(dropdownModelConfig, this.parserOptions.collectionUUID); if (isNotEmpty(fieldValue)) { dropdownModelConfig.value = fieldValue; } @@ -47,7 +47,7 @@ export class DropdownFieldParser extends FieldParser { const dropdownModel = new DynamicScrollableDropdownModel(dropdownModelConfig, layout); return dropdownModel; } else { - throw Error(`Authority name is not available. Please check the form configuration file.`); + throw Error(`Controlled Vocabulary name is not available. Please check the form configuration file.`); } } } diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index f218d442e1..540c9c7b4e 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -1,19 +1,20 @@ import { Inject, InjectionToken } from '@angular/core'; -import { hasValue, isNotEmpty, isNotNull, isNotUndefined, isEmpty } from '../../../empty.util'; -import { FormFieldModel } from '../models/form-field.model'; import { uniqueId } from 'lodash'; +import { DynamicFormControlLayout } from '@ng-dynamic-forms/core'; + +import { hasValue, isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../../empty.util'; +import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DynamicRowArrayModel, DynamicRowArrayModelConfig } from '../ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-form-ui/models/ds-dynamic-input.model'; -import { DynamicFormControlLayout } from '@ng-dynamic-forms/core'; import { setLayout } from './parser.utils'; -import { AuthorityOptions } from '../../../../core/integration/models/authority-options.model'; import { ParserOptions } from './parser-options'; import { RelationshipOptions } from '../models/relationship-options.model'; +import { VocabularyOptions } from '../../../../core/submission/vocabularies/models/vocabulary-options.model'; export const SUBMISSION_ID: InjectionToken = new InjectionToken('submissionId'); export const CONFIG_DATA: InjectionToken = new InjectionToken('configData'); @@ -49,7 +50,7 @@ export abstract class FieldParser { label: this.configData.label, initialCount: this.getInitArrayIndex(), notRepeatable: !this.configData.repeatable, - required: JSON.parse( this.configData.mandatory), + required: JSON.parse(this.configData.mandatory), groupFactory: () => { let model; if ((arrayCounter === 0)) { @@ -92,6 +93,52 @@ export abstract class FieldParser { } } + public setVocabularyOptions(controlModel, scope) { + if (isNotEmpty(this.configData.selectableMetadata) && isNotEmpty(this.configData.selectableMetadata[0].controlledVocabulary)) { + controlModel.vocabularyOptions = new VocabularyOptions( + this.configData.selectableMetadata[0].controlledVocabulary, + this.configData.selectableMetadata[0].metadata, + scope, + this.configData.selectableMetadata[0].closed + ) + } + } + + public setValues(modelConfig: DsDynamicInputModelConfig, fieldValue: any, forceValueAsObj: boolean = false, groupModel?: boolean) { + if (isNotEmpty(fieldValue)) { + if (groupModel) { + // Array, values is an array + modelConfig.value = this.getInitGroupValues(); + if (Array.isArray(modelConfig.value) && modelConfig.value.length > 0 && modelConfig.value[0].language) { + // Array Item has language, ex. AuthorityModel + modelConfig.language = modelConfig.value[0].language; + } + return; + } + + if (typeof fieldValue === 'object') { + modelConfig.language = fieldValue.language; + if (forceValueAsObj) { + modelConfig.value = fieldValue; + } else { + modelConfig.value = fieldValue.value; + } + } else { + if (forceValueAsObj) { + // If value isn't an instance of FormFieldMetadataValueObject instantiate it + modelConfig.value = new FormFieldMetadataValueObject(fieldValue); + } else { + if (typeof fieldValue === 'string') { + // Case only string + modelConfig.value = fieldValue; + } + } + } + } + + return modelConfig; + } + protected getInitValueCount(index = 0, fieldId?): number { const fieldIds = fieldId || this.getAllFieldIds(); if (isNotEmpty(this.initFormValues) && isNotNull(fieldIds) && fieldIds.length === 1 && this.initFormValues.hasOwnProperty(fieldIds[0])) { @@ -135,7 +182,7 @@ export abstract class FieldParser { fieldIds.forEach((id) => { if (this.initFormValues.hasOwnProperty(id)) { const valueObj: FormFieldMetadataValueObject = Object.assign(new FormFieldMetadataValueObject(), this.initFormValues[id][innerIndex]); - valueObj.metadata = id; + // valueObj.metadata = id; // valueObj.value = this.initFormValues[id][innerIndex]; values.push(valueObj); } @@ -224,14 +271,6 @@ export abstract class FieldParser { if (this.configData.languageCodes && this.configData.languageCodes.length > 0) { (controlModel as DsDynamicInputModel).languageCodes = this.configData.languageCodes; } - /* (controlModel as DsDynamicInputModel).languageCodes = [{ - display: 'English', - code: 'en_US' - }, - { - display: 'Italian', - code: 'it_IT' - }];*/ return controlModel; } @@ -278,50 +317,4 @@ export abstract class FieldParser { } } - public setAuthorityOptions(controlModel, authorityUuid) { - if (isNotEmpty(this.configData.selectableMetadata) && isNotEmpty(this.configData.selectableMetadata[0].authority)) { - controlModel.authorityOptions = new AuthorityOptions( - this.configData.selectableMetadata[0].authority, - this.configData.selectableMetadata[0].metadata, - authorityUuid, - this.configData.selectableMetadata[0].closed - ) - } - } - - public setValues(modelConfig: DsDynamicInputModelConfig, fieldValue: any, forceValueAsObj: boolean = false, groupModel?: boolean) { - if (isNotEmpty(fieldValue)) { - if (groupModel) { - // Array, values is an array - modelConfig.value = this.getInitGroupValues(); - if (Array.isArray(modelConfig.value) && modelConfig.value.length > 0 && modelConfig.value[0].language) { - // Array Item has language, ex. AuthorityModel - modelConfig.language = modelConfig.value[0].language; - } - return; - } - - if (typeof fieldValue === 'object') { - modelConfig.language = fieldValue.language; - if (forceValueAsObj) { - modelConfig.value = fieldValue; - } else { - modelConfig.value = fieldValue.value; - } - } else { - if (forceValueAsObj) { - // If value isn't an instance of FormFieldMetadataValueObject instantiate it - modelConfig.value = new FormFieldMetadataValueObject(fieldValue); - } else { - if (typeof fieldValue === 'string') { - // Case only string - modelConfig.value = fieldValue; - } - } - } - } - - return modelConfig; - } - } diff --git a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts index fab5ec3888..8a05b169fd 100644 --- a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts @@ -13,7 +13,7 @@ describe('ListFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { @@ -28,7 +28,7 @@ describe('ListFieldParser test suite', () => { selectableMetadata: [ { metadata: 'type', - authority: 'type_programme', + controlledVocabulary: 'type_programme', closed: false } ], diff --git a/src/app/shared/form/builder/parsers/list-field-parser.ts b/src/app/shared/form/builder/parsers/list-field-parser.ts index 273c9d1665..d3c158f4b8 100644 --- a/src/app/shared/form/builder/parsers/list-field-parser.ts +++ b/src/app/shared/form/builder/parsers/list-field-parser.ts @@ -1,19 +1,19 @@ import { FieldParser } from './field-parser'; import { isNotEmpty } from '../../../empty.util'; -import { IntegrationSearchOptions } from '../../../../core/integration/models/integration-options.model'; +import { VocabularyFindOptions } from '../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DynamicListCheckboxGroupModel } from '../ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model'; import { DynamicListRadioGroupModel } from '../ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model'; export class ListFieldParser extends FieldParser { - searchOptions: IntegrationSearchOptions; + searchOptions: VocabularyFindOptions; public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { const listModelConfig = this.initModel(null, label); listModelConfig.repeatable = this.configData.repeatable; - if (this.configData.selectableMetadata[0].authority - && this.configData.selectableMetadata[0].authority.length > 0) { + if (this.configData.selectableMetadata[0].controlledVocabulary + && this.configData.selectableMetadata[0].controlledVocabulary.length > 0) { if (isNotEmpty(this.getInitGroupValues())) { listModelConfig.value = []; @@ -26,7 +26,7 @@ export class ListFieldParser extends FieldParser { } }); } - this.setAuthorityOptions(listModelConfig, this.parserOptions.authorityUuid); + this.setVocabularyOptions(listModelConfig, this.parserOptions.collectionUUID); } let listModel; diff --git a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts index 5e14e0c013..87cee9d950 100644 --- a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('LookupFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { @@ -27,7 +27,7 @@ describe('LookupFieldParser test suite', () => { selectableMetadata: [ { metadata: 'journal', - authority: 'JOURNALAuthority', + controlledVocabulary: 'JOURNALAuthority', closed: false } ], diff --git a/src/app/shared/form/builder/parsers/lookup-field-parser.ts b/src/app/shared/form/builder/parsers/lookup-field-parser.ts index 9e9c434c4f..6e28194dad 100644 --- a/src/app/shared/form/builder/parsers/lookup-field-parser.ts +++ b/src/app/shared/form/builder/parsers/lookup-field-parser.ts @@ -5,10 +5,10 @@ import { FormFieldMetadataValueObject } from '../models/form-field-metadata-valu export class LookupFieldParser extends FieldParser { public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { - if (this.configData.selectableMetadata[0].authority) { + if (this.configData.selectableMetadata[0].controlledVocabulary) { const lookupModelConfig: DynamicLookupModelConfig = this.initModel(null, label); - this.setAuthorityOptions(lookupModelConfig, this.parserOptions.authorityUuid); + this.setVocabularyOptions(lookupModelConfig, this.parserOptions.collectionUUID); this.setValues(lookupModelConfig, fieldValue, true); diff --git a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts index adc1e90166..3d02b6952e 100644 --- a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('LookupNameFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { @@ -27,7 +27,7 @@ describe('LookupNameFieldParser test suite', () => { selectableMetadata: [ { metadata: 'author', - authority: 'RPAuthority', + controlledVocabulary: 'RPAuthority', closed: false } ], diff --git a/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts b/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts index 684e06bcb6..26806742c8 100644 --- a/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts +++ b/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts @@ -8,10 +8,10 @@ import { FormFieldMetadataValueObject } from '../models/form-field-metadata-valu export class LookupNameFieldParser extends FieldParser { public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { - if (this.configData.selectableMetadata[0].authority) { + if (this.configData.selectableMetadata[0].controlledVocabulary) { const lookupModelConfig: DynamicLookupNameModelConfig = this.initModel(null, label); - this.setAuthorityOptions(lookupModelConfig, this.parserOptions.authorityUuid); + this.setVocabularyOptions(lookupModelConfig, this.parserOptions.collectionUUID); this.setValues(lookupModelConfig, fieldValue, true); diff --git a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts index 1b0c637030..363ff40926 100644 --- a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts @@ -14,7 +14,7 @@ describe('NameFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts index 4668b3017d..c2d1b6f565 100644 --- a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts @@ -15,7 +15,7 @@ describe('OneboxFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { @@ -28,7 +28,7 @@ describe('OneboxFieldParser test suite', () => { selectableMetadata: [ { metadata: 'title', - authority: 'EVENTAuthority', + controlledVocabulary: 'EVENTAuthority', closed: false } ], diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.ts index d69c9d4677..598918ac2e 100644 --- a/src/app/shared/form/builder/parsers/onebox-field-parser.ts +++ b/src/app/shared/form/builder/parsers/onebox-field-parser.ts @@ -75,9 +75,9 @@ export class OneboxFieldParser extends FieldParser { inputSelectGroup.group.push(new DsDynamicInputModel(inputModelConfig, clsInput)); return new DynamicQualdropModel(inputSelectGroup, clsGroup); - } else if (this.configData.selectableMetadata[0].authority) { + } else if (this.configData.selectableMetadata[0].controlledVocabulary) { const typeaheadModelConfig: DsDynamicTypeaheadModelConfig = this.initModel(null, label); - this.setAuthorityOptions(typeaheadModelConfig, this.parserOptions.authorityUuid); + this.setVocabularyOptions(typeaheadModelConfig, this.parserOptions.collectionUUID); this.setValues(typeaheadModelConfig, fieldValue, true); return new DynamicTypeaheadModel(typeaheadModelConfig); diff --git a/src/app/shared/form/builder/parsers/parser-options.ts b/src/app/shared/form/builder/parsers/parser-options.ts index f96ce0f2f3..e8ac4b49cd 100644 --- a/src/app/shared/form/builder/parsers/parser-options.ts +++ b/src/app/shared/form/builder/parsers/parser-options.ts @@ -1,5 +1,5 @@ export interface ParserOptions { readOnly: boolean; submissionScope: string; - authorityUuid: string + collectionUUID: string } diff --git a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts index 84f3df0365..111193a637 100644 --- a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('RelationGroupFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: 'WORKSPACE' + collectionUUID: 'WORKSPACE' }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/relation-group-field-parser.ts b/src/app/shared/form/builder/parsers/relation-group-field-parser.ts index 01699d9e78..99dfcf777a 100644 --- a/src/app/shared/form/builder/parsers/relation-group-field-parser.ts +++ b/src/app/shared/form/builder/parsers/relation-group-field-parser.ts @@ -16,7 +16,7 @@ export class RelationGroupFieldParser extends FieldParser { const modelConfiguration: DynamicRelationGroupModelConfig = this.initModel(null, label); modelConfiguration.submissionId = this.submissionId; - modelConfiguration.scopeUUID = this.parserOptions.authorityUuid; + modelConfiguration.scopeUUID = this.parserOptions.collectionUUID; modelConfiguration.submissionScope = this.parserOptions.submissionScope; if (this.configData && this.configData.rows && this.configData.rows.length > 0) { modelConfiguration.formConfiguration = this.configData.rows; diff --git a/src/app/shared/form/builder/parsers/row-parser.spec.ts b/src/app/shared/form/builder/parsers/row-parser.spec.ts index 435c6a6426..63f343e60d 100644 --- a/src/app/shared/form/builder/parsers/row-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/row-parser.spec.ts @@ -35,7 +35,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'journal', - authority: 'JOURNALAuthority', + controlledVocabulary: 'JOURNALAuthority', closed: false } ], @@ -83,7 +83,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'title', - authority: 'EVENTAuthority', + controlledVocabulary: 'EVENTAuthority', closed: false } ], @@ -103,7 +103,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'title', - authority: 'EVENTAuthority', + controlledVocabulary: 'EVENTAuthority', closed: false } ], @@ -119,7 +119,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'otherTitle', - authority: 'EVENTAuthority', + controlledVocabulary: 'EVENTAuthority', closed: false } ], @@ -141,7 +141,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'type', - authority: 'common_types_dataset', + controlledVocabulary: 'common_types_dataset', closed: false } ], @@ -176,7 +176,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'author', - authority: 'RPAuthority', + controlledVocabulary: 'RPAuthority', closed: false } ], @@ -198,7 +198,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'type', - authority: 'type_programme', + controlledVocabulary: 'type_programme', closed: false } ], @@ -241,7 +241,7 @@ describe('RowParser test suite', () => { selectableMetadata: [ { metadata: 'subject', - authority: 'JOURNALAuthority', + controlledVocabulary: 'JOURNALAuthority', closed: false } ], diff --git a/src/app/shared/form/builder/parsers/row-parser.ts b/src/app/shared/form/builder/parsers/row-parser.ts index 4938b9859e..a7ea023569 100644 --- a/src/app/shared/form/builder/parsers/row-parser.ts +++ b/src/app/shared/form/builder/parsers/row-parser.ts @@ -5,7 +5,7 @@ import { } from '@ng-dynamic-forms/core'; import { uniqueId } from 'lodash'; -import { IntegrationSearchOptions } from '../../../../core/integration/models/integration-options.model'; +import { VocabularyFindOptions } from '../../../../core/submission/vocabularies/models/vocabulary-find-options.model'; import { isEmpty } from '../../../empty.util'; import { DynamicRowGroupModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from '../ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; @@ -48,7 +48,7 @@ export class RowParser { group: [], }; - const authorityOptions = new IntegrationSearchOptions(scopeUUID); + const vocabularyOptions = new VocabularyFindOptions(scopeUUID); const scopedFields: FormFieldModel[] = this.filterScopedFields(rowData.fields, submissionScope); @@ -58,7 +58,7 @@ export class RowParser { const parserOptions: ParserOptions = { readOnly: readOnly, submissionScope: submissionScope, - authorityUuid: authorityOptions.uuid + collectionUUID: vocabularyOptions.collection }; // Iterate over row's fields diff --git a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts index ceb4e96320..ca58dfb50a 100644 --- a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('SeriesFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts index 90449e62e5..7c63235f67 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('TagFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: 'testScopeUUID', - authorityUuid: null + collectionUUID: null }; beforeEach(() => { @@ -27,7 +27,7 @@ describe('TagFieldParser test suite', () => { selectableMetadata: [ { metadata: 'subject', - authority: 'JOURNALAuthority', + controlledVocabulary: 'JOURNALAuthority', closed: false } ], diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.ts b/src/app/shared/form/builder/parsers/tag-field-parser.ts index c1c39feb2b..08685e0e35 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.ts @@ -6,9 +6,9 @@ export class TagFieldParser extends FieldParser { public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { const tagModelConfig: DynamicTagModelConfig = this.initModel(null, label); - if (this.configData.selectableMetadata[0].authority - && this.configData.selectableMetadata[0].authority.length > 0) { - this.setAuthorityOptions(tagModelConfig, this.parserOptions.authorityUuid); + if (this.configData.selectableMetadata[0].controlledVocabulary + && this.configData.selectableMetadata[0].controlledVocabulary.length > 0) { + this.setVocabularyOptions(tagModelConfig, this.parserOptions.collectionUUID); } this.setValues(tagModelConfig, fieldValue, null, true); diff --git a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts index 167f126cf2..84acc9f239 100644 --- a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts @@ -12,7 +12,7 @@ describe('TextareaFieldParser test suite', () => { const parserOptions: ParserOptions = { readOnly: false, submissionScope: null, - authorityUuid: null + collectionUUID: null }; beforeEach(() => { diff --git a/src/app/shared/mocks/form-models.mock.ts b/src/app/shared/mocks/form-models.mock.ts index e4f9ec3131..1965f8d709 100644 --- a/src/app/shared/mocks/form-models.mock.ts +++ b/src/app/shared/mocks/form-models.mock.ts @@ -1,16 +1,19 @@ +import { DynamicSelectModel } from '@ng-dynamic-forms/core'; + import { DsDynamicInputModel } from '../form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { DynamicQualdropModel } from '../form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model'; -import { DynamicRowArrayModel, DynamicRowArrayModelConfig } from '../form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; -import { DynamicSelectModel } from '@ng-dynamic-forms/core'; +import { + DynamicRowArrayModel, + DynamicRowArrayModelConfig +} from '../form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { SubmissionScopeType } from '../../core/submission/submission-scope-type'; import { DynamicRelationGroupModel } from '../form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { FormFieldModel } from '../form/builder/models/form-field.model'; -import { AuthorityOptions } from '../../core/integration/models/authority-options.model'; -import { AuthorityValue } from '../../core/integration/models/authority.value'; +import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model'; +import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model'; import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model'; import { DynamicRowGroupModel } from '../form/builder/ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; import { FormRowModel } from '../../core/config/models/config-submission-form.model'; -import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; export const qualdropSelectConfig = { name: 'dc.identifier_QUALDROP_METADATA', @@ -87,7 +90,7 @@ export const MockRowArrayQualdropModel: DynamicRowArrayModel = new DynamicRowArr const mockFormRowModel = { fields: [ { - input: {type: 'lookup'}, + input: { type: 'lookup' }, label: 'Journal', mandatory: 'false', repeatable: false, @@ -95,14 +98,14 @@ const mockFormRowModel = { selectableMetadata: [ { metadata: 'journal', - authority: 'JOURNALAuthority', + controlledVocabulary: 'JOURNALAuthority', closed: false } ], languageCodes: [] } as FormFieldModel, { - input: {type: 'onebox'}, + input: { type: 'onebox' }, label: 'Issue', mandatory: 'false', repeatable: false, @@ -142,7 +145,7 @@ const relationGroupConfig = { export const MockRelationModel: DynamicRelationGroupModel = new DynamicRelationGroupModel(relationGroupConfig); export const inputWithLanguageAndAuthorityConfig = { - authorityOptions: new AuthorityOptions('testAuthority', 'testWithAuthority', 'scope'), + vocabularyOptions: new VocabularyOptions('testAuthority', 'testWithAuthority', 'scope'), languageCodes: [ { display: 'English', @@ -159,10 +162,10 @@ export const inputWithLanguageAndAuthorityConfig = { readOnly: false, disabled: false, repeatable: false, - value: { + value: { value: 'testWithLanguageAndAuthority', display: 'testWithLanguageAndAuthority', - id: 'testWithLanguageAndAuthority', + authority: 'testWithLanguageAndAuthority', }, submissionId: '1234', metadataFields: [] @@ -195,7 +198,7 @@ export const inputWithLanguageConfig = { export const mockInputWithLanguageModel = new DsDynamicInputModel(inputWithLanguageConfig); export const inputWithLanguageAndAuthorityArrayConfig = { - authorityOptions: new AuthorityOptions('testAuthority', 'testWithAuthority', 'scope'), + vocabularyOptions: new VocabularyOptions('testAuthority', 'testWithAuthority', 'scope'), languageCodes: [ { display: 'English', @@ -215,7 +218,7 @@ export const inputWithLanguageAndAuthorityArrayConfig = { value: [{ value: 'testLanguageAndAuthorityArray', display: 'testLanguageAndAuthorityArray', - id: 'testLanguageAndAuthorityArray', + authority: 'testLanguageAndAuthorityArray', }], submissionId: '1234', metadataFields: [] @@ -242,7 +245,11 @@ export const inputWithAuthorityValueConfig = { readOnly: false, disabled: false, repeatable: false, - value: Object.assign({}, new AuthorityValue(), { value: 'testWithAuthorityValue', id: 'testWithAuthorityValue', display: 'testWithAuthorityValue' }), + value: Object.assign({}, new VocabularyEntry(), { + value: 'testWithAuthorityValue', + authority: 'testWithAuthorityValue', + display: 'testWithAuthorityValue' + }), submissionId: '1234', metadataFields: [] }; @@ -255,7 +262,7 @@ export const inputWithObjectValueConfig = { readOnly: false, disabled: false, repeatable: false, - value: { value: 'testWithObjectValue', id: 'testWithObjectValue', display: 'testWithObjectValue' }, + value: { value: 'testWithObjectValue', authority: 'testWithObjectValue', display: 'testWithObjectValue' }, submissionId: '1234', metadataFields: [] }; diff --git a/src/app/shared/mocks/submission.mock.ts b/src/app/shared/mocks/submission.mock.ts index 082eec4c71..8965a17eef 100644 --- a/src/app/shared/mocks/submission.mock.ts +++ b/src/app/shared/mocks/submission.mock.ts @@ -1254,7 +1254,7 @@ export const mockUploadConfigResponse = { { metadata: 'dc.title', label: null, - authority: null, + controlledVocabulary: null, closed: null } ], @@ -1276,7 +1276,7 @@ export const mockUploadConfigResponse = { { metadata: 'dc.description', label: null, - authority: null, + controlledVocabulary: null, closed: null } ], diff --git a/src/app/shared/testing/authority-service.stub.ts b/src/app/shared/testing/authority-service.stub.ts deleted file mode 100644 index 3a5d31ab0d..0000000000 --- a/src/app/shared/testing/authority-service.stub.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {of as observableOf, Observable } from 'rxjs'; -import { IntegrationSearchOptions } from '../../core/integration/models/integration-options.model'; -import { IntegrationData } from '../../core/integration/integration-data'; -import { PageInfo } from '../../core/shared/page-info.model'; -import { AuthorityValue } from '../../core/integration/models/authority.value'; - -export class AuthorityServiceStub { - - private _payload = [ - Object.assign(new AuthorityValue(),{id: 1, display: 'one', value: 1}), - Object.assign(new AuthorityValue(),{id: 2, display: 'two', value: 2}), - ]; - - setNewPayload(payload) { - this._payload = payload; - } - - getEntriesByName(options: IntegrationSearchOptions) { - return observableOf(new IntegrationData(new PageInfo(), this._payload)); - } -} diff --git a/src/app/shared/testing/vocabulary-service.stub.ts b/src/app/shared/testing/vocabulary-service.stub.ts new file mode 100644 index 0000000000..15528653c2 --- /dev/null +++ b/src/app/shared/testing/vocabulary-service.stub.ts @@ -0,0 +1,29 @@ +import { Observable } from 'rxjs'; + +import { VocabularyFindOptions } from '../../core/submission/vocabularies/models/vocabulary-find-options.model'; +import { PageInfo } from '../../core/shared/page-info.model'; +import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; +import { RemoteData } from '../../core/data/remote-data'; + + +export class VocabularyServiceStub { + + private _payload = [ + Object.assign(new VocabularyEntry(),{authority: 1, display: 'one', value: 1}), + Object.assign(new VocabularyEntry(),{authority: 2, display: 'two', value: 2}), + ]; + + setNewPayload(payload) { + this._payload = payload; + } + + getList() { + return this._payload + } + + getVocabularyEntries(options: VocabularyFindOptions): Observable>> { + return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), this._payload)); + } +} diff --git a/src/app/submission/sections/form/section-form-operations.service.spec.ts b/src/app/submission/sections/form/section-form-operations.service.spec.ts index de8e7da7f9..bc1c17ddbd 100644 --- a/src/app/submission/sections/form/section-form-operations.service.spec.ts +++ b/src/app/submission/sections/form/section-form-operations.service.spec.ts @@ -27,7 +27,7 @@ import { mockRowGroupModel } from '../../../shared/mocks/form-models.mock'; import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; -import { AuthorityValue } from '../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model'; describe('SectionFormOperationsService test suite', () => { let formBuilderService: any; @@ -365,7 +365,7 @@ describe('SectionFormOperationsService test suite', () => { event = Object.assign({}, dynamicFormControlChangeEvent, { model: mockInputWithLanguageAndAuthorityModel }); - expectedValue = Object.assign(new AuthorityValue(), mockInputWithLanguageAndAuthorityModel.value, {language: mockInputWithLanguageAndAuthorityModel.language}); + expectedValue = Object.assign(new VocabularyEntry(), mockInputWithLanguageAndAuthorityModel.value, {language: mockInputWithLanguageAndAuthorityModel.language}); expect(service.getFieldValueFromChangeEvent(event)).toEqual(expectedValue); @@ -373,7 +373,7 @@ describe('SectionFormOperationsService test suite', () => { model: mockInputWithLanguageAndAuthorityArrayModel }); expectedValue = [ - Object.assign(new AuthorityValue(), mockInputWithLanguageAndAuthorityArrayModel.value[0], + Object.assign(new VocabularyEntry(), mockInputWithLanguageAndAuthorityArrayModel.value[0], { language: mockInputWithLanguageAndAuthorityArrayModel.language } ) ]; diff --git a/src/app/submission/sections/form/section-form-operations.service.ts b/src/app/submission/sections/form/section-form-operations.service.ts index 2d6b1c5477..a28536a0a7 100644 --- a/src/app/submission/sections/form/section-form-operations.service.ts +++ b/src/app/submission/sections/form/section-form-operations.service.ts @@ -15,11 +15,12 @@ import { FormFieldPreviousValueObject } from '../../../shared/form/builder/model import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; import { FormFieldLanguageValueObject } from '../../../shared/form/builder/models/form-field-language-value.model'; import { DsDynamicInputModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model'; -import { AuthorityValue } from '../../../core/integration/models/authority.value'; +import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; import { DynamicQualdropModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model'; import { DynamicRelationGroupModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; +import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; /** * The service handling all form section operations @@ -221,18 +222,19 @@ export class SectionFormOperationsService { if ((event.model as DsDynamicInputModel).hasAuthority) { if (Array.isArray(value)) { value.forEach((authority, index) => { - authority = Object.assign(new AuthorityValue(), authority, { language }); + authority = Object.assign(new VocabularyEntry(), authority, { language }); value[index] = authority; }); fieldValue = value; } else { - fieldValue = Object.assign(new AuthorityValue(), value, { language }); + fieldValue = Object.assign(new VocabularyEntry(), value, { language }); } } else { // Language without Authority (input, textArea) fieldValue = new FormFieldMetadataValueObject(value, language); } - } else if (value instanceof FormFieldLanguageValueObject || value instanceof AuthorityValue || isObject(value)) { + } else if (value instanceof FormFieldLanguageValueObject || value instanceof VocabularyEntry + || value instanceof VocabularyEntryDetail || isObject(value)) { fieldValue = value; } else { fieldValue = new FormFieldMetadataValueObject(value);