diff --git a/src/app/+admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts b/src/app/+admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts index 9d35ae5ff7..e5214977ca 100644 --- a/src/app/+admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts +++ b/src/app/+admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts @@ -13,8 +13,7 @@ import { MetadataSchema } from '../../../../core/metadata/metadataschema.model'; @Component({ selector: 'ds-metadata-schema-form', - templateUrl: './metadata-schema-form.component.html', - // styleUrls: ['./metadata-schema-form.component.css'] + templateUrl: './metadata-schema-form.component.html' }) export class MetadataSchemaFormComponent implements OnInit { diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts index 8c8c889739..fbabbb722d 100644 --- a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts @@ -6,11 +6,11 @@ import { RegistryService } from '../../../../core/registry/registry.service'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { Observable } from 'rxjs/internal/Observable'; import { MetadataField } from '../../../../core/metadata/metadatafield.model'; +import { take } from 'rxjs/operators'; @Component({ selector: 'ds-metadata-field-form', - templateUrl: './metadata-field-form.component.html', - // styleUrls: ['./metadata-field-form.component.css'] + templateUrl: './metadata-field-form.component.html' }) export class MetadataFieldFormComponent implements OnInit { @@ -60,7 +60,6 @@ export class MetadataFieldFormComponent implements OnInit { } ngOnInit() { - this.formGroup = this.formBuilderService.createFormGroup(this.formModel); this.registryService.getActiveMetadataField().subscribe((field) => { this.formGroup.patchValue({ @@ -74,36 +73,33 @@ export class MetadataFieldFormComponent implements OnInit { }); } - getMetadataField(): Observable { - return this.registryService.getActiveMetadataField(); - } - onCancel() { this.registryService.cancelEditMetadataField(); } onSubmit() { - this.registryService.getActiveMetadataField().subscribe( + this.registryService.getActiveMetadataField().pipe(take(1)).subscribe( (field) => { - + const values = { + schema: this.metadataSchema, + element: this.element.value, + qualifier: this.qualifier.value, + scopeNote: this.scopeNote.value + }; if (field == null) { - console.log('metadata field to create:'); - console.log('element: ' + this.element.value); - if (this.qualifier.value) { - console.log('qualifier: ' + this.qualifier.value); - } - if (this.scopeNote.value) { - console.log('scopeNote: ' + this.scopeNote.value); - } + this.registryService.createOrUpdateMetadataField(Object.assign(new MetadataField(), values)).subscribe((newField) => { + this.submitForm.emit(newField); + }); } else { - console.log('metadata field to update:'); - console.log('element: ' + this.element.value); - if (this.qualifier.value) { - console.log('qualifier: ' + this.qualifier.value); - } - if (this.scopeNote.value) { - console.log('scopeNote: ' + this.scopeNote.value); - } + this.registryService.createOrUpdateMetadataField(Object.assign(new MetadataField(), { + id: field.id, + schema: this.metadataSchema, + element: (values.element ? values.element : field.element), + qualifier: (values.qualifier ? values.qualifier : field.qualifier), + scopeNote: (values.scopeNote ? values.scopeNote : field.scopeNote) + })).subscribe((updatedField) => { + this.submitForm.emit(updatedField); + }); } } ); diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.html b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.html index 1e422bc381..81c4dcd414 100644 --- a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.html +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.html @@ -6,7 +6,9 @@

{{'admin.registries.schema.description' | translate:namespace }}

- +

{{'admin.registries.schema.fields.head' | translate}}

@@ -28,7 +30,8 @@ - + - {{(metadataSchema | async)?.payload?.prefix}}.{{field.element}}{{field.qualifier}} - {{field.scopeNote}} + {{(metadataSchema | async)?.payload?.prefix}}.{{field.element}}{{field.qualifier}} + {{field.scopeNote}} diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.scss b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.scss new file mode 100644 index 0000000000..8c208ffad5 --- /dev/null +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.scss @@ -0,0 +1,5 @@ +@import '../../../../styles/variables.scss'; + +.selectable-row:hover { + cursor: pointer; +} diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts index 3a31436fe2..0411b505aa 100644 --- a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts @@ -1,17 +1,22 @@ import { Component, OnInit } from '@angular/core'; import { RegistryService } from '../../../core/registry/registry.service'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { MetadataField } from '../../../core/metadata/metadatafield.model'; import { MetadataSchema } from '../../../core/metadata/metadataschema.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { map } from 'rxjs/operators'; +import { map, take } from 'rxjs/operators'; +import { hasValue } from '../../../shared/empty.util'; +import { RestResponse } from '../../../core/cache/response.models'; +import { zip } from 'rxjs/internal/observable/zip'; +import { NotificationsService } from '../../../shared/notifications/notifications.service'; @Component({ selector: 'ds-metadata-schema', - templateUrl: './metadata-schema.component.html' + templateUrl: './metadata-schema.component.html', + styleUrls: ['./metadata-schema.component.scss'] }) export class MetadataSchemaComponent implements OnInit { @@ -25,7 +30,10 @@ export class MetadataSchemaComponent implements OnInit { pageSizeOptions: [25, 50, 100, 200] }); - constructor(private registryService: RegistryService, private route: ActivatedRoute) { + constructor(private registryService: RegistryService, + private route: ActivatedRoute, + private notificationsService: NotificationsService, + private router: Router) { } @@ -53,8 +61,19 @@ export class MetadataSchemaComponent implements OnInit { }); } + private forceUpdateFields() { + this.registryService.clearMetadataFieldRequests().subscribe(); + this.updateFields(); + } + editField(field: MetadataField) { - this.registryService.editMetadataField(field); + this.getActiveField().pipe(take(1)).subscribe((activeField) => { + if (field === activeField) { + this.registryService.cancelEditMetadataField(); + } else { + this.registryService.editMetadataField(field); + } + }); } isActive(field: MetadataField): Observable { @@ -80,12 +99,27 @@ export class MetadataSchemaComponent implements OnInit { } deleteFields() { - this.registryService.getSelectedMetadataFields().subscribe( + this.registryService.getSelectedMetadataFields().pipe(take(1)).subscribe( (fields) => { - console.log('metadata fields to delete: '); + const tasks$ = []; for (const field of fields) { - console.log(field); + if (hasValue(field.id)) { + tasks$.push(this.registryService.deleteMetadataSchema(field.id)); + } } + zip(...tasks$).subscribe((responses: RestResponse[]) => { + const successResponses = responses.filter((response: RestResponse) => response.isSuccessful); + const failedResponses = responses.filter((response: RestResponse) => !response.isSuccessful); + if (successResponses.length > 0) { + this.notificationsService.success('Success', `Successfully deleted ${successResponses.length} metadata fields`); + } + if (failedResponses.length > 0) { + this.notificationsService.error('Error', `Failed to delete ${failedResponses.length} metadata fields`); + } + this.registryService.deselectAllMetadataField(); + this.router.navigate([], { queryParams: { page: 1 }, queryParamsHandling: 'merge'}); + this.forceUpdateFields(); + }); } ) } diff --git a/src/app/core/cache/response.models.ts b/src/app/core/cache/response.models.ts index 8b31e3d449..d2531b03ab 100644 --- a/src/app/core/cache/response.models.ts +++ b/src/app/core/cache/response.models.ts @@ -10,6 +10,7 @@ import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafie import { RegistryBitstreamformatsResponse } from '../registry/registry-bitstreamformats-response.model'; import { AuthStatus } from '../auth/models/auth-status.model'; import { MetadataSchema } from '../metadata/metadataschema.model'; +import { MetadataField } from '../metadata/metadatafield.model'; /* tslint:disable:max-classes-per-file */ export class RestResponse { @@ -71,6 +72,15 @@ export class MetadataschemaSuccessResponse extends RestResponse { } } +export class MetadatafieldSuccessResponse extends RestResponse { + constructor( + public metadatafield: MetadataField, + public statusCode: string + ) { + super(true, statusCode); + } +} + export class SearchSuccessResponse extends RestResponse { constructor( public results: SearchQueryResponse, diff --git a/src/app/core/data/metadatafield-parsing.service.ts b/src/app/core/data/metadatafield-parsing.service.ts new file mode 100644 index 0000000000..2ad95e920b --- /dev/null +++ b/src/app/core/data/metadatafield-parsing.service.ts @@ -0,0 +1,19 @@ +import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; +import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; +import { RestRequest } from './request.models'; +import { ResponseParsingService } from './parsing.service'; +import { Injectable } from '@angular/core'; +import { MetadatafieldSuccessResponse, MetadataschemaSuccessResponse, RestResponse } from '../cache/response.models'; +import { MetadataField } from '../metadata/metadatafield.model'; + +@Injectable() +export class MetadatafieldParsingService implements ResponseParsingService { + + parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { + const payload = data.payload; + + const deserialized = new DSpaceRESTv2Serializer(MetadataField).deserialize(payload); + return new MetadatafieldSuccessResponse(deserialized, data.statusCode); + } + +} diff --git a/src/app/core/data/registry-metadatafields-response-parsing.service.ts b/src/app/core/data/registry-metadatafields-response-parsing.service.ts index 7ed3d2dc21..93fd67b702 100644 --- a/src/app/core/data/registry-metadatafields-response-parsing.service.ts +++ b/src/app/core/data/registry-metadatafields-response-parsing.service.ts @@ -9,6 +9,7 @@ import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.seriali import { DSOResponseParsingService } from './dso-response-parsing.service'; import { Injectable } from '@angular/core'; import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-response.model'; +import { hasValue } from '../../shared/empty.util'; @Injectable() export class RegistryMetadatafieldsResponseParsingService implements ResponseParsingService { @@ -18,10 +19,14 @@ export class RegistryMetadatafieldsResponseParsingService implements ResponsePar parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { const payload = data.payload; - const metadatafields = payload._embedded.metadatafields; - metadatafields.forEach((field) => { - field.schema = field._embedded.schema; - }); + let metadatafields = []; + + if (hasValue(payload._embedded)) { + metadatafields = payload._embedded.metadatafields; + metadatafields.forEach((field) => { + field.schema = field._embedded.schema; + }); + } payload.metadatafields = metadatafields; diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index 1d176cdca5..2e357cdc1d 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -13,6 +13,7 @@ import { RestRequestMethod } from './rest-request-method'; import { BrowseItemsResponseParsingService } from './browse-items-response-parsing-service'; import { RegistryMetadataschemasResponseParsingService } from './registry-metadataschemas-response-parsing.service'; import { MetadataschemaParsingService } from './metadataschema-parsing.service'; +import { MetadatafieldParsingService } from './metadatafield-parsing.service'; /* tslint:disable:max-classes-per-file */ @@ -239,6 +240,26 @@ export class UpdateMetadataSchemaRequest extends PutRequest { } } +export class CreateMetadataFieldRequest extends PostRequest { + constructor(uuid: string, href: string, public body?: any, public options?: HttpOptions) { + super(uuid, href, body, options); + } + + getResponseParser(): GenericConstructor { + return MetadatafieldParsingService; + } +} + +export class UpdateMetadataFieldRequest extends PutRequest { + constructor(uuid: string, href: string, public body?: any, public options?: HttpOptions) { + super(uuid, href, body, options); + } + + getResponseParser(): GenericConstructor { + return MetadatafieldParsingService; + } +} + export class RequestError extends Error { statusText: string; } diff --git a/src/app/core/metadata/metadatafield.model.ts b/src/app/core/metadata/metadatafield.model.ts index 77cecb927e..f9b5155649 100644 --- a/src/app/core/metadata/metadatafield.model.ts +++ b/src/app/core/metadata/metadatafield.model.ts @@ -3,6 +3,9 @@ import { autoserialize } from 'cerialize'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; export class MetadataField implements ListableObject { + @autoserialize + id: number; + @autoserialize self: string; diff --git a/src/app/core/registry/registry.service.ts b/src/app/core/registry/registry.service.ts index 4bd59c3714..f07a656de8 100644 --- a/src/app/core/registry/registry.service.ts +++ b/src/app/core/registry/registry.service.ts @@ -20,7 +20,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { RequestService } from '../data/request.service'; import { RegistryMetadataschemasResponse } from './registry-metadataschemas-response.model'; import { - ErrorResponse, MetadataschemaSuccessResponse, + ErrorResponse, MetadatafieldSuccessResponse, MetadataschemaSuccessResponse, RegistryBitstreamformatsSuccessResponse, RegistryMetadatafieldsSuccessResponse, RegistryMetadataschemasSuccessResponse, RestResponse @@ -304,7 +304,7 @@ export class RegistryService { this.store.dispatch(new MetadataRegistryDeselectFieldAction(field)) } - public deselectAllMetadataField(field: MetadataField) { + public deselectAllMetadataField() { this.store.dispatch(new MetadataRegistryDeselectAllFieldAction()) } @@ -366,8 +366,79 @@ export class RegistryService { } public deleteMetadataSchema(id: number): Observable { + return this.delete(this.metadataSchemasPath, id); + } + + public clearMetadataSchemaRequests(): Observable { + return this.halService.getEndpoint(this.metadataSchemasPath).pipe( + tap((href: string) => this.requestService.removeByHrefSubstring(href)) + ) + } + + public createOrUpdateMetadataField(field: MetadataField): Observable { + const isUpdate = hasValue(field.id); const requestId = this.requestService.generateRequestId(); - const endpoint$ = this.halService.getEndpoint(this.metadataSchemasPath).pipe( + const endpoint$ = this.halService.getEndpoint(this.metadataFieldsPath).pipe( + isNotEmptyOperator(), + map((endpoint: string) => (isUpdate ? `${endpoint}/${field.id}` : `${endpoint}?schemaId=${field.schema.id}`)), + distinctUntilChanged() + ); + + const request$ = endpoint$.pipe( + take(1), + map((endpoint: string) => { + if (isUpdate) { + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'application/json'); + options.headers = headers; + return new UpdateMetadataSchemaRequest(requestId, endpoint, JSON.stringify(field), options); + } else { + return new CreateMetadataSchemaRequest(requestId, endpoint, JSON.stringify(field)); + } + }) + ); + + // Execute the post/put request + request$.pipe( + configureRequest(this.requestService) + ).subscribe(); + + // Return created/updated field + return this.requestService.getByUUID(requestId).pipe( + getResponseFromEntry(), + map((response: RestResponse) => { + if (!response.isSuccessful) { + if (hasValue((response as any).errorMessage)) { + this.notificationsService.error('Server Error:', (response as any).errorMessage, new NotificationOptions(-1)); + } + } else { + this.notificationsService.success('Success', `Successfully ${isUpdate ? 'updated' : 'created'} metadata field "${field.schema.prefix}.${field.element}.${field.qualifier}"`); + return response; + } + }), + isNotEmptyOperator(), + map((response: MetadatafieldSuccessResponse) => { + if (isNotEmpty(response.metadatafield)) { + return response.metadatafield; + } + }) + ); + } + + public deleteMetadataField(id: number): Observable { + return this.delete(this.metadataFieldsPath, id); + } + + public clearMetadataFieldRequests(): Observable { + return this.halService.getEndpoint(this.metadataFieldsPath).pipe( + tap((href: string) => this.requestService.removeByHrefSubstring(href)) + ) + } + + private delete(path: string, id: number): Observable { + const requestId = this.requestService.generateRequestId(); + const endpoint$ = this.halService.getEndpoint(path).pipe( isNotEmptyOperator(), map((endpoint: string) => `${endpoint}/${id}`), distinctUntilChanged() @@ -387,10 +458,4 @@ export class RegistryService { getResponseFromEntry() ); } - - public clearMetadataSchemaRequests(): Observable { - return this.halService.getEndpoint(this.metadataSchemasPath).pipe( - tap((href: string) => this.requestService.removeByHrefSubstring(href)) - ) - } }