61949: finished refactoring, adding type doc

This commit is contained in:
lotte
2019-04-29 16:21:36 +02:00
parent af291845ec
commit b2ceb8e9d6
39 changed files with 397 additions and 108 deletions

View File

@@ -7,8 +7,8 @@ import {
MetadataRegistrySelectSchemaAction
} from './metadata-registry.actions';
import { metadataRegistryReducer, MetadataRegistryState } from './metadata-registry.reducers';
import { MetadataSchema } from '../../../core/metadata/metadataschema.model';
import { MetadataField } from '../../../core/metadata/metadatafield.model';
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
import { MetadataField } from '../../../core/metadata/metadata-field.model';
class NullAction extends MetadataRegistryEditSchemaAction {
type = null;

View File

@@ -10,7 +10,7 @@ import { EnumKeysPipe } from '../../../../shared/utils/enum-keys-pipe';
import { RegistryService } from '../../../../core/registry/registry.service';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { MetadataSchema } from '../../../../core/metadata/metadataschema.model';
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
describe('MetadataSchemaFormComponent', () => {
let component: MetadataSchemaFormComponent;

View File

@@ -3,7 +3,6 @@ import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'
import { MetadataFieldFormComponent } from './metadata-field-form.component';
import { RegistryService } from '../../../../core/registry/registry.service';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { MetadataField } from '../../../../core/metadata/metadatafield.model';
import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
@@ -11,7 +10,8 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { EnumKeysPipe } from '../../../../shared/utils/enum-keys-pipe';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { MetadataSchema } from '../../../../core/metadata/metadataschema.model';
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
describe('MetadataFieldFormComponent', () => {
let component: MetadataFieldFormComponent;

View File

@@ -3,7 +3,6 @@ import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'
import { of as observableOf } from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list';
import { MetadataSchema } from '../../../core/metadata/metadataschema.model';
import { TranslateModule } from '@ngx-translate/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, Router } from '@angular/router';
@@ -21,6 +20,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
import { RestResponse } from '../../../core/cache/response.models';
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
describe('MetadataSchemaComponent', () => {
let comp: MetadataSchemaComponent;

View File

@@ -20,6 +20,7 @@ import { Item } from '../../core/shared/item.model';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { Community } from '../../core/shared/community.model';
import { MockRouter } from '../../shared/mocks/mock-router';
import { ResourceType } from '../../core/shared/resource-type';
describe('BrowseByMetadataPageComponent', () => {
let comp: BrowseByMetadataPageComponent;
@@ -39,21 +40,21 @@ describe('BrowseByMetadataPageComponent', () => {
const mockEntries = [
{
type: 'author',
type: ResourceType.BrowseEntry,
authority: null,
value: 'John Doe',
language: 'en',
count: 1
},
{
type: 'author',
type: ResourceType.BrowseEntry,
authority: null,
value: 'James Doe',
language: 'en',
count: 3
},
{
type: 'subject',
type: ResourceType.BrowseEntry,
authority: null,
value: 'Fake subject',
language: 'en',
@@ -68,7 +69,7 @@ describe('BrowseByMetadataPageComponent', () => {
];
const mockBrowseService = {
getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData(mockEntries.filter((entry) => entry.type === options.metadataDefinition)),
getBrowseEntriesFor: (options: BrowseEntrySearchOptions) => toRemoteData(mockEntries),
getBrowseItemsFor: (value: string, options: BrowseEntrySearchOptions) => toRemoteData(mockItems)
};
@@ -105,12 +106,6 @@ describe('BrowseByMetadataPageComponent', () => {
fixture.detectChanges();
});
it('should fetch the correct entries depending on the metadata definition', () => {
comp.browseEntries$.subscribe((result) => {
expect(result.payload.page).toEqual(mockEntries.filter((entry) => entry.type === 'author'));
});
});
it('should not fetch any items when no value is provided', () => {
expect(comp.items$).toBeUndefined();
});

View File

@@ -6,17 +6,17 @@ import { ObjectUpdatesService } from '../../../../core/data/object-updates/objec
import { of as observableOf } from 'rxjs';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { MetadataField } from '../../../../core/metadata/metadatafield.model';
import { By } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { SharedModule } from '../../../../shared/shared.module';
import { getTestScheduler } from 'jasmine-marbles';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
import { TestScheduler } from 'rxjs/testing';
import { MetadataSchema } from '../../../../core/metadata/metadataschema.model';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import { TranslateModule } from '@ngx-translate/core';
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
let comp: EditInPlaceFieldComponent;
let fixture: ComponentFixture<EditInPlaceFieldComponent>;

View File

@@ -24,9 +24,9 @@ import { RemoteData } from '../../../core/data/remote-data';
import { MetadatumViewModel } from '../../../core/shared/metadata.models';
import { RegistryService } from '../../../core/registry/registry.service';
import { PaginatedList } from '../../../core/data/paginated-list';
import { MetadataSchema } from '../../../core/metadata/metadataschema.model';
import { MetadataField } from '../../../core/metadata/metadatafield.model';
import { Metadata } from '../../../core/shared/metadata.utils';
import { MetadataSchema } from '../../../core/metadata/metadata-schema.model';
import { MetadataField } from '../../../core/metadata/metadata-field.model';
let comp: ItemMetadataComponent;
let fixture: ComponentFixture<ItemMetadataComponent>;

View File

@@ -43,7 +43,7 @@ describe('AuthService test', () => {
pipe: observableOf(true)
});
window = new NativeWindowRef();
routerStub = new RouterStub()
routerStub = new RouterStub();
token = new AuthTokenInfo('test_token');
token.expires = Date.now() + (1000 * 60 * 60);
authenticatedState = {

View File

@@ -3,25 +3,56 @@ import { AuthTokenInfo } from './auth-token-info.model';
import { EPerson } from '../../eperson/models/eperson.model';
import { RemoteData } from '../../data/remote-data';
import { Observable } from 'rxjs';
import { CacheableObject, TypedObject } from '../../cache/object-cache.reducer';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { ResourceType } from '../../shared/resource-type';
export class AuthStatus implements CacheableObject, TypedObject {
/**
* Object that represents the authenticated status of a user
*/
export class AuthStatus implements CacheableObject {
/**
* The unique identifier of this auth status
*/
id: string;
/**
* The unique uuid of this auth status
*/
uuid: string;
/**
* True if REST API is up and running, should never return false
*/
okay: boolean;
/**
* If the auth status represents an authenticated state
*/
authenticated: boolean;
/**
* Authentication error if there was one for this status
*/
error?: AuthError;
/**
* The eperson of this auth status
*/
eperson: Observable<RemoteData<EPerson>>;
/**
* True if the token is valid, false if there was no token or the token wasn't valid
*/
token?: AuthTokenInfo;
/**
* The self link of this auth status' REST object
*/
self: string;
/**
* The resource object of this auth status
*/
type: ResourceType;
}

View File

@@ -10,9 +10,15 @@ import { resourceType } from '../../shared/resource-type.decorator';
@inheritSerialization(NormalizedObject)
@resourceType(ResourceType.AuthStatus)
export class NormalizedAuthStatus extends NormalizedObject<AuthStatus> {
/**
* The unique identifier of this auth status
*/
@autoserialize
id: string;
/**
* The unique generated uuid of this auth status
*/
@autoserializeAs(new IDToUUIDSerializer('auth-status'), 'id')
uuid: string;
@@ -28,11 +34,16 @@ export class NormalizedAuthStatus extends NormalizedObject<AuthStatus> {
@autoserialize
authenticated: boolean;
/**
* The self link to the eperson of this auth status
*/
@relationship(ResourceType.EPerson, false)
@autoserialize
eperson: string;
/**
* The resource object of this auth status
*/
@autoserialize
type: ResourceType;
}

View File

@@ -29,7 +29,7 @@ export class RemoteDataBuildService {
protected requestService: RequestService) {
}
buildSingle<T extends TypedObject & CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<T>> {
buildSingle<T extends CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<T>> {
if (typeof href$ === 'string') {
href$ = observableOf(href$);
}
@@ -107,7 +107,7 @@ export class RemoteDataBuildService {
);
}
buildList<T extends TypedObject & CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<PaginatedList<T>>> {
buildList<T extends CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<PaginatedList<T>>> {
if (typeof href$ === 'string') {
href$ = observableOf(href$);
}
@@ -149,7 +149,7 @@ export class RemoteDataBuildService {
return this.toRemoteDataObservable(requestEntry$, payload$);
}
build<T extends TypedObject & CacheableObject>(normalized: NormalizedObject<T>): T {
build<T extends CacheableObject>(normalized: NormalizedObject<T>): T {
const links: any = {};
const relationships = getRelationships(normalized.constructor) || [];

View File

@@ -4,7 +4,7 @@ import { ResourceType } from '../../shared/resource-type';
/**
* An abstract model class for a NormalizedObject.
*/
export abstract class NormalizedObject<T extends TypedObject> implements CacheableObject, TypedObject {
export abstract class NormalizedObject<T extends TypedObject> implements CacheableObject {
/**
* The link to the rest endpoint where this object can be found

View File

@@ -9,6 +9,7 @@ import {
ResetObjectCacheTimestampsAction
} from './object-cache.actions';
import { Operation } from 'fast-json-patch';
import { ResourceType } from '../shared/resource-type';
class NullAction extends RemoveFromObjectCacheAction {
type = null;
@@ -28,6 +29,7 @@ describe('objectCacheReducer', () => {
const testState = {
[selfLink1]: {
data: {
type: ResourceType.Item,
self: selfLink1,
foo: 'bar'
},
@@ -39,6 +41,7 @@ describe('objectCacheReducer', () => {
},
[selfLink2]: {
data: {
type: ResourceType.Item,
self: requestUUID2,
foo: 'baz'
},
@@ -67,7 +70,7 @@ describe('objectCacheReducer', () => {
it('should add the payload to the cache in response to an ADD action', () => {
const state = Object.create(null);
const objectToCache = { self: selfLink1 };
const objectToCache = { self: selfLink1, type: ResourceType.Item };
const timeAdded = new Date().getTime();
const msToLive = 900000;
const requestUUID = requestUUID1;
@@ -80,7 +83,12 @@ describe('objectCacheReducer', () => {
});
it('should overwrite an object in the cache in response to an ADD action if it already exists', () => {
const objectToCache = { self: selfLink1, foo: 'baz', somethingElse: true };
const objectToCache = {
self: selfLink1,
foo: 'baz',
somethingElse: true,
type: ResourceType.Item
};
const timeAdded = new Date().getTime();
const msToLive = 900000;
const requestUUID = requestUUID1;
@@ -95,7 +103,7 @@ describe('objectCacheReducer', () => {
it('should perform the ADD action without affecting the previous state', () => {
const state = Object.create(null);
const objectToCache = { self: selfLink1 };
const objectToCache = { self: selfLink1, type: ResourceType.Item };
const timeAdded = new Date().getTime();
const msToLive = 900000;
const requestUUID = requestUUID1;

View File

@@ -35,12 +35,13 @@ export interface Patch {
export interface TypedObject {
type: ResourceType;
}
/**
* An interface to represent objects that can be cached
*
* A cacheable object should have a self link
*/
export interface CacheableObject {
export interface CacheableObject extends TypedObject {
uuid?: string;
self: string;
// isNew: boolean;
@@ -50,8 +51,6 @@ export interface CacheableObject {
// save(): void;
}
// export type TypedCacheableObject = TypedObject & CacheableObject;
/**
* An entry in the ObjectCache
*/

View File

@@ -4,13 +4,12 @@ import { applyPatch, Operation } from 'fast-json-patch';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, filter, map, mergeMap, take, } from 'rxjs/operators';
import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util';
import { hasNoValue, isNotEmpty } from '../../shared/empty.util';
import { CoreState } from '../core.reducers';
import { coreSelector } from '../core.selectors';
import { RestRequestMethod } from '../data/rest-request-method';
import { selfLinkFromUuidSelector } from '../index/index.selectors';
import { GenericConstructor } from '../shared/generic-constructor';
import { NormalizedObjectFactory } from './models/normalized-object-factory';
import { NormalizedObject } from './models/normalized-object.model';
import {
AddPatchObjectCacheAction,
@@ -21,6 +20,7 @@ import {
import { CacheableObject, ObjectCacheEntry, ObjectCacheState } from './object-cache.reducer';
import { AddToSSBAction } from './server-sync-buffer.actions';
import { getNormalizedConstructorByType } from '../shared/resource-type.decorator';
/**
* The base selector function to select the object cache in the store
@@ -109,7 +109,7 @@ export class ObjectCacheService {
}
),
map((entry: ObjectCacheEntry) => {
const type: GenericConstructor<NormalizedObject<T>> = NormalizedObjectFactory.getConstructor(entry.data.type);
const type: GenericConstructor<NormalizedObject<T>> = getNormalizedConstructorByType(entry.data.type);
return Object.assign(new type(), entry.data) as NormalizedObject<T>
})
);

View File

@@ -5,10 +5,8 @@ import { RestRequest } from '../data/request.models';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { ConfigSuccessResponse, ErrorResponse, RestResponse } from '../cache/response.models';
import { isNotEmpty } from '../../shared/empty.util';
import { ConfigObjectFactory } from './models/config-object-factory';
import { ConfigObject } from './models/config.model';
import { ConfigType } from './models/config-type';
import { BaseResponseParsingService } from '../data/base-response-parsing.service';
import { GLOBAL_CONFIG } from '../../../config';
import { GlobalConfig } from '../../../config/global-config.interface';
@@ -26,7 +24,7 @@ export class ConfigResponseParsingService extends BaseResponseParsingService imp
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && (data.statusCode === 201 || data.statusCode === 200)) {
const configDefinition = this.process<ConfigObject,ConfigType>(data.payload, request.uuid);
const configDefinition = this.process<ConfigObject>(data.payload, request.uuid);
return new ConfigSuccessResponse(configDefinition, data.statusCode, data.statusText, this.processPageInfo(data.payload));
} else {
return new ErrorResponse(

View File

@@ -1,7 +1,7 @@
import { CacheableObject, TypedObject } from '../../cache/object-cache.reducer';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { ResourceType } from '../../shared/resource-type';
export abstract class ConfigObject implements CacheableObject, TypedObject {
export abstract class ConfigObject implements CacheableObject {
/**
* The name for this configuration

View File

@@ -7,7 +7,7 @@ import { ResourceType } from '../../shared/resource-type';
* Normalized abstract class for a configuration object
*/
@inheritSerialization(NormalizedObject)
export abstract class NormalizedConfigObject<T extends CacheableObject> implements CacheableObject, TypedObject {
export abstract class NormalizedConfigObject<T extends CacheableObject> implements CacheableObject {
/**
* The name for this configuration

View File

@@ -1,14 +1,13 @@
import {
ModuleWithProviders,
NgModule,
Optional,
SkipSelf
} from '@angular/core';
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
import {
DynamicFormLayoutService,
DynamicFormService,
DynamicFormValidationService
} from '@ng-dynamic-forms/core';
import { coreEffects } from './core.effects';
import { coreReducers } from './core.reducers';
@@ -80,6 +79,27 @@ import { DSOChangeAnalyzer } from './data/dso-change-analyzer.service';
import { ObjectUpdatesService } from './data/object-updates/object-updates.service';
import { DefaultChangeAnalyzer } from './data/default-change-analyzer.service';
import { SearchService } from '../+search-page/search-service/search.service';
import { NormalizedCollection } from './cache/models/normalized-collection.model';
import { NormalizedCommunity } from './cache/models/normalized-community.model';
import { NormalizedDSpaceObject } from './cache/models/normalized-dspace-object.model';
import { NormalizedBitstream } from './cache/models/normalized-bitstream.model';
import { NormalizedBundle } from './cache/models/normalized-bundle.model';
import { NormalizedBitstreamFormat } from './cache/models/normalized-bitstream-format.model';
import { NormalizedItem } from './cache/models/normalized-item.model';
import { NormalizedEPerson } from './eperson/models/normalized-eperson.model';
import { NormalizedGroup } from './eperson/models/normalized-group.model';
import { NormalizedResourcePolicy } from './cache/models/normalized-resource-policy.model';
import { NormalizedMetadataSchema } from './metadata/normalized-metadata-schema.model';
import { NormalizedMetadataField } from './metadata/normalized-metadata-field.model';
import { NormalizedLicense } from './cache/models/normalized-license.model';
import { NormalizedWorkflowItem } from './submission/models/normalized-workflowitem.model';
import { NormalizedWorkspaceItem } from './submission/models/normalized-workspaceitem.model';
import { NormalizedSubmissionDefinitionsModel } from './config/models/normalized-config-submission-definitions.model';
import { NormalizedSubmissionFormsModel } from './config/models/normalized-config-submission-forms.model';
import { NormalizedSubmissionSectionModel } from './config/models/normalized-config-submission-section.model';
import { NormalizedAuthStatus } from './auth/models/normalized-auth-status.model';
import { NormalizedAuthorityValue } from './integration/models/normalized-authority-value.model';
import { BrowseEntry } from './shared/browse-entry.model';
const IMPORTS = [
CommonModule,
@@ -87,13 +107,9 @@ const IMPORTS = [
EffectsModule.forFeature(coreEffects)
];
const DECLARATIONS = [
const DECLARATIONS = [];
];
const EXPORTS = [
];
const EXPORTS = [];
const PROVIDERS = [
ApiService,
@@ -169,9 +185,37 @@ const PROVIDERS = [
multi: true
},
NotificationsService,
{ provide: NativeWindowService, useFactory: NativeWindowFactory }
{ provide: NativeWindowService, useFactory: NativeWindowFactory },
];
/**
* Declaration needed to make sure all decorator functions are called in time
*/
export const normalizedModels =
[
NormalizedDSpaceObject,
NormalizedBundle,
NormalizedBitstream,
NormalizedBitstreamFormat,
NormalizedItem,
NormalizedCollection,
NormalizedCommunity,
NormalizedEPerson,
NormalizedGroup,
NormalizedResourcePolicy,
NormalizedMetadataSchema,
NormalizedMetadataField,
NormalizedLicense,
NormalizedWorkflowItem,
NormalizedWorkspaceItem,
NormalizedSubmissionDefinitionsModel,
NormalizedSubmissionFormsModel,
NormalizedSubmissionSectionModel,
NormalizedAuthStatus,
NormalizedAuthorityValue,
BrowseEntry
];
@NgModule({
imports: [
...IMPORTS
@@ -186,8 +230,8 @@ const PROVIDERS = [
...PROVIDERS
]
})
export class CoreModule {
export class CoreModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: CoreModule,
@@ -197,10 +241,9 @@ export class CoreModule {
};
}
constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
if (isNotEmpty(parentModule)) {
throw new Error('CoreModule is already loaded. Import it in the AppModule only');
}
}
}

View File

@@ -31,12 +31,12 @@ import { configureRequest, getResponseFromEntry } from '../shared/operators';
import { ErrorResponse, RestResponse } from '../cache/response.models';
import { NotificationOptions } from '../../shared/notifications/models/notification-options.model';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
import { CacheableObject } from '../cache/object-cache.reducer';
import { RequestEntry } from './request.reducer';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { ChangeAnalyzer } from './change-analyzer';
import { RestRequestMethod } from './rest-request-method';
import { getNormalizedConstructorByType } from '../shared/resource-type.decorator';
export abstract class DataService<T extends CacheableObject> {
protected abstract requestService: RequestService;
@@ -243,7 +243,7 @@ export abstract class DataService<T extends CacheableObject> {
);
const normalizedObject: NormalizedObject<T> = this.dataBuildService.normalize<T>(dso);
const serializedDso = new DSpaceRESTv2Serializer(NormalizedObjectFactory.getConstructor(dso.type)).serialize(normalizedObject);
const serializedDso = new DSpaceRESTv2Serializer(getNormalizedConstructorByType(dso.type)).serialize(normalizedObject);
const request$ = endpoint$.pipe(
take(1),

View File

@@ -9,11 +9,11 @@ import { RequestService } from './request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindAllOptions } from './request.models';
import { ObjectCacheService } from '../cache/object-cache.service';
import { MetadataSchema } from '../metadata/metadataschema.model';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { HttpClient } from '@angular/common/http';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { MetadataSchema } from '../metadata/metadata-schema.model';
/**
* A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint

View File

@@ -1,10 +1,10 @@
import { MetadataSchema } from '../metadata/metadataschema.model';
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 { MetadataschemaSuccessResponse, RestResponse } from '../cache/response.models';
import { MetadataSchema } from '../metadata/metadata-schema.model';
@Injectable()
export class MetadataschemaParsingService implements ResponseParsingService {

View File

@@ -17,10 +17,10 @@ import { RequestError, RestRequest } from './request.models';
import { RequestEntry } from './request.reducer';
import { RequestService } from './request.service';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
import { catchError, filter, flatMap, map, take, tap } from 'rxjs/operators';
import { ErrorResponse, RestResponse } from '../cache/response.models';
import { StoreActionTypes } from '../../store.actions';
import { getNormalizedConstructorByType } from '../shared/resource-type.decorator';
export const addToResponseCacheAndCompleteAction = (request: RestRequest, envConfig: GlobalConfig) =>
(source: Observable<RestResponse>): Observable<RequestCompleteAction> =>
@@ -45,7 +45,7 @@ export class RequestEffects {
flatMap((request: RestRequest) => {
let body;
if (isNotEmpty(request.body)) {
const serializer = new DSpaceRESTv2Serializer(NormalizedObjectFactory.getConstructor(request.body.type));
const serializer = new DSpaceRESTv2Serializer(getNormalizedConstructorByType(request.body.type));
body = serializer.serialize(request.body);
}
return this.restApi.request(request.method, request.href, body, request.options).pipe(

View File

@@ -1,20 +1,52 @@
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { isNotEmpty } from '../../shared/empty.util';
import { MetadataSchema } from './metadata-schema.model';
import { ResourceType } from '../shared/resource-type';
/**
* Class the represents a metadata field
*/
export class MetadataField implements ListableObject {
/**
* The identifier of this metadata field
*/
id: number;
/**
* The self link of this metadata field
*/
self: string;
/**
* The element of this metadata field
*/
element: string;
/**
* The qualifier of this metadata field
*/
qualifier: string;
/**
* The scope note of this metadata field
*/
scopeNote: string;
/**
* The metadata schema object of this metadata field
*/
schema: MetadataSchema;
/**
* The resource type of this metadata field
*/
type: ResourceType;
/**
* Method to print this metadata field as a string
* @param separator The separator between the schema, element and qualifier in the string
*/
toString(separator: string = '.'): string {
let key = this.schema.prefix + separator + this.element;
if (isNotEmpty(this.qualifier)) {

View File

@@ -1,11 +1,32 @@
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { ResourceType } from '../shared/resource-type';
/**
* Class that represents a metadata schema
*/
export class MetadataSchema implements ListableObject {
/**
* The unique identifier for this metadata schema
*/
id: number;
/**
* The REST link to itself
*/
self: string;
/**
* A unique prefix that defines this schema
*/
prefix: string;
/**
* The namespace of this metadata schema
*/
namespace: string;
/**
* The resource type of this metadata schema
*/
type: ResourceType;
}

View File

@@ -6,25 +6,53 @@ import { MetadataField } from './metadata-field.model';
import { NormalizedObject } from '../cache/models/normalized-object.model';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
/**
* Class the represents a normalized metadata field
*/
@mapsTo(MetadataField)
@resourceType(ResourceType.MetadataField)
export class NormalizedMetadataField extends NormalizedObject<MetadataField> implements ListableObject {
/**
* The identifier of this normalized metadata field
*/
@autoserialize
id: number;
/**
* The self link of this normalized metadata field
*/
@autoserialize
self: string;
/**
* The element of this normalized metadata field
*/
@autoserialize
element: string;
/**
* The qualifier of this normalized metadata field
*/
@autoserialize
qualifier: string;
/**
* The scope note of this normalized metadata field
*/
@autoserialize
scopeNote: string;
/**
* The link to the metadata schema of this normalized metadata field
*/
@deserialize
@relationship(ResourceType.MetadataSchema)
schema: string;
/**
* The resource type of this normalized metadata field
*/
@autoserialize
type: ResourceType;
}

View File

@@ -35,4 +35,10 @@ export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> i
*/
@autoserialize
namespace: string;
/**
* The resource type of this metadata schema
*/
@autoserialize
type: ResourceType;
}

View File

@@ -1,14 +1,26 @@
import { PageInfo } from '../shared/page-info.model';
import { autoserialize, autoserializeAs } from 'cerialize';
import { MetadataField } from '../metadata/metadatafield.model';
import { MetadataField } from '../metadata/metadata-field.model';
/**
* Class that represents a response with a registry's metadata fields
*/
export class RegistryMetadatafieldsResponse {
/**
* List of metadata fields in the response
*/
@autoserializeAs(MetadataField)
metadatafields: MetadataField[];
/**
* Page info of this response
*/
@autoserialize
page: PageInfo;
/**
* The REST link to this response
*/
@autoserialize
self: string;
}

View File

@@ -39,8 +39,9 @@ import {
MetadataRegistrySelectFieldAction,
MetadataRegistrySelectSchemaAction
} from '../../+admin/admin-registries/metadata-registry/metadata-registry.actions';
import { MetadataSchema } from '../metadata/metadataschema.model';
import { MetadataField } from '../metadata/metadatafield.model';
import { ResourceType } from '../shared/resource-type';
import { MetadataSchema } from '../metadata/metadata-schema.model';
import { MetadataField } from '../metadata/metadata-field.model';
@Component({ template: '' })
class DummyComponent {
@@ -59,13 +60,15 @@ describe('RegistryService', () => {
id: 1,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1',
prefix: 'dc',
namespace: 'http://dublincore.org/documents/dcmi-terms/'
},
namespace: 'http://dublincore.org/documents/dcmi-terms/',
type: ResourceType.MetadataSchema
},
{
id: 2,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2',
prefix: 'mock',
namespace: 'http://dspace.org/mockschema'
namespace: 'http://dspace.org/mockschema',
type: ResourceType.MetadataSchema
}
];
const mockFieldsList = [
@@ -75,7 +78,8 @@ describe('RegistryService', () => {
element: 'contributor',
qualifier: 'advisor',
scopeNote: null,
schema: mockSchemasList[0]
schema: mockSchemasList[0],
type: ResourceType.MetadataField
},
{
id: 2,
@@ -83,7 +87,8 @@ describe('RegistryService', () => {
element: 'contributor',
qualifier: 'author',
scopeNote: null,
schema: mockSchemasList[0]
schema: mockSchemasList[0],
type: ResourceType.MetadataField
},
{
id: 3,
@@ -91,7 +96,8 @@ describe('RegistryService', () => {
element: 'contributor',
qualifier: 'editor',
scopeNote: 'test scope note',
schema: mockSchemasList[1]
schema: mockSchemasList[1],
type: ResourceType.MetadataField
},
{
id: 4,
@@ -99,7 +105,8 @@ describe('RegistryService', () => {
element: 'contributor',
qualifier: 'illustrator',
scopeNote: null,
schema: mockSchemasList[1]
schema: mockSchemasList[1],
type: ResourceType.MetadataField
}
];

View File

@@ -3,8 +3,6 @@ import { Injectable } from '@angular/core';
import { RemoteData } from '../data/remote-data';
import { PaginatedList } from '../data/paginated-list';
import { PageInfo } from '../shared/page-info.model';
import { MetadataSchema } from '../metadata/metadataschema.model';
import { MetadataField } from '../metadata/metadatafield.model';
import { BitstreamFormat } from './mock-bitstream-format.model';
import {
CreateMetadataFieldRequest,
@@ -56,7 +54,6 @@ import {
} from '../../+admin/admin-registries/metadata-registry/metadata-registry.actions';
import { distinctUntilChanged, flatMap, map, switchMap, take, tap } from 'rxjs/operators';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
import { ResourceType } from '../shared/resource-type';
import { NormalizedMetadataSchema } from '../metadata/normalized-metadata-schema.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
@@ -64,6 +61,9 @@ import { NotificationOptions } from '../../shared/notifications/models/notificat
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { HttpHeaders } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { MetadataSchema } from '../metadata/metadata-schema.model';
import { MetadataField } from '../metadata/metadata-field.model';
import { getNormalizedConstructorByType } from '../shared/resource-type.decorator';
const metadataRegistryStateSelector = (state: AppState) => state.metadataRegistry;
const editMetadataSchemaSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.editSchema);
@@ -71,6 +71,9 @@ const selectedMetadataSchemasSelector = createSelector(metadataRegistryStateSele
const editMetadataFieldSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.editField);
const selectedMetadataFieldsSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.selectedFields);
/**
* Service for registry related CRUD actions such as metadata schema, metadata field and bitstream format
*/
@Injectable()
export class RegistryService {
@@ -87,6 +90,10 @@ export class RegistryService {
}
/**
* Retrieves all metadata schemas
* @param pagination The pagination info used to retrieve the schemas
*/
public getMetadataSchemas(pagination: PaginationComponentOptions): Observable<RemoteData<PaginatedList<MetadataSchema>>> {
const requestObs = this.getMetadataSchemasRequestObs(pagination);
@@ -117,6 +124,10 @@ export class RegistryService {
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
}
/**
* Retrieves a metadata schema by its name
* @param schemaName The name of the schema to find
*/
public getMetadataSchemaByName(schemaName: string): Observable<RemoteData<MetadataSchema>> {
// Temporary pagination to get ALL metadataschemas until there's a rest api endpoint for fetching a specific schema
const pagination: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
@@ -142,6 +153,11 @@ export class RegistryService {
return this.rdb.toRemoteDataObservable(requestEntryObs, metadataschemaObs);
}
/**
* retrieves all metadata fields that belong to a certain metadata schema
* @param schema The schema to filter by
* @param pagination The pagination info used to retrieve the fields
*/
public getMetadataFieldsBySchema(schema: MetadataSchema, pagination: PaginationComponentOptions): Observable<RemoteData<PaginatedList<MetadataField>>> {
const requestObs = this.getMetadataFieldsBySchemaRequestObs(pagination, schema);
@@ -215,6 +231,10 @@ export class RegistryService {
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
}
/**
* Retrieves all bitstream formats
* @param pagination The pagination info used to retrieve the bitstream formats
*/
public getBitstreamFormats(pagination: PaginationComponentOptions): Observable<RemoteData<PaginatedList<BitstreamFormat>>> {
const requestObs = this.getBitstreamFormatsRequestObs(pagination);
@@ -245,7 +265,7 @@ export class RegistryService {
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
}
public getMetadataSchemasRequestObs(pagination: PaginationComponentOptions): Observable<RestRequest> {
private getMetadataSchemasRequestObs(pagination: PaginationComponentOptions): Observable<RestRequest> {
return this.halService.getEndpoint(this.metadataSchemasPath).pipe(
map((url: string) => {
const args: string[] = [];
@@ -327,58 +347,101 @@ export class RegistryService {
);
}
/**
* Method to start editing a metadata schema, dispatches an edit schema action
* @param schema The schema that's being edited
*/
public editMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistryEditSchemaAction(schema));
}
/**
* Method to cancel editing a metadata schema, dispatches a cancel schema action
*/
public cancelEditMetadataSchema() {
this.store.dispatch(new MetadataRegistryCancelSchemaAction());
}
/**
* Method to retrieve the metadata schema that are currently being edited
*/
public getActiveMetadataSchema(): Observable<MetadataSchema> {
return this.store.pipe(select(editMetadataSchemaSelector));
}
/**
* Method to select a metadata schema, dispatches a select schema action
* @param schema The schema that's being selected
*/
public selectMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistrySelectSchemaAction(schema))
}
/**
* Method to deselect a metadata schema, dispatches a deselect schema action
* @param schema The schema that's it being deselected
*/
public deselectMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistryDeselectSchemaAction(schema))
}
/**
* Method to deselect all currently selected metadata schema, dispatches a deselect all schema action
*/
public deselectAllMetadataSchema() {
this.store.dispatch(new MetadataRegistryDeselectAllSchemaAction())
}
/**
* Method to retrieve the metadata schemas that are currently selected
*/
public getSelectedMetadataSchemas(): Observable<MetadataSchema[]> {
return this.store.pipe(select(selectedMetadataSchemasSelector));
}
/**
* Method to start editing a metadata field, dispatches an edit field action
* @param field The field that's being edited
*/
public editMetadataField(field: MetadataField) {
this.store.dispatch(new MetadataRegistryEditFieldAction(field));
}
/**
* Method to cancel editing a metadata field, dispatches a cancel field action
*/
public cancelEditMetadataField() {
this.store.dispatch(new MetadataRegistryCancelFieldAction());
}
/**
* Method to retrieve the metadata field that are currently being edited
*/
public getActiveMetadataField(): Observable<MetadataField> {
return this.store.pipe(select(editMetadataFieldSelector));
}
/**
* Method to select a metadata field, dispatches a select field action
* @param field The field that's being selected
*/
public selectMetadataField(field: MetadataField) {
this.store.dispatch(new MetadataRegistrySelectFieldAction(field))
}
/**
* Method to deselect a metadata field, dispatches a deselect field action
* @param field The field that's it being deselected
*/
public deselectMetadataField(field: MetadataField) {
this.store.dispatch(new MetadataRegistryDeselectFieldAction(field))
}
/**
* Method to deselect all currently selected metadata fields, dispatches a deselect all field action
*/
public deselectAllMetadataField() {
this.store.dispatch(new MetadataRegistryDeselectAllFieldAction())
}
/**
* Method to retrieve the metadata fields that are currently selected
*/
public getSelectedMetadataFields(): Observable<MetadataField[]> {
return this.store.pipe(select(selectedMetadataFieldsSelector));
}
@@ -400,7 +463,7 @@ export class RegistryService {
distinctUntilChanged()
);
const serializedSchema = new DSpaceRESTv2Serializer(NormalizedObjectFactory.getConstructor(ResourceType.MetadataSchema)).serialize(schema as NormalizedMetadataSchema);
const serializedSchema = new DSpaceRESTv2Serializer(getNormalizedConstructorByType(ResourceType.MetadataSchema)).serialize(schema as NormalizedMetadataSchema);
const request$ = endpoint$.pipe(
take(1),
@@ -444,10 +507,17 @@ export class RegistryService {
);
}
/**
* Method to delete a metadata schema
* @param id The id of the metadata schema to delete
*/
public deleteMetadataSchema(id: number): Observable<RestResponse> {
return this.delete(this.metadataSchemasPath, id);
}
/**
* Method that clears a cached metadata schema request and returns its REST url
*/
public clearMetadataSchemaRequests(): Observable<string> {
return this.halService.getEndpoint(this.metadataSchemasPath).pipe(
tap((href: string) => this.requestService.removeByHrefSubstring(href))
@@ -514,10 +584,16 @@ export class RegistryService {
);
}
/**
* Method to delete a metadata field
* @param id The id of the metadata field to delete
*/
public deleteMetadataField(id: number): Observable<RestResponse> {
return this.delete(this.metadataFieldsPath, id);
}
/**
* Method that clears a cached metadata field request and returns its REST url
*/
public clearMetadataFieldRequests(): Observable<string> {
return this.halService.getEndpoint(this.metadataFieldsPath).pipe(
tap((href: string) => this.requestService.removeByHrefSubstring(href))

View File

@@ -5,7 +5,7 @@ import { ResourceType } from './resource-type';
/**
* Model class for a Bitstream Format
*/
export class BitstreamFormat implements CacheableObject, TypedObject {
export class BitstreamFormat implements CacheableObject {
/**
* Short description of this Bitstream Format

View File

@@ -2,24 +2,41 @@ import { autoserialize, autoserializeAs } from 'cerialize';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { ResourceType } from './resource-type';
import { resourceType } from './resource-type.decorator';
import { CacheableObject, TypedObject } from '../cache/object-cache.reducer';
import { TypedObject } from '../cache/object-cache.reducer';
/**
* Class object representing a browse entry
* This class is not normalized because browse entries do not have self links
*/
@resourceType(ResourceType.BrowseEntry)
export class BrowseEntry implements ListableObject, TypedObject {
/**
* The resource type of this browse entry
*/
@autoserialize
type: ResourceType;
/**
* The authority string of this browse entry
*/
@autoserialize
authority: string;
/**
* The value of this browse entry
*/
@autoserialize
value: string;
/**
* The language of the value of this browse entry
*/
@autoserializeAs('valueLang')
language: string;
/**
* The count of this browse entry
*/
@autoserialize
count: number;
}

View File

@@ -11,7 +11,7 @@ import { ListableObject } from '../../shared/object-collection/shared/listable-o
/**
* An abstract model class for a DSpaceObject.
*/
export class DSpaceObject implements CacheableObject, ListableObject, TypedObject {
export class DSpaceObject implements CacheableObject, ListableObject {
private _name: string;

View File

@@ -1,12 +1,11 @@
import { CacheableObject, TypedObject } from '../cache/object-cache.reducer';
import { CacheableObject } from '../cache/object-cache.reducer';
import { ResourceType } from './resource-type';
import { Group } from '../eperson/models/group.model';
import { ActionType } from '../cache/models/action-type.model';
/**
* Model class for a Resource Policy
*/
export class ResourcePolicy implements CacheableObject, TypedObject {
export class ResourcePolicy implements CacheableObject {
/**
* The action that is allowed by this Resource Policy
*/

View File

@@ -1,19 +1,27 @@
import { CacheableObject, TypedObject } from '../cache/object-cache.reducer';
import { TypedObject } from '../cache/object-cache.reducer';
import { GenericConstructor } from './generic-constructor';
import { ResourceType } from './resource-type';
const resourceTypeForObjectMap = new Map();
export function resourceType(...resourceType: ResourceType[]) {
/**
* Decorator function to map resource types to their matching normalized model class constructor
* @param type The resource type used as a key in the map
*/
export function resourceType(...type: ResourceType[]) {
return function decorator(objectConstructor: GenericConstructor<TypedObject>) {
if (!objectConstructor) {
return;
}
resourceType.forEach((rt: string) => resourceTypeForObjectMap.set(rt, objectConstructor)
type.forEach((rt: string) => resourceTypeForObjectMap.set(rt, objectConstructor)
)
};
}
export function getNormalizedConstructorByType(resourceType: ResourceType) {
return resourceTypeForObjectMap.get(resourceType);
/**
* Method to retrieve the normalized model class constructor based on a resource type
* @param type The resource type to look for
*/
export function getNormalizedConstructorByType(type: ResourceType) {
return resourceTypeForObjectMap.get(type);
}

View File

@@ -10,7 +10,6 @@ import { BaseResponseParsingService } from '../data/base-response-parsing.servic
import { GLOBAL_CONFIG } from '../../../config';
import { GlobalConfig } from '../../../config/global-config.interface';
import { ObjectCacheService } from '../cache/object-cache.service';
import { SubmissionResourceType } from './submission-resource-type';
import { NormalizedWorkspaceItem } from './models/normalized-workspaceitem.model';
import { NormalizedWorkflowItem } from './models/normalized-workflowitem.model';
import { FormFieldMetadataValueObject } from '../../shared/form/builder/models/form-field-metadata-value.model';

View File

@@ -6,24 +6,24 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
import { CoreState } from '../core.reducers';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service';
import { Workspaceitem } from './models/workspaceitem.model';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindAllOptions } from '../data/request.models';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { WorkspaceItem } from './models/workspaceitem.model';
/**
* A service that provides methods to make REST requests with workspaceitems endpoint.
*/
@Injectable()
export class WorkspaceitemDataService extends DataService<Workspaceitem> {
export class WorkspaceitemDataService extends DataService<WorkspaceItem> {
protected linkPath = 'workspaceitems';
protected forceBypassCache = true;
constructor(
protected comparator: DSOChangeAnalyzer<Workspaceitem>,
protected comparator: DSOChangeAnalyzer<WorkspaceItem>,
protected dataBuildService: NormalizedObjectBuildService,
protected halService: HALEndpointService,
protected http: HttpClient,

View File

@@ -6,13 +6,13 @@ import { first } from 'rxjs/operators';
import { SectionsService } from '../../sections/sections.service';
import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model';
import { normalizeSectionData } from '../../../core/submission/submission-response-parsing.service';
import { SubmissionService } from '../../submission.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { UploaderOptions } from '../../../shared/uploader/uploader-options.model';
import parseSectionErrors from '../../utils/parseSectionErrors';
import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model';
/**
* This component represents the drop zone that provides to add files to the submission.
@@ -119,7 +119,7 @@ export class SubmissionUploadFilesComponent implements OnChanges {
* @param workspaceitem
* The submission object retrieved from REST
*/
public onCompleteItem(workspaceitem: Workspaceitem) {
public onCompleteItem(workspaceitem: WorkspaceItem) {
// Checks if upload section is enabled so do upload
this.subs.push(
this.uploadEnabled

View File

@@ -20,7 +20,6 @@ import { CookieService } from '../../app/shared/services/cookie.service';
import { AuthService } from '../../app/core/auth/auth.service';
import { Angulartics2Module } from 'angulartics2';
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import { ServerSubmissionService } from '../../app/submission/server-submission.service';
import { SubmissionService } from '../../app/submission/submission.service';
export const REQ_KEY = makeStateKey<string>('req');