mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Added mocks for all model types, added CollectionDataService
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { EffectsModule } from "@ngrx/effects";
|
||||
import { HeaderEffects } from "./header/header.effects";
|
||||
import { coreEffects } from "./core/core.effects";
|
||||
|
||||
export const effects = [
|
||||
...coreEffects, //TODO should probably be imported in coreModule
|
||||
EffectsModule.run(HeaderEffects)
|
||||
];
|
||||
|
@@ -2,14 +2,17 @@ import { combineReducers } from "@ngrx/store";
|
||||
import { routerReducer, RouterState } from "@ngrx/router-store";
|
||||
import { headerReducer, HeaderState } from './header/header.reducer';
|
||||
import { hostWindowReducer, HostWindowState } from "./shared/host-window.reducer";
|
||||
import { CoreState, coreReducer } from "./core/core.reducers";
|
||||
|
||||
export interface AppState {
|
||||
core: CoreState;
|
||||
router: RouterState;
|
||||
hostWindow: HostWindowState;
|
||||
header: HeaderState;
|
||||
}
|
||||
|
||||
export const reducers = {
|
||||
core: coreReducer,
|
||||
router: routerReducer,
|
||||
hostWindow: hostWindowReducer,
|
||||
header: headerReducer
|
||||
|
6
src/app/core/core.effects.ts
Normal file
6
src/app/core/core.effects.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { EffectsModule } from "@ngrx/effects";
|
||||
import { CollectionDataEffects } from "./data-services/collection/collection-data.effects";
|
||||
|
||||
export const coreEffects = [
|
||||
EffectsModule.run(CollectionDataEffects)
|
||||
];
|
@@ -2,6 +2,8 @@ import { NgModule, Optional, SkipSelf, ModuleWithProviders } from '@angular/core
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from "../shared/shared.module";
|
||||
import { isNotEmpty } from "../shared/empty.util";
|
||||
import { DSpaceRESTv2Service } from "./dspace-rest-v2/dspace-rest-v2.service";
|
||||
import { CollectionDataService } from "./data-services/collection/collection-data.service";
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
@@ -15,6 +17,8 @@ const EXPORTS = [
|
||||
];
|
||||
|
||||
const PROVIDERS = [
|
||||
CollectionDataService,
|
||||
DSpaceRESTv2Service
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
17
src/app/core/core.reducers.ts
Normal file
17
src/app/core/core.reducers.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { combineReducers } from "@ngrx/store";
|
||||
import {
|
||||
CollectionDataState,
|
||||
collectionDataReducer
|
||||
} from "./data-services/collection/collection-data.reducer";
|
||||
|
||||
export interface CoreState {
|
||||
collectionData: CollectionDataState
|
||||
}
|
||||
|
||||
export const reducers = {
|
||||
collectionData: collectionDataReducer,
|
||||
};
|
||||
|
||||
export function coreReducer(state: any, action: any) {
|
||||
return combineReducers(reducers)(state, action);
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Actions, Effect } from "@ngrx/effects";
|
||||
import { Collection } from "../../shared/collection.model";
|
||||
import { Observable } from "rxjs";
|
||||
import {
|
||||
CollectionFindMultipleActionTypes,
|
||||
CollectionFindMultipleSuccessAction,
|
||||
CollectionFindMultipleErrorAction
|
||||
} from "./collection-find-multiple.actions";
|
||||
import {
|
||||
CollectionFindSingleActionTypes,
|
||||
CollectionFindByIdSuccessAction,
|
||||
CollectionFindByIdErrorAction
|
||||
} from "./collection-find-single.actions";
|
||||
import { DSpaceRESTV2Response } from "../../dspace-rest-v2/dspace-rest-v2-response.model";
|
||||
import { DSpaceRESTv2Serializer } from "../../dspace-rest-v2/dspace-rest-v2.serializer";
|
||||
import { DSpaceRESTv2Service } from "../../dspace-rest-v2/dspace-rest-v2.service";
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class CollectionDataEffects {
|
||||
constructor(
|
||||
private actions$: Actions,
|
||||
private restApiService: DSpaceRESTv2Service
|
||||
) {}
|
||||
|
||||
@Effect() findAll$ = this.actions$
|
||||
.ofType(CollectionFindMultipleActionTypes.FIND_MULTI_REQUEST)
|
||||
.switchMap(() => {
|
||||
return this.restApiService.get('/collections')
|
||||
.map((data: DSpaceRESTV2Response) => new DSpaceRESTv2Serializer(Collection).deserializeArray(data))
|
||||
.map((collections: Collection[]) => new CollectionFindMultipleSuccessAction(collections))
|
||||
.catch((errorMsg: string) => Observable.of(new CollectionFindMultipleErrorAction(errorMsg)));
|
||||
});
|
||||
|
||||
@Effect() findById$ = this.actions$
|
||||
.ofType(CollectionFindSingleActionTypes.FIND_BY_ID_REQUEST)
|
||||
.switchMap(action => {
|
||||
return this.restApiService.get(`/collections/${action.payload}`)
|
||||
.map((data: DSpaceRESTV2Response) => {
|
||||
const t = new DSpaceRESTv2Serializer(Collection).deserialize(data);
|
||||
return t;
|
||||
})
|
||||
.map((collection: Collection) => new CollectionFindByIdSuccessAction(collection))
|
||||
.catch((errorMsg: string) => Observable.of(new CollectionFindByIdErrorAction(errorMsg)));
|
||||
});
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import { combineReducers } from "@ngrx/store";
|
||||
import { CollectionFindMultipleState, findMultipleReducer } from "./collection-find-multiple.reducer";
|
||||
import { CollectionFindSingleState, findSingleReducer } from "./collection-find-single.reducer";
|
||||
|
||||
export interface CollectionDataState {
|
||||
findMultiple: CollectionFindMultipleState,
|
||||
findSingle: CollectionFindSingleState
|
||||
}
|
||||
|
||||
const reducers = {
|
||||
findMultiple: findMultipleReducer,
|
||||
findSingle: findSingleReducer
|
||||
};
|
||||
|
||||
export function collectionDataReducer(state: any, action: any) {
|
||||
return combineReducers(reducers)(state, action);
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
import { CollectionDataState } from "./collection-data.reducer";
|
||||
import { Store } from "@ngrx/store";
|
||||
import { Collection } from "../../shared/collection.model";
|
||||
import { CollectionFindMultipleRequestAction } from "./collection-find-multiple.actions";
|
||||
import { CollectionFindByIdRequestAction } from "./collection-find-single.actions";
|
||||
import { isNotEmpty } from "../../../shared/empty.util";
|
||||
import 'rxjs/add/operator/filter';
|
||||
|
||||
@Injectable()
|
||||
export class CollectionDataService {
|
||||
constructor(
|
||||
private store: Store<CollectionDataState>
|
||||
) { }
|
||||
|
||||
findAll(scope?: Collection): Observable<Collection[]> {
|
||||
this.store.dispatch(new CollectionFindMultipleRequestAction(scope));
|
||||
return this.store.select<Collection[]>('core', 'collectionData', 'findMultiple', 'collections');
|
||||
}
|
||||
|
||||
findById(id: string): Observable<Collection> {
|
||||
this.store.dispatch(new CollectionFindByIdRequestAction(id));
|
||||
return this.store.select<Collection>('core', 'collectionData', 'findSingle', 'collection')
|
||||
//this filter is necessary because the same collection
|
||||
//object in the state is used for every findById call
|
||||
.filter(collection => isNotEmpty(collection) && collection.id === id);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
import { Action } from "@ngrx/store";
|
||||
import { type } from "../../../shared/ngrx/type";
|
||||
import { Collection } from "../../shared/collection.model";
|
||||
import { PaginationOptions } from "../../shared/pagination-options.model";
|
||||
import { SortOptions } from "../../shared/sort-options.model";
|
||||
|
||||
export const CollectionFindMultipleActionTypes = {
|
||||
FIND_MULTI_REQUEST: type('dspace/core/data/collection/FIND_MULTI_REQUEST'),
|
||||
FIND_MULTI_SUCCESS: type('dspace/core/data/collection/FIND_MULTI_SUCCESS'),
|
||||
FIND_MULTI_ERROR: type('dspace/core/data/collection/FIND_MULTI_ERROR')
|
||||
};
|
||||
|
||||
export class CollectionFindMultipleRequestAction implements Action {
|
||||
type = CollectionFindMultipleActionTypes.FIND_MULTI_REQUEST;
|
||||
payload: {
|
||||
scope: Collection,
|
||||
paginationOptions: PaginationOptions,
|
||||
sortOptions: SortOptions
|
||||
};
|
||||
|
||||
constructor(
|
||||
scope?: Collection,
|
||||
paginationOptions: PaginationOptions = new PaginationOptions(),
|
||||
sortOptions: SortOptions = new SortOptions()
|
||||
) {
|
||||
this.payload = {
|
||||
scope,
|
||||
paginationOptions,
|
||||
sortOptions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CollectionFindMultipleSuccessAction implements Action {
|
||||
type = CollectionFindMultipleActionTypes.FIND_MULTI_SUCCESS;
|
||||
payload: Collection[];
|
||||
|
||||
constructor(collections: Collection[]) {
|
||||
this.payload = collections;
|
||||
}
|
||||
}
|
||||
|
||||
export class CollectionFindMultipleErrorAction implements Action {
|
||||
type = CollectionFindMultipleActionTypes.FIND_MULTI_ERROR;
|
||||
payload: string;
|
||||
|
||||
constructor(errorMessage: string) {
|
||||
this.payload = errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
export type CollectionFindMultipleAction
|
||||
= CollectionFindMultipleRequestAction
|
||||
| CollectionFindMultipleSuccessAction
|
||||
| CollectionFindMultipleErrorAction;
|
@@ -0,0 +1,60 @@
|
||||
import { Collection } from "../../shared/collection.model";
|
||||
import { PaginationOptions } from "../../shared/pagination-options.model";
|
||||
import { SortOptions } from "../../shared/sort-options.model";
|
||||
import {
|
||||
CollectionFindMultipleAction,
|
||||
CollectionFindMultipleActionTypes
|
||||
} from "./collection-find-multiple.actions";
|
||||
|
||||
export interface CollectionFindMultipleState {
|
||||
scope: Collection;
|
||||
collections: Collection[];
|
||||
isLoading: boolean;
|
||||
errorMessage: string;
|
||||
paginationOptions: PaginationOptions;
|
||||
sortOptions: SortOptions;
|
||||
}
|
||||
|
||||
const initialState: CollectionFindMultipleState = {
|
||||
scope: undefined,
|
||||
collections: [],
|
||||
isLoading: false,
|
||||
errorMessage: undefined,
|
||||
paginationOptions: undefined,
|
||||
sortOptions: undefined
|
||||
};
|
||||
|
||||
export const findMultipleReducer = (state = initialState, action: CollectionFindMultipleAction): CollectionFindMultipleState => {
|
||||
switch (action.type) {
|
||||
|
||||
case CollectionFindMultipleActionTypes.FIND_MULTI_REQUEST: {
|
||||
return Object.assign({}, state, {
|
||||
scope: action.payload.scope,
|
||||
collections: [],
|
||||
isLoading: true,
|
||||
errorMessage: undefined,
|
||||
paginationOptions: action.payload.paginationOptions,
|
||||
sortOptions: action.payload.sortOptions
|
||||
});
|
||||
}
|
||||
|
||||
case CollectionFindMultipleActionTypes.FIND_MULTI_SUCCESS: {
|
||||
return Object.assign({}, state, {
|
||||
isLoading: false,
|
||||
collections: action.payload,
|
||||
errorMessage: undefined
|
||||
});
|
||||
}
|
||||
|
||||
case CollectionFindMultipleActionTypes.FIND_MULTI_ERROR: {
|
||||
return Object.assign({}, state, {
|
||||
isLoading: false,
|
||||
errorMessage: action.payload
|
||||
});
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
@@ -0,0 +1,42 @@
|
||||
import { Action } from "@ngrx/store";
|
||||
import { type } from "../../../shared/ngrx/type";
|
||||
import { Collection } from "../../shared/collection.model";
|
||||
|
||||
export const CollectionFindSingleActionTypes = {
|
||||
FIND_BY_ID_REQUEST: type('dspace/core/data/collection/FIND_BY_ID_REQUEST'),
|
||||
FIND_BY_ID_SUCCESS: type('dspace/core/data/collection/FIND_BY_ID_SUCCESS'),
|
||||
FIND_BY_ID_ERROR: type('dspace/core/data/collection/FIND_BY_ID_ERROR')
|
||||
};
|
||||
|
||||
export class CollectionFindByIdRequestAction implements Action {
|
||||
type = CollectionFindSingleActionTypes.FIND_BY_ID_REQUEST;
|
||||
payload: string;
|
||||
|
||||
constructor(id: string) {
|
||||
this.payload = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class CollectionFindByIdSuccessAction implements Action {
|
||||
type = CollectionFindSingleActionTypes.FIND_BY_ID_SUCCESS;
|
||||
payload: Collection;
|
||||
|
||||
constructor(collection: Collection) {
|
||||
this.payload = collection;
|
||||
}
|
||||
}
|
||||
|
||||
export class CollectionFindByIdErrorAction implements Action {
|
||||
type = CollectionFindSingleActionTypes.FIND_BY_ID_ERROR;
|
||||
payload: string;
|
||||
|
||||
constructor(errorMessage: string) {
|
||||
this.payload = errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
export type CollectionFindSingleAction
|
||||
= CollectionFindByIdRequestAction
|
||||
| CollectionFindByIdSuccessAction
|
||||
| CollectionFindByIdErrorAction;
|
||||
|
@@ -0,0 +1,52 @@
|
||||
import { Collection } from "../../shared/collection.model";
|
||||
import {
|
||||
CollectionFindSingleAction,
|
||||
CollectionFindSingleActionTypes
|
||||
} from "./collection-find-single.actions";
|
||||
|
||||
export interface CollectionFindSingleState {
|
||||
collection: Collection;
|
||||
isLoading: boolean;
|
||||
errorMessage: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const initialState: CollectionFindSingleState = {
|
||||
collection: undefined,
|
||||
isLoading: false,
|
||||
errorMessage: undefined,
|
||||
id: undefined,
|
||||
};
|
||||
|
||||
export const findSingleReducer = (state = initialState, action: CollectionFindSingleAction): CollectionFindSingleState => {
|
||||
switch (action.type) {
|
||||
|
||||
case CollectionFindSingleActionTypes.FIND_BY_ID_REQUEST: {
|
||||
return Object.assign({}, state, {
|
||||
isLoading: true,
|
||||
id: action.payload,
|
||||
collections: undefined,
|
||||
errorMessage: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
case CollectionFindSingleActionTypes.FIND_BY_ID_SUCCESS: {
|
||||
return Object.assign({}, state, {
|
||||
isLoading: false,
|
||||
collection: action.payload,
|
||||
errorMessage: undefined
|
||||
});
|
||||
}
|
||||
|
||||
case CollectionFindSingleActionTypes.FIND_BY_ID_ERROR: {
|
||||
return Object.assign({}, state, {
|
||||
isLoading: false,
|
||||
errorMessage: action.payload
|
||||
});
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
34
src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts
Normal file
34
src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http, RequestOptionsArgs } from '@angular/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { RESTURLCombiner } from "../url-combiner/rest-url-combiner";
|
||||
|
||||
/**
|
||||
* Service to access DSpace's REST API
|
||||
*/
|
||||
@Injectable()
|
||||
export class DSpaceRESTv2Service {
|
||||
constructor(public _http: Http) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a request to the REST API with the `get` http method.
|
||||
*
|
||||
* @param relativeURL
|
||||
* A URL, relative to the basepath of the rest api
|
||||
* @param options
|
||||
* A RequestOptionsArgs object, with options for the http call.
|
||||
* @return {Observable<string>}
|
||||
* An Observablse<string> containing the response from the server
|
||||
*/
|
||||
get(relativeURL: string, options?: RequestOptionsArgs): Observable<string> {
|
||||
return this._http.get(new RESTURLCombiner(relativeURL).toString(), options)
|
||||
.map(res => res.json())
|
||||
.catch(err => {
|
||||
console.log('Error: ', err);
|
||||
return Observable.throw(err);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
37
src/app/core/shared/bitstream.model.ts
Normal file
37
src/app/core/shared/bitstream.model.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { inheritSerialization } from "cerialize";
|
||||
import { DSpaceObject } from "./dspace-object.model";
|
||||
import { Bundle } from "./bundle.model";
|
||||
|
||||
@inheritSerialization(DSpaceObject)
|
||||
export class Bitstream extends DSpaceObject {
|
||||
|
||||
/**
|
||||
* The size of this bitstream in bytes(?)
|
||||
*/
|
||||
size: number;
|
||||
|
||||
/**
|
||||
* The relative path to this Bitstream's file
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* The mime type of this Bitstream
|
||||
*/
|
||||
mimetype: string;
|
||||
|
||||
/**
|
||||
* The description of this Bitstream
|
||||
*/
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* An array of Bundles that are direct parents of this Bitstream
|
||||
*/
|
||||
parents: Array<Bundle>;
|
||||
|
||||
/**
|
||||
* The Bundle that owns this Bitstream
|
||||
*/
|
||||
owner: Bundle;
|
||||
}
|
22
src/app/core/shared/bundle.model.ts
Normal file
22
src/app/core/shared/bundle.model.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { inheritSerialization } from "cerialize";
|
||||
import { DSpaceObject } from "./dspace-object.model";
|
||||
import { Bitstream } from "./bitstream.model";
|
||||
import { Item } from "./item.model";
|
||||
|
||||
@inheritSerialization(DSpaceObject)
|
||||
export class Bundle extends DSpaceObject {
|
||||
/**
|
||||
* The primary bitstream of this Bundle
|
||||
*/
|
||||
primaryBitstream: Bitstream;
|
||||
|
||||
/**
|
||||
* An array of Items that are direct parents of this Bundle
|
||||
*/
|
||||
parents: Array<Item>;
|
||||
|
||||
/**
|
||||
* The Item that owns this Bundle
|
||||
*/
|
||||
owner: Item;
|
||||
}
|
69
src/app/core/shared/collection.model.ts
Normal file
69
src/app/core/shared/collection.model.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { autoserialize, inheritSerialization } from "cerialize";
|
||||
import { DSpaceObject } from "./dspace-object.model";
|
||||
import { Bitstream } from "./bitstream.model";
|
||||
|
||||
@inheritSerialization(DSpaceObject)
|
||||
export class Collection extends DSpaceObject {
|
||||
|
||||
/**
|
||||
* A string representing the unique handle of this Collection
|
||||
*/
|
||||
@autoserialize
|
||||
handle: string;
|
||||
|
||||
/**
|
||||
* The introductory text of this Collection
|
||||
* Corresponds to the metadata field dc.description
|
||||
*/
|
||||
get introductoryText(): string {
|
||||
return this.findMetadata("dc.description");
|
||||
}
|
||||
|
||||
/**
|
||||
* The short description: HTML
|
||||
* Corresponds to the metadata field dc.description.abstract
|
||||
*/
|
||||
get shortDescription(): string {
|
||||
return this.findMetadata("dc.description.abstract");
|
||||
}
|
||||
|
||||
/**
|
||||
* The copyright text of this Collection
|
||||
* Corresponds to the metadata field dc.rights
|
||||
*/
|
||||
get copyrightText(): string {
|
||||
return this.findMetadata("dc.rights");
|
||||
}
|
||||
|
||||
/**
|
||||
* The license of this Collection
|
||||
* Corresponds to the metadata field dc.rights.license
|
||||
*/
|
||||
get license(): string {
|
||||
return this.findMetadata("dc.rights.license");
|
||||
}
|
||||
|
||||
/**
|
||||
* The sidebar text of this Collection
|
||||
* Corresponds to the metadata field dc.description.tableofcontents
|
||||
*/
|
||||
get sidebarText(): string {
|
||||
return this.findMetadata("dc.description.tableofcontents");
|
||||
}
|
||||
|
||||
/**
|
||||
* The Bitstream that represents the logo of this Collection
|
||||
*/
|
||||
logo: Bitstream;
|
||||
|
||||
/**
|
||||
* An array of Collections that are direct parents of this Collection
|
||||
*/
|
||||
parents: Array<Collection>;
|
||||
|
||||
/**
|
||||
* The Collection that owns this Collection
|
||||
*/
|
||||
owner: Collection;
|
||||
|
||||
}
|
67
src/app/core/shared/dspace-object.model.ts
Normal file
67
src/app/core/shared/dspace-object.model.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { autoserialize, autoserializeAs } from "cerialize";
|
||||
import { Metadatum } from "./metadatum.model"
|
||||
import { isEmpty, isNotEmpty } from "../../shared/empty.util";
|
||||
|
||||
/**
|
||||
* An abstract model class for a DSpaceObject.
|
||||
*/
|
||||
export abstract class DSpaceObject {
|
||||
|
||||
/**
|
||||
* The identifier of this DSpaceObject
|
||||
*/
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* A string representing the kind of DSpaceObject, e.g. community, item, …
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* The name for this DSpaceObject
|
||||
*/
|
||||
@autoserialize
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* An array containing all metadata of this DSpaceObject
|
||||
*/
|
||||
@autoserializeAs(Metadatum)
|
||||
metadata: Array<Metadatum>;
|
||||
|
||||
/**
|
||||
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
||||
*/
|
||||
parents: Array<DSpaceObject>;
|
||||
|
||||
/**
|
||||
* The DSpaceObject that owns this DSpaceObject
|
||||
*/
|
||||
owner: DSpaceObject;
|
||||
|
||||
/**
|
||||
* Find a metadata field by key and language
|
||||
*
|
||||
* This method returns the value of the first element
|
||||
* in the metadata array that matches the provided
|
||||
* key and language
|
||||
*
|
||||
* @param key
|
||||
* @param language
|
||||
* @return string
|
||||
*/
|
||||
findMetadata(key: string, language?: string): string {
|
||||
const metadatum = this.metadata
|
||||
.find((metadatum: Metadatum) => {
|
||||
return metadatum.key === key &&
|
||||
(isEmpty(language) || metadatum.language === language)
|
||||
});
|
||||
if (isNotEmpty(metadatum)) {
|
||||
return metadatum.value;
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
38
src/app/core/shared/item.model.ts
Normal file
38
src/app/core/shared/item.model.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { inheritSerialization, autoserialize } from "cerialize";
|
||||
import { DSpaceObject } from "./dspace-object.model";
|
||||
import { Collection } from "./collection.model";
|
||||
|
||||
@inheritSerialization(DSpaceObject)
|
||||
export class Item extends DSpaceObject {
|
||||
|
||||
/**
|
||||
* A string representing the unique handle of this Item
|
||||
*/
|
||||
@autoserialize
|
||||
handle: string;
|
||||
|
||||
/**
|
||||
* The Date of the last modification of this Item
|
||||
*/
|
||||
lastModified: Date;
|
||||
|
||||
/**
|
||||
* A boolean representing if this Item is currently archived or not
|
||||
*/
|
||||
isArchived: boolean;
|
||||
|
||||
/**
|
||||
* A boolean representing if this Item is currently withdrawn or not
|
||||
*/
|
||||
isWithdrawn: boolean;
|
||||
|
||||
/**
|
||||
* An array of Collections that are direct parents of this Item
|
||||
*/
|
||||
parents: Array<Collection>;
|
||||
|
||||
/**
|
||||
* The Collection that owns this Item
|
||||
*/
|
||||
owner: Collection;
|
||||
}
|
21
src/app/core/shared/metadatum.model.ts
Normal file
21
src/app/core/shared/metadatum.model.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { autoserialize } from "cerialize";
|
||||
export class Metadatum {
|
||||
|
||||
/**
|
||||
* The metadata field of this Metadatum
|
||||
*/
|
||||
@autoserialize
|
||||
key: string;
|
||||
|
||||
/**
|
||||
* The language of this Metadatum
|
||||
*/
|
||||
@autoserialize
|
||||
language: string;
|
||||
|
||||
/**
|
||||
* The value of this Metadatum
|
||||
*/
|
||||
@autoserialize
|
||||
value: string;
|
||||
}
|
12
src/app/core/shared/pagination-options.model.ts
Normal file
12
src/app/core/shared/pagination-options.model.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export class PaginationOptions {
|
||||
/**
|
||||
* The number of results per page.
|
||||
*/
|
||||
resultsPerPage: number = 10;
|
||||
|
||||
/**
|
||||
* The active page.
|
||||
*/
|
||||
currentPage: number = 1;
|
||||
|
||||
}
|
9
src/app/core/shared/sort-options.model.ts
Normal file
9
src/app/core/shared/sort-options.model.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export enum SortDirection {
|
||||
Ascending,
|
||||
Descending
|
||||
}
|
||||
|
||||
export class SortOptions {
|
||||
field: string = "id";
|
||||
direction: SortDirection = SortDirection.Ascending
|
||||
}
|
14
src/app/core/url-combiner/rest-url-combiner.ts
Normal file
14
src/app/core/url-combiner/rest-url-combiner.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { URLCombiner } from "./url-combiner";
|
||||
import { GlobalConfig } from "../../../config";
|
||||
|
||||
/**
|
||||
* Combines a variable number of strings representing parts
|
||||
* of a relative REST URL in to a single, absolute REST URL
|
||||
*
|
||||
* TODO write tests once GlobalConfig becomes injectable
|
||||
*/
|
||||
export class RESTURLCombiner extends URLCombiner{
|
||||
constructor(...parts:Array<string>) {
|
||||
super(GlobalConfig.rest.baseURL, GlobalConfig.rest.nameSpace, ...parts);
|
||||
}
|
||||
}
|
14
src/app/core/url-combiner/ui-url-combiner.ts
Normal file
14
src/app/core/url-combiner/ui-url-combiner.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { URLCombiner } from "./url-combiner";
|
||||
import { GlobalConfig } from "../../../config";
|
||||
|
||||
/**
|
||||
* Combines a variable number of strings representing parts
|
||||
* of a relative UI URL in to a single, absolute UI URL
|
||||
*
|
||||
* TODO write tests once GlobalConfig becomes injectable
|
||||
*/
|
||||
export class UIURLCombiner extends URLCombiner{
|
||||
constructor(...parts:Array<string>) {
|
||||
super(GlobalConfig.ui.baseURL, GlobalConfig.ui.nameSpace, ...parts);
|
||||
}
|
||||
}
|
33
src/app/core/url-combiner/url-combiner.spec.ts
Normal file
33
src/app/core/url-combiner/url-combiner.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { URLCombiner } from "./url-combiner";
|
||||
|
||||
describe("URLCombiner", () => {
|
||||
|
||||
it("should return a valid URL when created with a valid set of url parts", () => {
|
||||
const url = new URLCombiner('http://foo.com', 'bar', 'id', '5').toString();
|
||||
expect(url).toBe('http://foo.com/bar/id/5');
|
||||
});
|
||||
|
||||
it("should return a URL with the protocol followed by two slashes", () => {
|
||||
const url = new URLCombiner('http:/foo.com').toString();
|
||||
expect(url).toBe('http://foo.com');
|
||||
});
|
||||
|
||||
it("should return a URL with a single slash between each part", () => {
|
||||
const url = new URLCombiner('http://foo.com/', '/bar/', '//id', '///5').toString();
|
||||
expect(url).toBe('http://foo.com/bar/id/5');
|
||||
});
|
||||
|
||||
it("should return a URL without a trailing slash before its parameters", () => {
|
||||
const url1 = new URLCombiner('http://foo.com/', '?bar=25').toString();
|
||||
const url2 = new URLCombiner('http://foo.com/', '#bar').toString();
|
||||
|
||||
expect(url1).toBe('http://foo.com?bar=25');
|
||||
expect(url2).toBe('http://foo.com#bar');
|
||||
});
|
||||
|
||||
it("should return an empty string when created without url parts", () => {
|
||||
const url = new URLCombiner().toString();
|
||||
expect(url).toBe('');
|
||||
});
|
||||
|
||||
});
|
55
src/app/core/url-combiner/url-combiner.ts
Normal file
55
src/app/core/url-combiner/url-combiner.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { isEmpty } from "../../shared/empty.util";
|
||||
|
||||
/**
|
||||
* Combines a variable number of strings representing parts
|
||||
* of a URL in to a single, normalized URL
|
||||
*/
|
||||
export class URLCombiner {
|
||||
private parts: Array<string>;
|
||||
|
||||
/**
|
||||
* Creates a new URLCombiner
|
||||
*
|
||||
* @param parts
|
||||
* a variable number of strings representing parts of a URL
|
||||
*/
|
||||
constructor(...parts:Array<string>) {
|
||||
// can't do this in the constructor signature,
|
||||
// because of the spread operator
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the parts of this URLCombiner in to a single,
|
||||
* normalized URL
|
||||
*
|
||||
* e.g. new URLCombiner('http:/foo.com/', '/bar', 'id', '5').toString()
|
||||
* returns: http://foo.com/bar/id/5
|
||||
*
|
||||
* @return {string}
|
||||
* The combined URL
|
||||
*/
|
||||
toString(): string {
|
||||
if (isEmpty(this.parts)) {
|
||||
return '';
|
||||
}
|
||||
else {
|
||||
let url = this.parts.join('/');
|
||||
|
||||
// make sure protocol is followed by two slashes
|
||||
url = url.replace(/:\//g, '://');
|
||||
|
||||
// remove consecutive slashes
|
||||
url = url.replace(/([^:\s])\/+/g, '$1/');
|
||||
|
||||
// remove trailing slash before parameters or hash
|
||||
url = url.replace(/\/(\?|&|#[^!])/g, '$1');
|
||||
|
||||
// replace ? in parameters with &
|
||||
url = url.replace(/(\?.+)\?/g, '$1&');
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -6,6 +6,8 @@ import { fakeDataBase } from './db';
|
||||
import { fakeDemoRedisCache } from './cache';
|
||||
import { COLLECTIONS } from "./collections";
|
||||
import { ITEMS } from "./items";
|
||||
import { BUNDLES } from "./bundles";
|
||||
import { BITSTREAMS } from "./bitstreams";
|
||||
import { METADATA } from "./metadata";
|
||||
|
||||
// you would use cookies/token etc
|
||||
@@ -30,14 +32,10 @@ export function serverApi(req, res) {
|
||||
}
|
||||
|
||||
|
||||
let COLLECTION_COUNT = 2;
|
||||
let ITEM_COUNT = 2;
|
||||
|
||||
|
||||
function toJSONAPIResponse(req, data, included?) {
|
||||
function toHALResponse(req, data, included?) {
|
||||
let result = {
|
||||
"data": data,
|
||||
"links": {
|
||||
"_embedded": data,
|
||||
"_links": {
|
||||
"self": req.protocol + '://' + req.get('host') + req.originalUrl
|
||||
}
|
||||
};
|
||||
@@ -58,7 +56,7 @@ export function createMockApi() {
|
||||
console.log('GET');
|
||||
// 70ms latency
|
||||
setTimeout(function() {
|
||||
res.json(toJSONAPIResponse(req, COLLECTIONS));
|
||||
res.json(toHALResponse(req, COLLECTIONS));
|
||||
}, 0);
|
||||
|
||||
// })
|
||||
@@ -84,7 +82,7 @@ export function createMockApi() {
|
||||
try {
|
||||
req.collection_id = id;
|
||||
req.collection = COLLECTIONS.find((collection) => {
|
||||
return collection.id = id;
|
||||
return collection.id === id;
|
||||
});
|
||||
next();
|
||||
} catch (e) {
|
||||
@@ -95,7 +93,7 @@ export function createMockApi() {
|
||||
router.route('/collections/:collection_id')
|
||||
.get(function(req, res) {
|
||||
console.log('GET', util.inspect(req.collection, { colors: true }));
|
||||
res.json(toJSONAPIResponse(req, req.collection));
|
||||
res.json(toHALResponse(req, req.collection));
|
||||
// })
|
||||
// .put(function(req, res) {
|
||||
// console.log('PUT', util.inspect(req.body, { colors: true }));
|
||||
@@ -120,7 +118,7 @@ export function createMockApi() {
|
||||
console.log('GET');
|
||||
// 70ms latency
|
||||
setTimeout(function() {
|
||||
res.json(toJSONAPIResponse(req, ITEMS));
|
||||
res.json(toHALResponse(req, ITEMS));
|
||||
}, 0);
|
||||
|
||||
// })
|
||||
@@ -161,7 +159,7 @@ export function createMockApi() {
|
||||
const itemMetadata: any[] = METADATA.filter((metadatum) => {
|
||||
return metadataIds.indexOf(metadatum.id) >= 0
|
||||
});
|
||||
res.json(toJSONAPIResponse(req, req.item, itemMetadata));
|
||||
res.json(toHALResponse(req, req.item, itemMetadata));
|
||||
// })
|
||||
// .put(function(req, res) {
|
||||
// console.log('PUT', util.inspect(req.body, { colors: true }));
|
||||
@@ -180,5 +178,72 @@ export function createMockApi() {
|
||||
// res.json(req.item);
|
||||
});
|
||||
|
||||
router.route('/bundles')
|
||||
.get(function(req, res) {
|
||||
console.log('GET');
|
||||
// 70ms latency
|
||||
setTimeout(function() {
|
||||
res.json(toHALResponse(req, BUNDLES));
|
||||
}, 0);
|
||||
});
|
||||
|
||||
router.param('bundle_id', function(req, res, next, bundle_id) {
|
||||
// ensure correct prop type
|
||||
let id = req.params.bundle_id;
|
||||
try {
|
||||
req.bundle_id = id;
|
||||
req.bundle = BUNDLES.find((bundle) => {
|
||||
return bundle.id === id;
|
||||
});
|
||||
next();
|
||||
} catch (e) {
|
||||
next(new Error('failed to load item'));
|
||||
}
|
||||
});
|
||||
|
||||
router.route('/bundles/:bundle_id')
|
||||
.get(function(req, res) {
|
||||
console.log('GET', util.inspect(req.bundle, { colors: true }));
|
||||
const metadataIds: string[] = req.bundle.relationships.metadata.data.map(obj => obj.id);
|
||||
const bundleMetadata: any[] = METADATA.filter((metadatum) => {
|
||||
return metadataIds.indexOf(metadatum.id) >= 0
|
||||
});
|
||||
res.json(toHALResponse(req, req.bundle, bundleMetadata));
|
||||
});
|
||||
|
||||
|
||||
router.route('/bitstreams')
|
||||
.get(function(req, res) {
|
||||
console.log('GET');
|
||||
// 70ms latency
|
||||
setTimeout(function() {
|
||||
res.json(toHALResponse(req, BITSTREAMS));
|
||||
}, 0);
|
||||
});
|
||||
|
||||
router.param('bitstream_id', function(req, res, next, bitstream_id) {
|
||||
// ensure correct prop type
|
||||
let id = req.params.bitstream_id;
|
||||
try {
|
||||
req.bitstream_id = id;
|
||||
req.bitstream = BITSTREAMS.find((bitstream) => {
|
||||
return bitstream.id === id;
|
||||
});
|
||||
next();
|
||||
} catch (e) {
|
||||
next(new Error('failed to load item'));
|
||||
}
|
||||
});
|
||||
|
||||
router.route('/bitstreams/:bitstream_id')
|
||||
.get(function(req, res) {
|
||||
console.log('GET', util.inspect(req.bitstream, { colors: true }));
|
||||
const metadataIds: string[] = req.bitstream.relationships.metadata.data.map(obj => obj.id);
|
||||
const bitstreamMetadata: any[] = METADATA.filter((metadatum) => {
|
||||
return metadataIds.indexOf(metadatum.id) >= 0
|
||||
});
|
||||
res.json(toHALResponse(req, req.bitstream, bitstreamMetadata));
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
42
src/backend/bitstreams.ts
Normal file
42
src/backend/bitstreams.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export const BITSTREAMS = [
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/bitstreams/43c57c2b-206f-4645-8c8f-5f10c84b09fa" },
|
||||
"bundle": { "href": "/bundles/35e0606d-5e18-4f9c-aa61-74fc751cc3f9" },
|
||||
"retrieve": { "href": "/bitstreams/43c57c2b-206f-4645-8c8f-5f10c84b09fa/retrieve" }
|
||||
},
|
||||
"id": "43c57c2b-206f-4645-8c8f-5f10c84b09fa",
|
||||
"name": "do_open_access_CRL.pdf",
|
||||
"size": 636626,
|
||||
"checksum": {
|
||||
"value": "063dfbbbac873aa3fca479b878eccff3",
|
||||
"algorithm": "MD5"
|
||||
},
|
||||
"metadata": [
|
||||
{ "key": "dc.title", "value": "do_open_access_CRL.pdf", "language": null },
|
||||
{ "key": "dc.description", "value": "Conference Paper", "language": "en" }
|
||||
],
|
||||
"format": "Adobe PDF",
|
||||
"mimetype": "application/pdf"
|
||||
},
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/bitstreams/1a013ecc-fb25-4689-a44f-f1383ad26632" },
|
||||
"bundle": { "href": "/bundles/a469c57a-abcf-45c3-83e4-b187ebd708fd" },
|
||||
"retrieve": { "href": "/rest/bitstreams/1a013ecc-fb25-4689-a44f-f1383ad26632/retrieve" }
|
||||
},
|
||||
"id": "1a013ecc-fb25-4689-a44f-f1383ad26632",
|
||||
"name": "do_open_access_CRL.pdf.jpg",
|
||||
"size": 41183,
|
||||
"checksum": {
|
||||
"value": "a8ad475e86f9645c60e13e06f1427814",
|
||||
"algorithm": "MD5"
|
||||
},
|
||||
"metadata": [
|
||||
{ "key": "dc.title", "value": "do_open_access_CRL.pdf.jpg", "language": null },
|
||||
{ "key": "dc.description", "value": "Generated Thumbnail", "language": "en" }
|
||||
],
|
||||
"format": "JPEG",
|
||||
"mimetype": "image/jpeg"
|
||||
}
|
||||
];
|
36
src/backend/bundles.ts
Normal file
36
src/backend/bundles.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export const BUNDLES = [
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/bundles/35e0606d-5e18-4f9c-aa61-74fc751cc3f9" },
|
||||
"items": [
|
||||
{ "href": "/items/21539b1d-9ef1-4eda-9c77-49565b5bfb78" }
|
||||
],
|
||||
"bitstreams": [
|
||||
{ "href": "/bitstreams/43c57c2b-206f-4645-8c8f-5f10c84b09fa" },
|
||||
],
|
||||
"primaryBitstream": { "href": "/bitstreams/43c57c2b-206f-4645-8c8f-5f10c84b09fa" }
|
||||
},
|
||||
"id": "35e0606d-5e18-4f9c-aa61-74fc751cc3f9",
|
||||
"name": "ORIGINAL",
|
||||
"metadata": [
|
||||
{ "key": "dc.title", "value": "ORIGINAL", "language": "en" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"_links": {
|
||||
"self": { "href": "/bundles/a469c57a-abcf-45c3-83e4-b187ebd708fd" },
|
||||
"items": [
|
||||
{ "href": "/items/21539b1d-9ef1-4eda-9c77-49565b5bfb78" }
|
||||
],
|
||||
"bitstreams": [
|
||||
{ "href": "/bitstreams/1a013ecc-fb25-4689-a44f-f1383ad26632" },
|
||||
],
|
||||
"primaryBitstream": { "href": "/bitstreams/1a013ecc-fb25-4689-a44f-f1383ad26632" }
|
||||
},
|
||||
"id": "a469c57a-abcf-45c3-83e4-b187ebd708fd",
|
||||
"name": "THUMBNAIL",
|
||||
"metadata": [
|
||||
{ "key": "dc.title", "value": "THUMBNAIL", "language": "en" }
|
||||
]
|
||||
}
|
||||
];
|
@@ -1,42 +1,70 @@
|
||||
export const COLLECTIONS = [
|
||||
{
|
||||
"id": "9e32a2e2-6b91-4236-a361-995ccdc14c60",
|
||||
"type": "collections",
|
||||
"attributes": {
|
||||
"name": "A Test Collection",
|
||||
"handle": "123456789/5179",
|
||||
"copyrightText": "<p>© 2005-2016 JOHN DOE SOME RIGHTS RESERVED</p>",
|
||||
"introductoryText": "<p class='lead'>An introductory text dolor sit amet, consectetur adipiscing elit. Duis laoreet lorem erat, eget auctor est ultrices quis. Nullam ac tincidunt quam. In nec nisl odio. In egestas aliquam tincidunt.</p>\r\n<p>Integer vitae diam id dolor pharetra dignissim in sed enim. Vivamus pulvinar tristique sem a iaculis. Aenean ultricies dui vel facilisis laoreet. Integer porta erat eu ultrices rhoncus. Sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum.</p>",
|
||||
"shortDescription": "A collection for testing purposes",
|
||||
"sidebarText": "<p>Some news sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum. Donec sed ultricies erat, nec sollicitudin mauris. Duis varius nulla quis quam vulputate, at hendrerit turpis rutrum. Integer nec facilisis sapien. Fusce fringilla malesuada lectus id pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae</p>"
|
||||
"_links": {
|
||||
"self": { "href": "/collections/9e32a2e2-6b91-4236-a361-995ccdc14c60" },
|
||||
"items": [
|
||||
{ "href": "/items/21539b1d-9ef1-4eda-9c77-49565b5bfb78" },
|
||||
{ "href": "/items/be8325f7-243b-49f4-8a4b-df2b793ff3b5" }
|
||||
]
|
||||
},
|
||||
"relationships": {
|
||||
"items": {
|
||||
"data": [
|
||||
{ "type": "items", "id": "21539b1d-9ef1-4eda-9c77-49565b5bfb78" },
|
||||
{ "type": "items", "id": "be8325f7-243b-49f4-8a4b-df2b793ff3b5" }
|
||||
]
|
||||
"id": "9e32a2e2-6b91-4236-a361-995ccdc14c60",
|
||||
"name": "A Test Collection",
|
||||
"handle": "123456789/5179",
|
||||
"metadata": [
|
||||
{
|
||||
"key": "dc.rights",
|
||||
"value": "<p>© 2005-2016 JOHN DOE SOME RIGHTS RESERVED</p>",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description",
|
||||
"value": "<p class='lead'>An introductory text dolor sit amet, consectetur adipiscing elit. Duis laoreet lorem erat, eget auctor est ultrices quis. Nullam ac tincidunt quam. In nec nisl odio. In egestas aliquam tincidunt.</p>\r\n<p>Integer vitae diam id dolor pharetra dignissim in sed enim. Vivamus pulvinar tristique sem a iaculis. Aenean ultricies dui vel facilisis laoreet. Integer porta erat eu ultrices rhoncus. Sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum.</p>",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description.abstract",
|
||||
"value": "A collection for testing purposes",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description.tableofcontents",
|
||||
"value": "<p>Some news sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum. Donec sed ultricies erat, nec sollicitudin mauris. Duis varius nulla quis quam vulputate, at hendrerit turpis rutrum. Integer nec facilisis sapien. Fusce fringilla malesuada lectus id pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae</p>",
|
||||
"language": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "598ce822-c357-46f3-ab70-63724d02d6ad",
|
||||
"type": "collections",
|
||||
"attributes": {
|
||||
"name": "Another Test Collection",
|
||||
"handle": "123456789/6547",
|
||||
"copyrightText": "<p>© 2005-2016 JOHN DOE SOME RIGHTS RESERVED</p>",
|
||||
"introductoryText": "<p class='lead'>Another introductory text dolor sit amet, consectetur adipiscing elit. Duis laoreet lorem erat, eget auctor est ultrices quis. Nullam ac tincidunt quam. In nec nisl odio. In egestas aliquam tincidunt.</p>\r\n<p>Integer vitae diam id dolor pharetra dignissim in sed enim. Vivamus pulvinar tristique sem a iaculis. Aenean ultricies dui vel facilisis laoreet. Integer porta erat eu ultrices rhoncus. Sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum.</p>",
|
||||
"shortDescription": "Another collection for testing purposes",
|
||||
"sidebarText": "<p>Some more news sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum. Donec sed ultricies erat, nec sollicitudin mauris. Duis varius nulla quis quam vulputate, at hendrerit turpis rutrum. Integer nec facilisis sapien. Fusce fringilla malesuada lectus id pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae</p>"
|
||||
"_links": {
|
||||
"self": { "href": "/collections/598ce822-c357-46f3-ab70-63724d02d6ad" },
|
||||
"items": [
|
||||
{ "href": "/items/21539b1d-9ef1-4eda-9c77-49565b5bfb78" },
|
||||
{ "href": "/items/be8325f7-243b-49f4-8a4b-df2b793ff3b5" }
|
||||
]
|
||||
},
|
||||
"relationships": {
|
||||
"items": {
|
||||
"data": [
|
||||
{ "type": "items", "id": "21539b1d-9ef1-4eda-9c77-49565b5bfb78" },
|
||||
{ "type": "items", "id": "be8325f7-243b-49f4-8a4b-df2b793ff3b5" }
|
||||
]
|
||||
"id": "598ce822-c357-46f3-ab70-63724d02d6ad",
|
||||
"name": "Another Test Collection",
|
||||
"handle": "123456789/6547",
|
||||
"metadata": [
|
||||
{
|
||||
"key": "dc.rights",
|
||||
"value": "<p>© 2005-2016 JOHN DOE SOME RIGHTS RESERVED</p>",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description",
|
||||
"value": "<p class='lead'>Another introductory text dolor sit amet, consectetur adipiscing elit. Duis laoreet lorem erat, eget auctor est ultrices quis. Nullam ac tincidunt quam. In nec nisl odio. In egestas aliquam tincidunt.</p>\r\n<p>Integer vitae diam id dolor pharetra dignissim in sed enim. Vivamus pulvinar tristique sem a iaculis. Aenean ultricies dui vel facilisis laoreet. Integer porta erat eu ultrices rhoncus. Sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum.</p>",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description.abstract",
|
||||
"value": "Another collection for testing purposes",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description.tableofcontents",
|
||||
"value": "<p>Some more news sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum. Donec sed ultricies erat, nec sollicitudin mauris. Duis varius nulla quis quam vulputate, at hendrerit turpis rutrum. Integer nec facilisis sapien. Fusce fringilla malesuada lectus id pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae</p>",
|
||||
"language": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
@@ -1,128 +1,164 @@
|
||||
export const ITEMS = [
|
||||
{
|
||||
"id": "21539b1d-9ef1-4eda-9c77-49565b5bfb78",
|
||||
"type": "items",
|
||||
"attributes": {
|
||||
"name": "Do Open-Access Articles Have a Greater Research Impact?",
|
||||
"handle": "123456789/8871",
|
||||
"lastModified": "2016-10-14 10:41:12.886",
|
||||
"isArchived": true,
|
||||
"isWithdrawn": false
|
||||
},
|
||||
"relationships": {
|
||||
"collections": {
|
||||
"data": [
|
||||
{ "type": "collections", "id": "9e32a2e2-6b91-4236-a361-995ccdc14c60" },
|
||||
{ "type": "collections", "id": "598ce822-c357-46f3-ab70-63724d02d6ad" }
|
||||
]
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/items/21539b1d-9ef1-4eda-9c77-49565b5bfb78"
|
||||
},
|
||||
"metadata": {
|
||||
"data": [
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "d58a3098-b390-4cd6-8f52-b088b3daa637",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "56660730-0e0d-47ec-864a-bda2327d5716",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "b9d4ae74-2758-4964-a95e-eecd35b62f26",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "311529ea-e339-4d8f-9292-813ebe515f03",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "fa875444-3faf-482a-b099-77233bda914d",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "ddbb161b-6e52-4a90-9096-c8eae8cec4c9",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "ba51287d-a2c9-409b-8129-060b693a7570",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "e5c1c9d4-b4e2-4bdc-9153-6b769742b33f",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "4c125844-1eca-47aa-98f8-61c51a9c962f",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "362c753c-a44d-468d-b256-486470b8c1e1",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": " 69a02355-37bb-479f-9496-c8743fcacf3c",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "ffbd75d5-bf3a-47ff-af22-490240f6fcc6",
|
||||
}
|
||||
]
|
||||
}
|
||||
"collections": [
|
||||
{
|
||||
"href": "/collections/9e32a2e2-6b91-4236-a361-995ccdc14c60"
|
||||
},
|
||||
{
|
||||
"href": "/collections/598ce822-c357-46f3-ab70-63724d02d6ad"
|
||||
}
|
||||
],
|
||||
"bundles": [
|
||||
{
|
||||
"href": "/bundles/35e0606d-5e18-4f9c-aa61-74fc751cc3f9"
|
||||
},
|
||||
{
|
||||
"href": "/bundles/a469c57a-abcf-45c3-83e4-b187ebd708fd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "21539b1d-9ef1-4eda-9c77-49565b5bfb78",
|
||||
"name": "Do Open-Access Articles Have a Greater Research Impact?",
|
||||
"handle": "123456789/8871",
|
||||
"lastModified": "2016-10-14 10:41:12.886",
|
||||
"isArchived": true,
|
||||
"isWithdrawn": false,
|
||||
"metadata": [
|
||||
{
|
||||
"key": "dc.contributor.author",
|
||||
"value": "Antelman, Kristin",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.date.accessioned",
|
||||
"value": "2016-10-14T10:41:13Z",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.date.available",
|
||||
"value": "2016-10-14T10:41:13Z",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.date.issued",
|
||||
"value": "2004-09-01",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.identifier.uri",
|
||||
"value": "http://hdl.handle.net/123456789/8871",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description.abstract",
|
||||
"value": "Although many authors believe that their work has a greater research impact if it is freely available, studies to demonstrate that impact are few. This study looks at articles in four disciplines at varying stages of adoption of open access—philosophy, political science, electrical and electronic engineering and mathematics—to see whether they have a greater impact as measured by citations in the ISI Web of Science database when their authors make them freely available on the Internet. The finding is that, across all four disciplines, freely available articles do have a greater research impact. Shedding light on this category of open access reveals that scholars in diverse disciplines are adopting open-access practices and being rewarded for it.",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.publisher",
|
||||
"value": "College & Research Libraries News",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.subject",
|
||||
"value": "Publishing",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.subject",
|
||||
"value": "Intellectual Property",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.subject",
|
||||
"value": "Open Access",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.title",
|
||||
"value": "Do Open-Access Articles Have a Greater Research Impact?",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.type",
|
||||
"value": "(not specified)",
|
||||
"language": "en"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "be8325f7-243b-49f4-8a4b-df2b793ff3b5",
|
||||
"type": "items",
|
||||
"attributes": {
|
||||
"name": "Another Test Item",
|
||||
"handle": "123456789/9978",
|
||||
"lastModified": "2016-05-27 03:00:20.063",
|
||||
"isArchived": true,
|
||||
"isWithdrawn": false
|
||||
},
|
||||
"relationships": {
|
||||
"collections": {
|
||||
"data": [
|
||||
{ "type": "collections", "id": "9e32a2e2-6b91-4236-a361-995ccdc14c60" },
|
||||
{ "type": "collections", "id": "598ce822-c357-46f3-ab70-63724d02d6ad" }
|
||||
]
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "/items/be8325f7-243b-49f4-8a4b-df2b793ff3b5"
|
||||
},
|
||||
"metadata": {
|
||||
"data": [
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "981c725e-53f3-4749-89ee-ef042f23c3c3",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "521df61d-c541-4180-beb8-ac0a1bd1e852",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "551a216d-5350-4b15-9398-9bc2e95e7a3d",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": " eb17dce4-3892-47fe-b014-6ff8e17a93ef",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "3e840957-cb1b-4521-8f5d-fb5f6956f303",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "ae0bc880-481b-4425-aa5b-354b38d24e4f",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "8dc89ac4-d606-4f1a-8524-8f70a6b371de",
|
||||
},
|
||||
{
|
||||
"type": "metadata",
|
||||
"id": "13185eb9-dc05-4bd7-9c2d-5322a2ac5326",
|
||||
}
|
||||
]
|
||||
"collections": [
|
||||
{
|
||||
"href": "/collections/9e32a2e2-6b91-4236-a361-995ccdc14c60"
|
||||
},
|
||||
{
|
||||
"href": "/collections/598ce822-c357-46f3-ab70-63724d02d6ad"
|
||||
}
|
||||
],
|
||||
"bundles": [
|
||||
{
|
||||
"href": "/bundles/b0176baa-d52e-4c20-a8e6-d586f2c70c76"
|
||||
},
|
||||
{
|
||||
"href": "/bundles/40b1cd3f-07ad-4ca6-9716-132671f93a15"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "be8325f7-243b-49f4-8a4b-df2b793ff3b5",
|
||||
"name": "Another Test Item",
|
||||
"handle": "123456789/9978",
|
||||
"lastModified": "2016-05-27 03:00:20.063",
|
||||
"isArchived": true,
|
||||
"isWithdrawn": false,
|
||||
"metadata": [
|
||||
{
|
||||
"key": "dc.contributor.author",
|
||||
"value": "John Doe",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.date.accessioned",
|
||||
"value": "2016-05-27T07:45:04Z",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.date.available",
|
||||
"value": "2016-05-27T07:45:04Z",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.date.issued",
|
||||
"value": "2016-05-27",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.identifier.uri",
|
||||
"value": "http://hdl.handle.net/123456789/9978",
|
||||
"language": null
|
||||
},
|
||||
{
|
||||
"key": "dc.description.abstract",
|
||||
"value": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas lacus velit, lacinia eu ultrices nec, auctor in sem. Donec interdum convallis ornare. Aliquam et tortor risus. Praesent ut feugiat eros, eu consequat nibh. Morbi id quam eu mi pellentesque consequat vel vitae sem. Praesent sed velit ullamcorper, efficitur odio non, aliquet urna. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque eu placerat urna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nulla non aliquet mauris. Nulla quis posuere lorem. Pellentesque tempus maximus ipsum ac pretium. Nunc hendrerit tempus sem, vitae luctus erat consectetur vestibulum. Nulla sodales felis in dictum sagittis.\n\nNullam porta magna quis magna vulputate elementum. Pellentesque dictum lorem id nisl tincidunt condimentum. Sed est dolor, dapibus sit amet augue at, malesuada cursus quam. Pellentesque elit felis, malesuada dictum congue tristique, iaculis euismod ligula. Donec dignissim dolor eu lacus pulvinar porttitor. Sed quis semper augue, dictum sollicitudin eros. \n\nMauris congue lectus at turpis viverra scelerisque. Praesent at urna rhoncus, condimentum odio ac, sagittis libero. Nulla aliquam ornare bibendum. Duis quis ornare urna. Suspendisse semper tincidunt neque nec consequat. Sed enim diam, mollis eu neque vitae, lacinia varius risus. Fusce nec sem tempor, efficitur lectus sed, porta sem. Pellentesque sollicitudin ut dui vitae malesuada.",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.title",
|
||||
"value": "Another Test Item",
|
||||
"language": "en"
|
||||
},
|
||||
{
|
||||
"key": "dc.type",
|
||||
"value": "(not specified)",
|
||||
"language": "en"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
Reference in New Issue
Block a user