mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 10:34:15 +00:00
Merge remote-tracking branch 'remotes/origin/master' into shibboleth
# Conflicts: # src/app/core/auth/auth.service.ts # src/app/core/auth/models/auth-status.model.ts # src/app/core/auth/models/normalized-auth-status.model.ts # src/app/core/auth/server-auth.service.ts # src/app/core/core.module.ts # src/app/shared/shared.module.ts
This commit is contained in:
@@ -1,21 +1,28 @@
|
|||||||
.git
|
.git
|
||||||
node-modules
|
.idea
|
||||||
__build__
|
.vscode
|
||||||
__server_build__
|
.DS_Store
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# Build folders
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
dist
|
||||||
typings
|
typings
|
||||||
tsd_typings
|
tsd_typings
|
||||||
npm-debug.log
|
|
||||||
dist
|
|
||||||
coverage
|
coverage
|
||||||
.idea
|
__build__
|
||||||
*.iml
|
__server_build__
|
||||||
|
|
||||||
|
# Node
|
||||||
|
*.log
|
||||||
|
npm-debug.log.*
|
||||||
|
|
||||||
|
# Angular files
|
||||||
*.ngfactory.ts
|
*.ngfactory.ts
|
||||||
*.css.shim.ts
|
*.css.shim.ts
|
||||||
*.scss.shim.ts
|
*.scss.shim.ts
|
||||||
.DS_Store
|
|
||||||
|
# Webpack files
|
||||||
webpack.records.json
|
webpack.records.json
|
||||||
npm-debug.log.*
|
|
||||||
morgan.log
|
|
||||||
yarn-error.log
|
|
||||||
*.css
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
@@ -44,8 +44,8 @@ after_script:
|
|||||||
language: node_js
|
language: node_js
|
||||||
|
|
||||||
node_js:
|
node_js:
|
||||||
- "8"
|
|
||||||
- "10"
|
- "10"
|
||||||
|
- "12"
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
yarn: true
|
yarn: true
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
# This image will be published as dspace/dspace-angular
|
# This image will be published as dspace/dspace-angular
|
||||||
# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details
|
# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details
|
||||||
|
|
||||||
FROM node:8-alpine
|
FROM node:12-alpine
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ADD . /app/
|
ADD . /app/
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
@@ -13,7 +13,7 @@ You can find additional information on the DSpace 7 Angular UI on the [wiki](htt
|
|||||||
Quick start
|
Quick start
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
**Ensure you're running [Node](https://nodejs.org) `v8.0.x` or `v10.0.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) >= `v1.x`**
|
**Ensure you're running [Node](https://nodejs.org) `v10.x` or `v12.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) >= `v1.x`**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# clone the repo
|
# clone the repo
|
||||||
@@ -65,7 +65,7 @@ Requirements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
- [Node.js](https://nodejs.org), [npm](https://www.npmjs.com/), and [yarn](https://yarnpkg.com)
|
- [Node.js](https://nodejs.org), [npm](https://www.npmjs.com/), and [yarn](https://yarnpkg.com)
|
||||||
- Ensure you're running node `v8.x` or `v10.x`, npm >= `v5.x` and yarn >= `v1.x`
|
- Ensure you're running node `v10.x` or `v12.x`, npm >= `v5.x` and yarn >= `v1.x`
|
||||||
|
|
||||||
If you have [`nvm`](https://github.com/creationix/nvm#install-script) or [`nvm-windows`](https://github.com/coreybutler/nvm-windows) installed, which is highly recommended, you can run `nvm install --lts && nvm use` to install and start using the latest Node LTS.
|
If you have [`nvm`](https://github.com/creationix/nvm#install-script) or [`nvm-windows`](https://github.com/coreybutler/nvm-windows) installed, which is highly recommended, you can run `nvm install --lts && nvm use` to install and start using the latest Node LTS.
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { browser, element, by, protractor } from 'protractor';
|
import { browser, by, element, protractor } from 'protractor';
|
||||||
import { promise } from 'selenium-webdriver';
|
import { promise } from 'selenium-webdriver';
|
||||||
|
|
||||||
export class ProtractorPage {
|
export class ProtractorPage {
|
||||||
@@ -32,9 +32,4 @@ export class ProtractorPage {
|
|||||||
submitByPressingEnter() {
|
submitByPressingEnter() {
|
||||||
element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
|
element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitByPressingEnter() {
|
|
||||||
element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
71
package.json
71
package.json
@@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "8.* || >= 10.*"
|
"node": "10.* || >= 12.*"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"serialize-javascript": ">= 2.1.2",
|
"serialize-javascript": ">= 2.1.2",
|
||||||
@@ -75,30 +75,28 @@
|
|||||||
"sync-i18n": "node ./scripts/sync-i18n-files.js"
|
"sync-i18n": "node ./scripts/sync-i18n-files.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^7.2.15",
|
"@angular/animations": "^8.2.14",
|
||||||
"@angular/cdk": "7.3.7",
|
"@angular/cdk": "8.2.3",
|
||||||
"@angular/cli": "^7.3.5",
|
"@angular/cli": "^8.3.25",
|
||||||
"@angular/common": "^7.2.15",
|
"@angular/common": "^8.2.14",
|
||||||
"@angular/core": "^7.2.15",
|
"@angular/core": "^8.2.14",
|
||||||
"@angular/forms": "^7.2.15",
|
"@angular/forms": "^8.2.14",
|
||||||
"@angular/http": "^7.2.15",
|
"@angular/platform-browser": "^8.2.14",
|
||||||
"@angular/platform-browser": "^7.2.15",
|
"@angular/platform-browser-dynamic": "^8.2.14",
|
||||||
"@angular/platform-browser-dynamic": "^7.2.15",
|
"@angular/platform-server": "^8.2.14",
|
||||||
"@angular/platform-server": "^7.2.15",
|
"@angular/router": "^8.2.14",
|
||||||
"@angular/router": "^7.2.15",
|
|
||||||
"@angularclass/bootloader": "1.0.1",
|
"@angularclass/bootloader": "1.0.1",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^4.1.0",
|
"@ng-bootstrap/ng-bootstrap": "^5.2.1",
|
||||||
"@ng-dynamic-forms/core": "^7.1.0",
|
"@ng-dynamic-forms/core": "8.1.1",
|
||||||
"@ng-dynamic-forms/ui-ng-bootstrap": "^7.1.0",
|
"@ng-dynamic-forms/ui-ng-bootstrap": "8.1.1",
|
||||||
"@ngrx/effects": "^7.3.0",
|
"@ngrx/effects": "^8.6.0",
|
||||||
"@ngrx/router-store": "^7.3.0",
|
"@ngrx/router-store": "^8.6.0",
|
||||||
"@ngrx/store": "^7.3.0",
|
"@ngrx/store": "^8.6.0",
|
||||||
"@nguniversal/express-engine": "^7.1.1",
|
"@nguniversal/express-engine": "^8.2.6",
|
||||||
"@ngx-translate/core": "11.0.1",
|
"@ngx-translate/core": "11.0.1",
|
||||||
"@ngx-translate/http-loader": "4.0.0",
|
"@ngx-translate/http-loader": "4.0.0",
|
||||||
"@nicky-lenaers/ngx-scroll-to": "^1.0.0",
|
"@nicky-lenaers/ngx-scroll-to": "^3.0.1",
|
||||||
"angular-idle-preload": "3.0.0",
|
"angular-idle-preload": "3.0.0",
|
||||||
"angular-sortablejs": "^2.5.0",
|
|
||||||
"angular2-text-mask": "9.0.0",
|
"angular2-text-mask": "9.0.0",
|
||||||
"angulartics2": "7.5.2",
|
"angulartics2": "7.5.2",
|
||||||
"body-parser": "1.18.2",
|
"body-parser": "1.18.2",
|
||||||
@@ -106,7 +104,7 @@
|
|||||||
"cerialize": "0.1.18",
|
"cerialize": "0.1.18",
|
||||||
"compression": "1.7.1",
|
"compression": "1.7.1",
|
||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
"core-js": "^2.6.5",
|
"core-js": "^3.6.4",
|
||||||
"debug-loader": "^0.0.1",
|
"debug-loader": "^0.0.1",
|
||||||
"express": "4.16.2",
|
"express": "4.16.2",
|
||||||
"express-session": "1.15.6",
|
"express-session": "1.15.6",
|
||||||
@@ -126,17 +124,18 @@
|
|||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
"moment-range": "^4.0.2",
|
"moment-range": "^4.0.2",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"ng-mocks": "^7.6.0",
|
"ng-mocks": "^8.1.0",
|
||||||
"ng2-file-upload": "1.2.1",
|
"ng2-file-upload": "1.2.1",
|
||||||
"ng2-nouislider": "^1.8.2",
|
"ng2-nouislider": "^1.8.2",
|
||||||
"ngx-bootstrap": "^3.2.0",
|
"ngx-bootstrap": "^5.3.2",
|
||||||
"ngx-infinite-scroll": "6.0.1",
|
"ngx-infinite-scroll": "6.0.1",
|
||||||
"ngx-moment": "^3.4.0",
|
"ngx-moment": "^3.4.0",
|
||||||
"ngx-pagination": "3.0.3",
|
"ngx-pagination": "3.0.3",
|
||||||
|
"ngx-sortablejs": "^3.1.4",
|
||||||
"nouislider": "^11.0.0",
|
"nouislider": "^11.0.0",
|
||||||
"pem": "1.13.2",
|
"pem": "1.13.2",
|
||||||
"reflect-metadata": "0.1.12",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "6.4.0",
|
"rxjs": "6.5.4",
|
||||||
"rxjs-spy": "^7.5.1",
|
"rxjs-spy": "^7.5.1",
|
||||||
"sass-resources-loader": "^2.0.0",
|
"sass-resources-loader": "^2.0.0",
|
||||||
"sortablejs": "1.7.0",
|
"sortablejs": "1.7.0",
|
||||||
@@ -147,17 +146,17 @@
|
|||||||
"uuid": "^3.2.1",
|
"uuid": "^3.2.1",
|
||||||
"webfontloader": "1.6.28",
|
"webfontloader": "1.6.28",
|
||||||
"webpack-cli": "^3.2.0",
|
"webpack-cli": "^3.2.0",
|
||||||
"zone.js": "^0.8.29"
|
"zone.js": "^0.9.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^0.13.5",
|
"@angular-devkit/build-angular": "^0.803.25",
|
||||||
"@angular/compiler": "^7.2.15",
|
"@angular/compiler": "^8.2.14",
|
||||||
"@angular/compiler-cli": "^7.2.15",
|
"@angular/compiler-cli": "^8.2.14",
|
||||||
"@fortawesome/fontawesome-free": "^5.5.0",
|
"@fortawesome/fontawesome-free": "^5.5.0",
|
||||||
"@ngrx/entity": "^7.3.0",
|
"@ngrx/entity": "^8.6.0",
|
||||||
"@ngrx/schematics": "^7.3.0",
|
"@ngrx/schematics": "^8.6.0",
|
||||||
"@ngrx/store-devtools": "^7.3.0",
|
"@ngrx/store-devtools": "^8.6.0",
|
||||||
"@ngtools/webpack": "^7.3.9",
|
"@ngtools/webpack": "^8.3.25",
|
||||||
"@schematics/angular": "^0.7.5",
|
"@schematics/angular": "^0.7.5",
|
||||||
"@types/acorn": "^4.0.3",
|
"@types/acorn": "^4.0.3",
|
||||||
"@types/cookie-parser": "1.4.1",
|
"@types/cookie-parser": "1.4.1",
|
||||||
@@ -215,8 +214,6 @@
|
|||||||
"karma-webdriver-launcher": "^1.0.7",
|
"karma-webdriver-launcher": "^1.0.7",
|
||||||
"karma-webpack": "3.0.0",
|
"karma-webpack": "3.0.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"ngrx-store-freeze": "^0.2.4",
|
|
||||||
"node-sass": "^4.11.0",
|
|
||||||
"nodemon": "^1.15.0",
|
"nodemon": "^1.15.0",
|
||||||
"npm-run-all": "4.1.3",
|
"npm-run-all": "4.1.3",
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||||
@@ -247,7 +244,7 @@
|
|||||||
"ts-node": "4.1.0",
|
"ts-node": "4.1.0",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"typedoc": "^0.9.0",
|
"typedoc": "^0.9.0",
|
||||||
"typescript": "3.1.6",
|
"typescript": "3.5.3",
|
||||||
"webdriver-manager": "^12.1.7",
|
"webdriver-manager": "^12.1.7",
|
||||||
"webpack": "^4.29.6",
|
"webpack": "^4.29.6",
|
||||||
"webpack-bundle-analyzer": "^3.3.2",
|
"webpack-bundle-analyzer": "^3.3.2",
|
||||||
|
@@ -13,8 +13,8 @@
|
|||||||
*/
|
*/
|
||||||
Error.stackTraceLimit = Infinity;
|
Error.stackTraceLimit = Infinity;
|
||||||
|
|
||||||
require('core-js/es6');
|
require('core-js/es');
|
||||||
require('core-js/es7/reflect');
|
require('core-js/features/reflect');
|
||||||
|
|
||||||
// Typescript emit helpers polyfill
|
// Typescript emit helpers polyfill
|
||||||
require('ts-helpers');
|
require('ts-helpers');
|
||||||
|
@@ -1,19 +1,18 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { RouterStub } from '../../../../shared/testing/router-stub';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { RestResponse } from '../../../../core/cache/response.models';
|
||||||
|
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
||||||
|
import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
|
||||||
|
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
|
||||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub';
|
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub';
|
||||||
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
import { RouterStub } from '../../../../shared/testing/router-stub';
|
||||||
import { RestResponse } from '../../../../core/cache/response.models';
|
|
||||||
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
|
|
||||||
import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
|
|
||||||
import { ResourceType } from '../../../../core/shared/resource-type';
|
|
||||||
import { AddBitstreamFormatComponent } from './add-bitstream-format.component';
|
import { AddBitstreamFormatComponent } from './add-bitstream-format.component';
|
||||||
|
|
||||||
describe('AddBitstreamFormatComponent', () => {
|
describe('AddBitstreamFormatComponent', () => {
|
||||||
@@ -43,7 +42,7 @@ describe('AddBitstreamFormatComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [AddBitstreamFormatComponent],
|
declarations: [AddBitstreamFormatComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: Router, useValue: router},
|
{provide: Router, useValue: router},
|
||||||
@@ -83,7 +82,7 @@ describe('AddBitstreamFormatComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [AddBitstreamFormatComponent],
|
declarations: [AddBitstreamFormatComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: Router, useValue: router},
|
{provide: Router, useValue: router},
|
||||||
|
@@ -92,7 +92,7 @@ describe('BitstreamFormatsComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
|
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
|
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
|
||||||
@@ -214,7 +214,7 @@ describe('BitstreamFormatsComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
|
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
|
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
|
||||||
@@ -260,7 +260,7 @@ describe('BitstreamFormatsComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
|
declarations: [BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
|
{provide: BitstreamFormatDataService, useValue: bitstreamFormatService},
|
||||||
|
@@ -1,21 +1,20 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { RouterStub } from '../../../../shared/testing/router-stub';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { RestResponse } from '../../../../core/cache/response.models';
|
||||||
|
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import { EditBitstreamFormatComponent } from './edit-bitstream-format.component';
|
import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
|
||||||
|
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
|
||||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub';
|
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service-stub';
|
||||||
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
import { RouterStub } from '../../../../shared/testing/router-stub';
|
||||||
import { RestResponse } from '../../../../core/cache/response.models';
|
import { EditBitstreamFormatComponent } from './edit-bitstream-format.component';
|
||||||
import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model';
|
|
||||||
import { BitstreamFormatSupportLevel } from '../../../../core/shared/bitstream-format-support-level';
|
|
||||||
import { ResourceType } from '../../../../core/shared/resource-type';
|
|
||||||
|
|
||||||
describe('EditBitstreamFormatComponent', () => {
|
describe('EditBitstreamFormatComponent', () => {
|
||||||
let comp: EditBitstreamFormatComponent;
|
let comp: EditBitstreamFormatComponent;
|
||||||
@@ -49,7 +48,7 @@ describe('EditBitstreamFormatComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [EditBitstreamFormatComponent],
|
declarations: [EditBitstreamFormatComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ActivatedRoute, useValue: routeStub},
|
{provide: ActivatedRoute, useValue: routeStub},
|
||||||
@@ -99,7 +98,7 @@ describe('EditBitstreamFormatComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [EditBitstreamFormatComponent],
|
declarations: [EditBitstreamFormatComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ActivatedRoute, useValue: routeStub},
|
{provide: ActivatedRoute, useValue: routeStub},
|
||||||
|
@@ -40,7 +40,7 @@ describe('FormatFormComponent', () => {
|
|||||||
|
|
||||||
const initAsync = () => {
|
const initAsync = () => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), ReactiveFormsModule, FormsModule, TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), ReactiveFormsModule, FormsModule, TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [FormatFormComponent],
|
declarations: [FormatFormComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: Router, useValue: router},
|
{provide: Router, useValue: router},
|
||||||
|
@@ -148,4 +148,6 @@ export type MetadataRegistryAction
|
|||||||
| MetadataRegistryEditFieldAction
|
| MetadataRegistryEditFieldAction
|
||||||
| MetadataRegistryCancelFieldAction
|
| MetadataRegistryCancelFieldAction
|
||||||
| MetadataRegistrySelectFieldAction
|
| MetadataRegistrySelectFieldAction
|
||||||
| MetadataRegistryDeselectFieldAction;
|
| MetadataRegistryDeselectFieldAction
|
||||||
|
| MetadataRegistryDeselectAllSchemaAction
|
||||||
|
| MetadataRegistryDeselectAllFieldAction;
|
||||||
|
@@ -26,13 +26,21 @@ describe('MetadataRegistryComponent', () => {
|
|||||||
const mockSchemasList = [
|
const mockSchemasList = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1'
|
||||||
|
},
|
||||||
|
},
|
||||||
prefix: 'dc',
|
prefix: 'dc',
|
||||||
namespace: 'http://dublincore.org/documents/dcmi-terms/'
|
namespace: 'http://dublincore.org/documents/dcmi-terms/'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2'
|
||||||
|
},
|
||||||
|
},
|
||||||
prefix: 'mock',
|
prefix: 'mock',
|
||||||
namespace: 'http://dspace.org/mockschema'
|
namespace: 'http://dspace.org/mockschema'
|
||||||
}
|
}
|
||||||
@@ -53,7 +61,7 @@ describe('MetadataRegistryComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [MetadataRegistryComponent, PaginationComponent, EnumKeysPipe],
|
declarations: [MetadataRegistryComponent, PaginationComponent, EnumKeysPipe],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RegistryService, useValue: registryServiceStub },
|
{ provide: RegistryService, useValue: registryServiceStub },
|
||||||
|
@@ -20,7 +20,11 @@ class NullAction extends MetadataRegistryEditSchemaAction {
|
|||||||
const schema: MetadataSchema = Object.assign(new MetadataSchema(),
|
const schema: MetadataSchema = Object.assign(new MetadataSchema(),
|
||||||
{
|
{
|
||||||
id: 'schema-id',
|
id: 'schema-id',
|
||||||
self: 'http://rest.self/schema/dc',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'http://rest.self/schema/dc'
|
||||||
|
},
|
||||||
|
},
|
||||||
prefix: 'dc',
|
prefix: 'dc',
|
||||||
namespace: 'http://dublincore.org/documents/dcmi-terms/'
|
namespace: 'http://dublincore.org/documents/dcmi-terms/'
|
||||||
});
|
});
|
||||||
@@ -28,7 +32,11 @@ const schema: MetadataSchema = Object.assign(new MetadataSchema(),
|
|||||||
const schema2: MetadataSchema = Object.assign(new MetadataSchema(),
|
const schema2: MetadataSchema = Object.assign(new MetadataSchema(),
|
||||||
{
|
{
|
||||||
id: 'another-schema-id',
|
id: 'another-schema-id',
|
||||||
self: 'http://rest.self/schema/dcterms',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'http://rest.self/schema/dcterms',
|
||||||
|
},
|
||||||
|
},
|
||||||
prefix: 'dcterms',
|
prefix: 'dcterms',
|
||||||
namespace: 'http://purl.org/dc/terms/'
|
namespace: 'http://purl.org/dc/terms/'
|
||||||
});
|
});
|
||||||
@@ -36,7 +44,11 @@ const schema2: MetadataSchema = Object.assign(new MetadataSchema(),
|
|||||||
const field: MetadataField = Object.assign(new MetadataField(),
|
const field: MetadataField = Object.assign(new MetadataField(),
|
||||||
{
|
{
|
||||||
id: 'author-field-id',
|
id: 'author-field-id',
|
||||||
self: 'http://rest.self/field/author',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'http://rest.self/field/author',
|
||||||
|
},
|
||||||
|
},
|
||||||
element: 'contributor',
|
element: 'contributor',
|
||||||
qualifier: 'author',
|
qualifier: 'author',
|
||||||
scopeNote: 'Author of an item',
|
scopeNote: 'Author of an item',
|
||||||
@@ -46,7 +58,11 @@ const field: MetadataField = Object.assign(new MetadataField(),
|
|||||||
const field2: MetadataField = Object.assign(new MetadataField(),
|
const field2: MetadataField = Object.assign(new MetadataField(),
|
||||||
{
|
{
|
||||||
id: 'title-field-id',
|
id: 'title-field-id',
|
||||||
self: 'http://rest.self/field/title',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'http://rest.self/field/title',
|
||||||
|
},
|
||||||
|
},
|
||||||
element: 'title',
|
element: 'title',
|
||||||
qualifier: null,
|
qualifier: null,
|
||||||
scopeNote: 'Title of an item',
|
scopeNote: 'Title of an item',
|
||||||
|
@@ -34,7 +34,7 @@ describe('MetadataSchemaFormComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ MetadataSchemaFormComponent, EnumKeysPipe ],
|
declarations: [ MetadataSchemaFormComponent, EnumKeysPipe ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RegistryService, useValue: registryServiceStub },
|
{ provide: RegistryService, useValue: registryServiceStub },
|
||||||
|
@@ -42,7 +42,7 @@ describe('MetadataFieldFormComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ MetadataFieldFormComponent, EnumKeysPipe ],
|
declarations: [ MetadataFieldFormComponent, EnumKeysPipe ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RegistryService, useValue: registryServiceStub },
|
{ provide: RegistryService, useValue: registryServiceStub },
|
||||||
|
@@ -30,13 +30,21 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
const mockSchemasList = [
|
const mockSchemasList = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1',
|
||||||
|
},
|
||||||
|
},
|
||||||
prefix: 'dc',
|
prefix: 'dc',
|
||||||
namespace: 'http://dublincore.org/documents/dcmi-terms/'
|
namespace: 'http://dublincore.org/documents/dcmi-terms/'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2',
|
||||||
|
},
|
||||||
|
},
|
||||||
prefix: 'mock',
|
prefix: 'mock',
|
||||||
namespace: 'http://dspace.org/mockschema'
|
namespace: 'http://dspace.org/mockschema'
|
||||||
}
|
}
|
||||||
@@ -44,7 +52,11 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
const mockFieldsList = [
|
const mockFieldsList = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/8',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/8',
|
||||||
|
},
|
||||||
|
},
|
||||||
element: 'contributor',
|
element: 'contributor',
|
||||||
qualifier: 'advisor',
|
qualifier: 'advisor',
|
||||||
scopeNote: null,
|
scopeNote: null,
|
||||||
@@ -52,7 +64,11 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/9',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/9',
|
||||||
|
},
|
||||||
|
},
|
||||||
element: 'contributor',
|
element: 'contributor',
|
||||||
qualifier: 'author',
|
qualifier: 'author',
|
||||||
scopeNote: null,
|
scopeNote: null,
|
||||||
@@ -60,7 +76,11 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/10',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/10',
|
||||||
|
},
|
||||||
|
},
|
||||||
element: 'contributor',
|
element: 'contributor',
|
||||||
qualifier: 'editor',
|
qualifier: 'editor',
|
||||||
scopeNote: 'test scope note',
|
scopeNote: 'test scope note',
|
||||||
@@ -68,7 +88,11 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/11',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/11',
|
||||||
|
},
|
||||||
|
},
|
||||||
element: 'contributor',
|
element: 'contributor',
|
||||||
qualifier: 'illustrator',
|
qualifier: 'illustrator',
|
||||||
scopeNote: null,
|
scopeNote: null,
|
||||||
@@ -99,7 +123,7 @@ describe('MetadataSchemaComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [MetadataSchemaComponent, PaginationComponent, EnumKeysPipe],
|
declarations: [MetadataSchemaComponent, PaginationComponent, EnumKeysPipe],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RegistryService, useValue: registryServiceStub },
|
{ provide: RegistryService, useValue: registryServiceStub },
|
||||||
|
@@ -68,7 +68,7 @@ describe('BrowseByDatePageComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [BrowseByDatePageComponent, EnumKeysPipe, VarDirective],
|
declarations: [BrowseByDatePageComponent, EnumKeysPipe, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },
|
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },
|
||||||
|
@@ -86,7 +86,7 @@ describe('BrowseByMetadataPageComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [BrowseByMetadataPageComponent, EnumKeysPipe, VarDirective],
|
declarations: [BrowseByMetadataPageComponent, EnumKeysPipe, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
||||||
|
@@ -64,7 +64,7 @@ describe('BrowseByTitlePageComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [BrowseByTitlePageComponent, EnumKeysPipe, VarDirective],
|
declarations: [BrowseByTitlePageComponent, EnumKeysPipe, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { filter, tap } from 'rxjs/operators';
|
||||||
import { CollectionItemMapperComponent } from './collection-item-mapper.component';
|
import { CollectionItemMapperComponent } from './collection-item-mapper.component';
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@@ -52,7 +53,12 @@ describe('CollectionItemMapperComponent', () => {
|
|||||||
|
|
||||||
const mockCollection: Collection = Object.assign(new Collection(), {
|
const mockCollection: Collection = Object.assign(new Collection(), {
|
||||||
id: 'ce41d451-97ed-4a9c-94a1-7de34f16a9f4',
|
id: 'ce41d451-97ed-4a9c-94a1-7de34f16a9f4',
|
||||||
name: 'test-collection'
|
name: 'test-collection',
|
||||||
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'https://rest.api/collections/ce41d451-97ed-4a9c-94a1-7de34f16a9f4'
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const mockCollectionRD: RemoteData<Collection> = new RemoteData<Collection>(false, false, true, null, mockCollection);
|
const mockCollectionRD: RemoteData<Collection> = new RemoteData<Collection>(false, false, true, null, mockCollection);
|
||||||
const mockSearchOptions = of(new PaginatedSearchOptions({
|
const mockSearchOptions = of(new PaginatedSearchOptions({
|
||||||
@@ -116,7 +122,7 @@ describe('CollectionItemMapperComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [CollectionItemMapperComponent, ItemSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent],
|
declarations: [CollectionItemMapperComponent, ItemSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
||||||
|
@@ -22,6 +22,7 @@ import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.comp
|
|||||||
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service';
|
||||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||||
import { SearchService } from '../../core/shared/search/search.service';
|
import { SearchService } from '../../core/shared/search/search.service';
|
||||||
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-item-mapper',
|
selector: 'ds-collection-item-mapper',
|
||||||
@@ -48,7 +49,7 @@ export class CollectionItemMapperComponent implements OnInit {
|
|||||||
* A view on the tabset element
|
* A view on the tabset element
|
||||||
* Used to switch tabs programmatically
|
* Used to switch tabs programmatically
|
||||||
*/
|
*/
|
||||||
@ViewChild('tabs') tabs;
|
@ViewChild('tabs', {static: false}) tabs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The collection to map items to
|
* The collection to map items to
|
||||||
@@ -122,7 +123,7 @@ export class CollectionItemMapperComponent implements OnInit {
|
|||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
return this.collectionDataService.getMappedItems(collectionRD.payload.id, Object.assign(options, {
|
return this.collectionDataService.getMappedItems(collectionRD.payload.id, Object.assign(options, {
|
||||||
sort: this.defaultSortOptions
|
sort: this.defaultSortOptions
|
||||||
}))
|
}),followLink('owningCollection'))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -154,7 +155,7 @@ export class CollectionItemMapperComponent implements OnInit {
|
|||||||
map((collectionRD: RemoteData<Collection>) => collectionRD.payload),
|
map((collectionRD: RemoteData<Collection>) => collectionRD.payload),
|
||||||
switchMap((collection: Collection) =>
|
switchMap((collection: Collection) =>
|
||||||
observableCombineLatest(ids.map((id: string) =>
|
observableCombineLatest(ids.map((id: string) =>
|
||||||
remove ? this.itemDataService.removeMappingFromCollection(id, collection.id) : this.itemDataService.mapToCollection(id, collection.self)
|
remove ? this.itemDataService.removeMappingFromCollection(id, collection.id) : this.itemDataService.mapToCollection(id, collection._links.self.href)
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -6,6 +6,7 @@ import { CollectionDataService } from '../core/data/collection-data.service';
|
|||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { find } from 'rxjs/operators';
|
import { find } from 'rxjs/operators';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
|
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a resolver that requests a specific collection before the route is activated
|
* This class represents a resolver that requests a specific collection before the route is activated
|
||||||
@@ -23,7 +24,7 @@ export class CollectionPageResolver implements Resolve<RemoteData<Collection>> {
|
|||||||
* or an error if something went wrong
|
* or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> {
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> {
|
||||||
return this.collectionService.findById(route.params.id).pipe(
|
return this.collectionService.findById(route.params.id, followLink('logo')).pipe(
|
||||||
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
|
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
28
src/app/+community-page/community-page.resolver.spec.ts
Normal file
28
src/app/+community-page/community-page.resolver.spec.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { first } from 'rxjs/operators';
|
||||||
|
import { CommunityPageResolver } from './community-page.resolver';
|
||||||
|
|
||||||
|
describe('CommunityPageResolver', () => {
|
||||||
|
describe('resolve', () => {
|
||||||
|
let resolver: CommunityPageResolver;
|
||||||
|
let communityService: any;
|
||||||
|
const uuid = '1234-65487-12354-1235';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
communityService = {
|
||||||
|
findById: (id: string) => observableOf({ payload: { id }, hasSucceeded: true })
|
||||||
|
};
|
||||||
|
resolver = new CommunityPageResolver(communityService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve a community with the correct id', () => {
|
||||||
|
resolver.resolve({ params: { id: uuid } } as any, undefined)
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe(
|
||||||
|
(resolved) => {
|
||||||
|
expect(resolved.payload.id).toEqual(uuid);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -6,6 +6,7 @@ import { Community } from '../core/shared/community.model';
|
|||||||
import { CommunityDataService } from '../core/data/community-data.service';
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
import { find } from 'rxjs/operators';
|
import { find } from 'rxjs/operators';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
|
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a resolver that requests a specific community before the route is activated
|
* This class represents a resolver that requests a specific community before the route is activated
|
||||||
@@ -23,7 +24,12 @@ export class CommunityPageResolver implements Resolve<RemoteData<Community>> {
|
|||||||
* or an error if something went wrong
|
* or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> {
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> {
|
||||||
return this.communityService.findById(route.params.id).pipe(
|
return this.communityService.findById(
|
||||||
|
route.params.id,
|
||||||
|
followLink('logo'),
|
||||||
|
followLink('subcommunities'),
|
||||||
|
followLink('collections')
|
||||||
|
).pipe(
|
||||||
find((RD) => hasValue(RD.error) || RD.hasSucceeded)
|
find((RD) => hasValue(RD.error) || RD.hasSucceeded)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,14 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { RouteService } from '../../core/services/route.service';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
|
||||||
import { DeleteCommunityPageComponent } from './delete-community-page.component';
|
|
||||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
|
import { DeleteCommunityPageComponent } from './delete-community-page.component';
|
||||||
|
|
||||||
describe('DeleteCommunityPageComponent', () => {
|
describe('DeleteCommunityPageComponent', () => {
|
||||||
let comp: DeleteCommunityPageComponent;
|
let comp: DeleteCommunityPageComponent;
|
||||||
|
@@ -116,7 +116,7 @@ describe('CommunityPageSubCollectionList Component', () => {
|
|||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RouterTestingModule.withRoutes([]),
|
RouterTestingModule.withRoutes([]),
|
||||||
NgbModule.forRoot(),
|
NgbModule,
|
||||||
NoopAnimationsModule
|
NoopAnimationsModule
|
||||||
],
|
],
|
||||||
declarations: [CommunityPageSubCollectionListComponent],
|
declarations: [CommunityPageSubCollectionListComponent],
|
||||||
|
@@ -117,7 +117,7 @@ describe('CommunityPageSubCommunityListComponent Component', () => {
|
|||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RouterTestingModule.withRoutes([]),
|
RouterTestingModule.withRoutes([]),
|
||||||
NgbModule.forRoot(),
|
NgbModule,
|
||||||
NoopAnimationsModule
|
NoopAnimationsModule
|
||||||
],
|
],
|
||||||
declarations: [CommunityPageSubCommunityListComponent],
|
declarations: [CommunityPageSubCommunityListComponent],
|
||||||
|
@@ -107,7 +107,7 @@ describe('TopLevelCommunityList Component', () => {
|
|||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
SharedModule,
|
SharedModule,
|
||||||
RouterTestingModule.withRoutes([]),
|
RouterTestingModule.withRoutes([]),
|
||||||
NgbModule.forRoot(),
|
NgbModule,
|
||||||
NoopAnimationsModule
|
NoopAnimationsModule
|
||||||
],
|
],
|
||||||
declarations: [TopLevelCommunityListComponent],
|
declarations: [TopLevelCommunityListComponent],
|
||||||
|
@@ -1,42 +1,43 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { EventEmitter } from '@angular/core';
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
|
||||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
|
||||||
import { RouterStub } from '../../../shared/testing/router-stub';
|
|
||||||
import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
|
|
||||||
import { EventEmitter } from '@angular/core';
|
|
||||||
import { SearchServiceStub } from '../../../shared/testing/search-service-stub';
|
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
|
||||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
|
||||||
import { HostWindowService } from '../../../shared/host-window.service';
|
|
||||||
import { HostWindowServiceStub } from '../../../shared/testing/host-window-service-stub';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
|
||||||
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
|
|
||||||
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
|
|
||||||
import { of } from 'rxjs/internal/observable/of';
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
import { CollectionSelectComponent } from '../../../shared/object-select/collection-select/collection-select.component';
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { EnumKeysPipe } from '../../../shared/utils/enum-keys-pipe';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { VarDirective } from '../../../shared/utils/var.directive';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { SearchFormComponent } from '../../../shared/search-form/search-form.component';
|
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { ErrorComponent } from '../../../shared/error/error.component';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
|
import { ErrorComponent } from '../../../shared/error/error.component';
|
||||||
|
import { HostWindowService } from '../../../shared/host-window.service';
|
||||||
|
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { CollectionSelectComponent } from '../../../shared/object-select/collection-select/collection-select.component';
|
||||||
|
import { ObjectSelectService } from '../../../shared/object-select/object-select.service';
|
||||||
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
|
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
|
||||||
|
import { SearchFormComponent } from '../../../shared/search-form/search-form.component';
|
||||||
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
|
||||||
|
import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
|
||||||
|
import { HostWindowServiceStub } from '../../../shared/testing/host-window-service-stub';
|
||||||
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
|
import { ObjectSelectServiceStub } from '../../../shared/testing/object-select-service-stub';
|
||||||
|
import { RouterStub } from '../../../shared/testing/router-stub';
|
||||||
|
import { SearchServiceStub } from '../../../shared/testing/search-service-stub';
|
||||||
|
import { EnumKeysPipe } from '../../../shared/utils/enum-keys-pipe';
|
||||||
|
import { VarDirective } from '../../../shared/utils/var.directive';
|
||||||
|
import { ItemCollectionMapperComponent } from './item-collection-mapper.component';
|
||||||
|
|
||||||
describe('ItemCollectionMapperComponent', () => {
|
describe('ItemCollectionMapperComponent', () => {
|
||||||
let comp: ItemCollectionMapperComponent;
|
let comp: ItemCollectionMapperComponent;
|
||||||
@@ -98,7 +99,7 @@ describe('ItemCollectionMapperComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemCollectionMapperComponent, CollectionSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent],
|
declarations: [ItemCollectionMapperComponent, CollectionSelectComponent, SearchFormComponent, PaginationComponent, EnumKeysPipe, VarDirective, ErrorComponent, LoadingComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
{ provide: ActivatedRoute, useValue: activatedRouteStub },
|
||||||
@@ -109,7 +110,8 @@ describe('ItemCollectionMapperComponent', () => {
|
|||||||
{ provide: SearchService, useValue: searchServiceStub },
|
{ provide: SearchService, useValue: searchServiceStub },
|
||||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
||||||
{ provide: TranslateService, useValue: translateServiceStub },
|
{ provide: TranslateService, useValue: translateServiceStub },
|
||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) }
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||||
|
{ provide: CollectionDataService, useValue: {} }
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
@@ -1,12 +1,18 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
|
|
||||||
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators';
|
import {
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
getRemoteDataPayload,
|
||||||
|
getSucceededRemoteData,
|
||||||
|
toDSpaceObjectListRD
|
||||||
|
} from '../../../core/shared/operators';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
@@ -39,7 +45,7 @@ export class ItemCollectionMapperComponent implements OnInit {
|
|||||||
* A view on the tabset element
|
* A view on the tabset element
|
||||||
* Used to switch tabs programmatically
|
* Used to switch tabs programmatically
|
||||||
*/
|
*/
|
||||||
@ViewChild('tabs') tabs;
|
@ViewChild('tabs', {static: false}) tabs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The item to map to collections
|
* The item to map to collections
|
||||||
@@ -81,6 +87,7 @@ export class ItemCollectionMapperComponent implements OnInit {
|
|||||||
private searchService: SearchService,
|
private searchService: SearchService,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private itemDataService: ItemDataService,
|
private itemDataService: ItemDataService,
|
||||||
|
private collectionDataService: CollectionDataService,
|
||||||
private translateService: TranslateService) {
|
private translateService: TranslateService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +113,8 @@ export class ItemCollectionMapperComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const owningCollectionRD$ = this.itemRD$.pipe(
|
const owningCollectionRD$ = this.itemRD$.pipe(
|
||||||
switchMap((itemRD: RemoteData<Item>) => itemRD.payload.owningCollection)
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
switchMap((item: Item) => this.collectionDataService.findOwningCollectionFor(item))
|
||||||
);
|
);
|
||||||
const itemCollectionsAndOptions$ = observableCombineLatest(
|
const itemCollectionsAndOptions$ = observableCombineLatest(
|
||||||
this.itemCollectionsRD$,
|
this.itemCollectionsRD$,
|
||||||
|
@@ -182,7 +182,7 @@ describe('ItemDeleteComponent', () => {
|
|||||||
notificationsServiceStub = new NotificationsServiceStub();
|
notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemDeleteComponent, VarDirective],
|
declarations: [ItemDeleteComponent, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
@@ -1,23 +1,22 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
||||||
import { EditInPlaceFieldComponent } from './edit-in-place-field.component';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { RegistryService } from '../../../../core/registry/registry.service';
|
|
||||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { SharedModule } from '../../../../shared/shared.module';
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { getTestScheduler } from 'jasmine-marbles';
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||||
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
|
|
||||||
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
|
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
|
||||||
|
import { RegistryService } from '../../../../core/registry/registry.service';
|
||||||
|
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
|
||||||
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
|
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
|
||||||
|
import { SharedModule } from '../../../../shared/shared.module';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||||
|
import { EditInPlaceFieldComponent } from './edit-in-place-field.component';
|
||||||
|
|
||||||
let comp: EditInPlaceFieldComponent;
|
let comp: EditInPlaceFieldComponent;
|
||||||
let fixture: ComponentFixture<EditInPlaceFieldComponent>;
|
let fixture: ComponentFixture<EditInPlaceFieldComponent>;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { LinkService } from '../../../core/cache/builders/link.service';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||||
|
@@ -1,23 +1,23 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
|
||||||
import { RouterStub } from '../../../shared/testing/router-stub';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ItemMoveComponent } from './item-move.component';
|
|
||||||
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
|
||||||
|
import { RouterStub } from '../../../shared/testing/router-stub';
|
||||||
|
import { ItemMoveComponent } from './item-move.component';
|
||||||
|
|
||||||
describe('ItemMoveComponent', () => {
|
describe('ItemMoveComponent', () => {
|
||||||
let comp: ItemMoveComponent;
|
let comp: ItemMoveComponent;
|
||||||
@@ -50,16 +50,14 @@ describe('ItemMoveComponent', () => {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const collection1 = Object.assign(new Collection(),{
|
const collection1 = Object.assign(new Collection(), {
|
||||||
uuid: 'collection-uuid-1',
|
uuid: 'collection-uuid-1',
|
||||||
name: 'Test collection 1',
|
name: 'Test collection 1'
|
||||||
self: 'self-link-1',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const collection2 = Object.assign(new Collection(),{
|
const collection2 = Object.assign(new Collection(), {
|
||||||
uuid: 'collection-uuid-2',
|
uuid: 'collection-uuid-2',
|
||||||
name: 'Test collection 2',
|
name: 'Test collection 2'
|
||||||
self: 'self-link-2',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockSearchService = {
|
const mockSearchService = {
|
||||||
@@ -80,23 +78,20 @@ describe('ItemMoveComponent', () => {
|
|||||||
const notificationsServiceStub = new NotificationsServiceStub();
|
const notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
|
||||||
describe('ItemMoveComponent success', () => {
|
describe('ItemMoveComponent success', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemMoveComponent],
|
declarations: [ItemMoveComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ActivatedRoute, useValue: routeStub},
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
{provide: Router, useValue: routerStub},
|
{ provide: Router, useValue: routerStub },
|
||||||
{provide: ItemDataService, useValue: mockItemDataService},
|
{ provide: ItemDataService, useValue: mockItemDataService },
|
||||||
{provide: NotificationsService, useValue: notificationsServiceStub},
|
{ provide: NotificationsService, useValue: notificationsServiceStub },
|
||||||
{provide: SearchService, useValue: mockSearchService},
|
{ provide: SearchService, useValue: mockSearchService },
|
||||||
], schemas: [
|
], schemas: [
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ItemMoveComponent);
|
fixture = TestBed.createComponent(ItemMoveComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -141,23 +136,20 @@ describe('ItemMoveComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('ItemMoveComponent fail', () => {
|
describe('ItemMoveComponent fail', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemMoveComponent],
|
declarations: [ItemMoveComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ActivatedRoute, useValue: routeStub},
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
{provide: Router, useValue: routerStub},
|
{ provide: Router, useValue: routerStub },
|
||||||
{provide: ItemDataService, useValue: mockItemDataServiceFail},
|
{ provide: ItemDataService, useValue: mockItemDataServiceFail },
|
||||||
{provide: NotificationsService, useValue: notificationsServiceStub},
|
{ provide: NotificationsService, useValue: notificationsServiceStub },
|
||||||
{provide: SearchService, useValue: mockSearchService},
|
{ provide: SearchService, useValue: mockSearchService },
|
||||||
], schemas: [
|
], schemas: [
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(ItemMoveComponent);
|
fixture = TestBed.createComponent(ItemMoveComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@@ -60,7 +60,7 @@ describe('ItemPrivateComponent', () => {
|
|||||||
notificationsServiceStub = new NotificationsServiceStub();
|
notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemPrivateComponent],
|
declarations: [ItemPrivateComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
@@ -60,7 +60,7 @@ describe('ItemPublicComponent', () => {
|
|||||||
notificationsServiceStub = new NotificationsServiceStub();
|
notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemPublicComponent],
|
declarations: [ItemPublicComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
@@ -60,7 +60,7 @@ describe('ItemReinstateComponent', () => {
|
|||||||
notificationsServiceStub = new NotificationsServiceStub();
|
notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemReinstateComponent],
|
declarations: [ItemReinstateComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
@@ -1,19 +1,22 @@
|
|||||||
import {EditRelationshipListComponent} from './edit-relationship-list.component';
|
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model';
|
import { By } from '@angular/platform-browser';
|
||||||
import {Relationship} from '../../../../core/shared/item-relationships/relationship.model';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import {of as observableOf} from 'rxjs/internal/observable/of';
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
import {RemoteData} from '../../../../core/data/remote-data';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import {Item} from '../../../../core/shared/item.model';
|
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
||||||
import {PaginatedList} from '../../../../core/data/paginated-list';
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||||
import {PageInfo} from '../../../../core/shared/page-info.model';
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
import {FieldChangeType} from '../../../../core/data/object-updates/object-updates.actions';
|
import { RelationshipTypeService } from '../../../../core/data/relationship-type.service';
|
||||||
import {SharedModule} from '../../../../shared/shared.module';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import {TranslateModule} from '@ngx-translate/core';
|
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
|
||||||
import {ObjectUpdatesService} from '../../../../core/data/object-updates/object-updates.service';
|
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core';
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
import {By} from '@angular/platform-browser';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import {ItemType} from '../../../../core/shared/item-relationships/item-type.model';
|
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||||
|
import { getMockLinkService } from '../../../../shared/mocks/mock-link-service';
|
||||||
|
import { SharedModule } from '../../../../shared/shared.module';
|
||||||
|
import { EditRelationshipListComponent } from './edit-relationship-list.component';
|
||||||
|
|
||||||
let comp: EditRelationshipListComponent;
|
let comp: EditRelationshipListComponent;
|
||||||
let fixture: ComponentFixture<EditRelationshipListComponent>;
|
let fixture: ComponentFixture<EditRelationshipListComponent>;
|
||||||
@@ -57,7 +60,11 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
relationship1 = Object.assign(new Relationship(), {
|
relationship1 = Object.assign(new Relationship(), {
|
||||||
self: url + '/2',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: url + '/2'
|
||||||
|
}
|
||||||
|
},
|
||||||
id: '2',
|
id: '2',
|
||||||
uuid: '2',
|
uuid: '2',
|
||||||
leftId: 'author1',
|
leftId: 'author1',
|
||||||
@@ -68,7 +75,11 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
relationship2 = Object.assign(new Relationship(), {
|
relationship2 = Object.assign(new Relationship(), {
|
||||||
self: url + '/3',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: url + '/3'
|
||||||
|
}
|
||||||
|
},
|
||||||
id: '3',
|
id: '3',
|
||||||
uuid: '3',
|
uuid: '3',
|
||||||
leftId: 'author2',
|
leftId: 'author2',
|
||||||
@@ -79,7 +90,9 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
item = Object.assign(new Item(), {
|
item = Object.assign(new Item(), {
|
||||||
self: 'fake-item-url/publication',
|
_links: {
|
||||||
|
self: { href: 'fake-item-url/publication' }
|
||||||
|
},
|
||||||
id: 'publication',
|
id: 'publication',
|
||||||
uuid: 'publication',
|
uuid: 'publication',
|
||||||
relationships: observableOf(new RemoteData(
|
relationships: observableOf(new RemoteData(
|
||||||
@@ -142,6 +155,8 @@ describe('EditRelationshipListComponent', () => {
|
|||||||
declarations: [EditRelationshipListComponent],
|
declarations: [EditRelationshipListComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ObjectUpdatesService, useValue: objectUpdatesService },
|
{ provide: ObjectUpdatesService, useValue: objectUpdatesService },
|
||||||
|
{ provide: RelationshipTypeService, useValue: {} },
|
||||||
|
{ provide: LinkService, useValue: getMockLinkService() },
|
||||||
], schemas: [
|
], schemas: [
|
||||||
NO_ERRORS_SCHEMA
|
NO_ERRORS_SCHEMA
|
||||||
]
|
]
|
||||||
|
@@ -1,15 +1,21 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import {FieldUpdate, FieldUpdates} from '../../../../core/data/object-updates/object-updates.reducer';
|
import {FieldUpdate, FieldUpdates} from '../../../../core/data/object-updates/object-updates.reducer';
|
||||||
import {Item} from '../../../../core/shared/item.model';
|
import {Item} from '../../../../core/shared/item.model';
|
||||||
import {map, switchMap} from 'rxjs/operators';
|
import { map, switchMap, tap } from 'rxjs/operators';
|
||||||
import {hasValue} from '../../../../shared/empty.util';
|
import {hasValue} from '../../../../shared/empty.util';
|
||||||
import {Relationship} from '../../../../core/shared/item-relationships/relationship.model';
|
import {Relationship} from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model';
|
import {RelationshipType} from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
import {getRemoteDataPayload, getSucceededRemoteData} from '../../../../core/shared/operators';
|
import {
|
||||||
import {combineLatest as observableCombineLatest, combineLatest} from 'rxjs';
|
getAllSucceededRemoteData,
|
||||||
import {ItemType} from '../../../../core/shared/item-relationships/item-type.model';
|
getRemoteDataPayload,
|
||||||
|
getSucceededRemoteData
|
||||||
|
} from '../../../../core/shared/operators';
|
||||||
|
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
|
import { ItemType } from '../../../../core/shared/item-relationships/item-type.model';
|
||||||
|
import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-relationship-list',
|
selector: 'ds-edit-relationship-list',
|
||||||
@@ -47,6 +53,7 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected objectUpdatesService: ObjectUpdatesService,
|
protected objectUpdatesService: ObjectUpdatesService,
|
||||||
|
protected linkService: LinkService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +78,7 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
private getLabel(): Observable<string> {
|
private getLabel(): Observable<string> {
|
||||||
|
|
||||||
return combineLatest([
|
return observableCombineLatest([
|
||||||
this.relationshipType.leftType,
|
this.relationshipType.leftType,
|
||||||
this.relationshipType.rightType,
|
this.relationshipType.rightType,
|
||||||
].map((itemTypeRD) => itemTypeRD.pipe(
|
].map((itemTypeRD) => itemTypeRD.pipe(
|
||||||
@@ -94,8 +101,20 @@ export class EditRelationshipListComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.updates$ = this.item.relationships.pipe(
|
this.updates$ = this.item.relationships.pipe(
|
||||||
|
getAllSucceededRemoteData(),
|
||||||
map((relationships) => relationships.payload.page.filter((relationship) => relationship)),
|
map((relationships) => relationships.payload.page.filter((relationship) => relationship)),
|
||||||
switchMap((itemRelationships) =>
|
map((relationships: Relationship[]) =>
|
||||||
|
relationships.map((relationship: Relationship) => {
|
||||||
|
this.linkService.resolveLinks(
|
||||||
|
relationship,
|
||||||
|
followLink('relationshipType'),
|
||||||
|
followLink('leftItem'),
|
||||||
|
followLink('rightItem'),
|
||||||
|
);
|
||||||
|
return relationship;
|
||||||
|
})
|
||||||
|
),
|
||||||
|
switchMap((itemRelationships: Relationship[]) =>
|
||||||
observableCombineLatest(
|
observableCombineLatest(
|
||||||
itemRelationships
|
itemRelationships
|
||||||
.map((relationship) => relationship.relationshipType.pipe(
|
.map((relationship) => relationship.relationshipType.pipe(
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import { async, TestBed } from '@angular/core/testing';
|
|
||||||
import { of as observableOf } from 'rxjs/internal/observable/of';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { EditRelationshipComponent } from './edit-relationship.component';
|
import { async, TestBed } from '@angular/core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
||||||
|
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
|
||||||
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||||
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
|
import { EditRelationshipComponent } from './edit-relationship.component';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
let objectUpdatesService;
|
let objectUpdatesService;
|
||||||
@@ -42,7 +42,11 @@ describe('EditRelationshipComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
item = Object.assign(new Item(), {
|
item = Object.assign(new Item(), {
|
||||||
self: 'fake-item-url/publication',
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'fake-item-url/publication'
|
||||||
|
}
|
||||||
|
},
|
||||||
id: 'publication',
|
id: 'publication',
|
||||||
uuid: 'publication',
|
uuid: 'publication',
|
||||||
relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships)))
|
relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships)))
|
||||||
@@ -54,7 +58,9 @@ describe('EditRelationshipComponent', () => {
|
|||||||
|
|
||||||
relationships = [
|
relationships = [
|
||||||
Object.assign(new Relationship(), {
|
Object.assign(new Relationship(), {
|
||||||
self: url + '/2',
|
_links: {
|
||||||
|
self: { href: url + '/2' }
|
||||||
|
},
|
||||||
id: '2',
|
id: '2',
|
||||||
uuid: '2',
|
uuid: '2',
|
||||||
leftId: 'author1',
|
leftId: 'author1',
|
||||||
@@ -64,7 +70,9 @@ describe('EditRelationshipComponent', () => {
|
|||||||
rightItem: observableOf(new RemoteData(false, false, true, undefined, item)),
|
rightItem: observableOf(new RemoteData(false, false, true, undefined, item)),
|
||||||
}),
|
}),
|
||||||
Object.assign(new Relationship(), {
|
Object.assign(new Relationship(), {
|
||||||
self: url + '/3',
|
_links: {
|
||||||
|
self: { href: url + '/3' }
|
||||||
|
},
|
||||||
id: '3',
|
id: '3',
|
||||||
uuid: '3',
|
uuid: '3',
|
||||||
leftId: 'author2',
|
leftId: 'author2',
|
||||||
|
@@ -1,32 +1,35 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { ItemRelationshipsComponent } from './item-relationships.component';
|
|
||||||
import { ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { INotification, Notification } from '../../../shared/notifications/models/notification.model';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { NotificationType } from '../../../shared/notifications/models/notification-type';
|
|
||||||
import { RouterStub } from '../../../shared/testing/router-stub';
|
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
|
||||||
import { SharedModule } from '../../../shared/shared.module';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
|
||||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { GLOBAL_CONFIG } from '../../../../config';
|
import { GLOBAL_CONFIG } from '../../../../config';
|
||||||
|
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||||
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { EntityTypeService } from '../../../core/data/entity-type.service';
|
||||||
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
|
import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions';
|
||||||
|
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { RelationshipService } from '../../../core/data/relationship.service';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
|
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
|
||||||
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
|
import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
|
||||||
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
|
||||||
import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
|
||||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions';
|
import { NotificationType } from '../../../shared/notifications/models/notification-type';
|
||||||
import { RelationshipService } from '../../../core/data/relationship.service';
|
import {
|
||||||
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
INotification,
|
||||||
import { getTestScheduler } from 'jasmine-marbles';
|
Notification
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
} from '../../../shared/notifications/models/notification.model';
|
||||||
import { RequestService } from '../../../core/data/request.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { EntityTypeService } from '../../../core/data/entity-type.service';
|
import { SharedModule } from '../../../shared/shared.module';
|
||||||
import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
|
import { RouterStub } from '../../../shared/testing/router-stub';
|
||||||
|
import { ItemRelationshipsComponent } from './item-relationships.component';
|
||||||
|
|
||||||
let comp: any;
|
let comp: any;
|
||||||
let fixture: ComponentFixture<ItemRelationshipsComponent>;
|
let fixture: ComponentFixture<ItemRelationshipsComponent>;
|
||||||
@@ -77,13 +80,17 @@ describe('ItemRelationshipsComponent', () => {
|
|||||||
|
|
||||||
relationships = [
|
relationships = [
|
||||||
Object.assign(new Relationship(), {
|
Object.assign(new Relationship(), {
|
||||||
self: url + '/2',
|
_links: {
|
||||||
|
self: { href: url + '/2' }
|
||||||
|
},
|
||||||
id: '2',
|
id: '2',
|
||||||
uuid: '2',
|
uuid: '2',
|
||||||
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
||||||
}),
|
}),
|
||||||
Object.assign(new Relationship(), {
|
Object.assign(new Relationship(), {
|
||||||
self: url + '/3',
|
_links: {
|
||||||
|
self: { href: url + '/3' }
|
||||||
|
},
|
||||||
id: '3',
|
id: '3',
|
||||||
uuid: '3',
|
uuid: '3',
|
||||||
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
relationshipType: observableOf(new RemoteData(false, false, true, undefined, relationshipType))
|
||||||
@@ -91,7 +98,9 @@ describe('ItemRelationshipsComponent', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
item = Object.assign(new Item(), {
|
item = Object.assign(new Item(), {
|
||||||
self: 'fake-item-url/publication',
|
_links: {
|
||||||
|
self: { href: 'fake-item-url/publication' }
|
||||||
|
},
|
||||||
id: 'publication',
|
id: 'publication',
|
||||||
uuid: 'publication',
|
uuid: 'publication',
|
||||||
relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships))),
|
relationships: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(new PageInfo(), relationships))),
|
||||||
|
@@ -4,6 +4,7 @@ import { DeleteRelationship, FieldUpdate, FieldUpdates } from '../../../core/dat
|
|||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { filter, map, switchMap, take } from 'rxjs/operators';
|
import { filter, map, switchMap, take } from 'rxjs/operators';
|
||||||
import { zip as observableZip } from 'rxjs';
|
import { zip as observableZip } from 'rxjs';
|
||||||
|
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||||
@@ -71,7 +72,10 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.itemUpdateSubscription = this.requestService.hasByHrefObservable(this.item.self).pipe(
|
this.itemUpdateSubscription = this.requestService.hasByHrefObservable(this.item.self).pipe(
|
||||||
filter((exists: boolean) => !exists),
|
filter((exists: boolean) => !exists),
|
||||||
switchMap(() => this.itemService.findById(this.item.uuid)),
|
switchMap(() => this.itemService.findById(this.item.uuid,
|
||||||
|
followLink('owningCollection'),
|
||||||
|
followLink('bundles'),
|
||||||
|
followLink('relationships'))),
|
||||||
getSucceededRemoteData(),
|
getSucceededRemoteData(),
|
||||||
).subscribe((itemRD: RemoteData<Item>) => {
|
).subscribe((itemRD: RemoteData<Item>) => {
|
||||||
this.item = itemRD.payload;
|
this.item = itemRD.payload;
|
||||||
@@ -94,7 +98,11 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
|
|||||||
|
|
||||||
this.relationshipTypes$ = this.entityType$.pipe(
|
this.relationshipTypes$ = this.entityType$.pipe(
|
||||||
switchMap((entityType) =>
|
switchMap((entityType) =>
|
||||||
this.entityTypeService.getEntityTypeRelationships(entityType.id).pipe(
|
this.entityTypeService.getEntityTypeRelationships(
|
||||||
|
entityType.id,
|
||||||
|
followLink('leftType'),
|
||||||
|
followLink('rightType'))
|
||||||
|
.pipe(
|
||||||
getSucceededRemoteData(),
|
getSucceededRemoteData(),
|
||||||
getRemoteDataPayload(),
|
getRemoteDataPayload(),
|
||||||
map((relationshipTypes) => relationshipTypes.page),
|
map((relationshipTypes) => relationshipTypes.page),
|
||||||
|
@@ -33,7 +33,7 @@ describe('ItemStatusComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [ItemStatusComponent],
|
declarations: [ItemStatusComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
@@ -60,7 +60,7 @@ describe('ItemWithdrawComponent', () => {
|
|||||||
notificationsServiceStub = new NotificationsServiceStub();
|
notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot(),],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule,],
|
||||||
declarations: [ItemWithdrawComponent],
|
declarations: [ItemWithdrawComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
@@ -83,7 +83,7 @@ describe('AbstractSimpleItemActionComponent', () => {
|
|||||||
notificationsServiceStub = new NotificationsServiceStub();
|
notificationsServiceStub = new NotificationsServiceStub();
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
declarations: [MySimpleItemActionComponent],
|
declarations: [MySimpleItemActionComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-metadata-field-wrapper *ngIf="hasSucceeded() | async" [label]="label | translate">
|
<ds-metadata-field-wrapper *ngIf="(this.collectionsRD$ | async)?.hasSucceeded" [label]="label | translate">
|
||||||
<div class="collections">
|
<div class="collections">
|
||||||
<a *ngFor="let collection of (collections | async); let last=last;" [routerLink]="['/collections', collection.id]">
|
<a *ngFor="let collection of (this.collectionsRD$ | async)?.payload?.page; let last=last;" [routerLink]="['/collections', collection.id]">
|
||||||
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
|
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,22 +1,20 @@
|
|||||||
import { CollectionsComponent } from './collections.component';
|
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
|
||||||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
|
||||||
import { getMockRemoteDataBuildService } from '../../../shared/mocks/mock-remote-data-build.service';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import {
|
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
||||||
createFailedRemoteDataObject$,
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
createSuccessfulRemoteDataObject$
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
} from '../../../shared/testing/utils';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { getMockRemoteDataBuildService } from '../../../shared/mocks/mock-remote-data-build.service';
|
||||||
|
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
|
import { CollectionsComponent } from './collections.component';
|
||||||
|
|
||||||
let collectionsComponent: CollectionsComponent;
|
let collectionsComponent: CollectionsComponent;
|
||||||
let fixture: ComponentFixture<CollectionsComponent>;
|
let fixture: ComponentFixture<CollectionsComponent>;
|
||||||
|
|
||||||
|
let collectionDataServiceStub;
|
||||||
|
|
||||||
const mockCollection1: Collection = Object.assign(new Collection(), {
|
const mockCollection1: Collection = Object.assign(new Collection(), {
|
||||||
metadata: {
|
metadata: {
|
||||||
'dc.description.abstract': [
|
'dc.description.abstract': [
|
||||||
@@ -32,12 +30,22 @@ const succeededMockItem: Item = Object.assign(new Item(), {owningCollection: cre
|
|||||||
const failedMockItem: Item = Object.assign(new Item(), {owningCollection: createFailedRemoteDataObject$(mockCollection1)});
|
const failedMockItem: Item = Object.assign(new Item(), {owningCollection: createFailedRemoteDataObject$(mockCollection1)});
|
||||||
|
|
||||||
describe('CollectionsComponent', () => {
|
describe('CollectionsComponent', () => {
|
||||||
|
collectionDataServiceStub = {
|
||||||
|
findOwningCollectionFor(item: Item) {
|
||||||
|
if (item === succeededMockItem) {
|
||||||
|
return createSuccessfulRemoteDataObject$(mockCollection1);
|
||||||
|
} else {
|
||||||
|
return createFailedRemoteDataObject$(mockCollection1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot()],
|
imports: [TranslateModule.forRoot()],
|
||||||
declarations: [ CollectionsComponent ],
|
declarations: [ CollectionsComponent ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: RemoteDataBuildService, useValue: getMockRemoteDataBuildService()}
|
{ provide: RemoteDataBuildService, useValue: getMockRemoteDataBuildService()},
|
||||||
|
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
schemas: [ NO_ERRORS_SCHEMA ]
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
|
|
||||||
import {map} from 'rxjs/operators';
|
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the parent collections section of the item
|
* This component renders the parent collections section of the item
|
||||||
@@ -25,9 +26,9 @@ export class CollectionsComponent implements OnInit {
|
|||||||
|
|
||||||
separator = '<br/>';
|
separator = '<br/>';
|
||||||
|
|
||||||
collections: Observable<Collection[]>;
|
collectionsRD$: Observable<RemoteData<PaginatedList<Collection>>>;
|
||||||
|
|
||||||
constructor(private rdbs: RemoteDataBuildService) {
|
constructor(private cds: CollectionDataService) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,11 +38,25 @@ export class CollectionsComponent implements OnInit {
|
|||||||
// TODO: this should use parents, but the collections
|
// TODO: this should use parents, but the collections
|
||||||
// for an Item aren't returned by the REST API yet,
|
// for an Item aren't returned by the REST API yet,
|
||||||
// only the owning collection
|
// only the owning collection
|
||||||
this.collections = this.item.owner.pipe(map((rd: RemoteData<Collection>) => [rd.payload]));
|
this.collectionsRD$ = this.cds.findOwningCollectionFor(this.item).pipe(
|
||||||
|
map((rd: RemoteData<Collection>) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
return new RemoteData(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
new PaginatedList({
|
||||||
|
elementsPerPage: 10,
|
||||||
|
totalPages: 1,
|
||||||
|
currentPage: 1,
|
||||||
|
totalElements: 1
|
||||||
|
} as PageInfo, [rd.payload])
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return rd as any;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSucceeded() {
|
|
||||||
return this.item.owner.pipe(map((rd: RemoteData<Collection>) => rd.hasSucceeded));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<div class="file-section row" *ngFor="let file of (bitstreamsObs | async); let last=last;">
|
<div class="file-section row" *ngFor="let file of (bitstreams$ | async); let last=last;">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<ds-thumbnail [thumbnail]="thumbnails.get(file.id) | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<a [href]="file.content" [download]="file.name">
|
<a [href]="file._links.content.href" [download]="file.name">
|
||||||
{{"item.page.filesection.download" | translate}}
|
{{"item.page.filesection.download" | translate}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
|
import { Component, Injector, Input, OnInit } from '@angular/core';
|
||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { map, startWith } from 'rxjs/operators';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
||||||
|
import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
||||||
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
|
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the file section of the item
|
* This component renders the file section of the item
|
||||||
@@ -22,27 +25,49 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
|||||||
|
|
||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
bitstreamsObs: Observable<Bitstream[]>;
|
bitstreams$: Observable<Bitstream[]>;
|
||||||
|
|
||||||
thumbnails: Map<string, Observable<Bitstream>> = new Map();
|
constructor(
|
||||||
|
bitstreamDataService: BitstreamDataService
|
||||||
|
) {
|
||||||
|
super(bitstreamDataService);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(): void {
|
initialize(): void {
|
||||||
const originals = this.item.getFiles();
|
// TODO pagination
|
||||||
const licenses = this.item.getBitstreamsByBundleName('LICENSE');
|
const originals$ = this.bitstreamDataService.findAllByItemAndBundleName(
|
||||||
this.bitstreamsObs = observableCombineLatest(originals, licenses).pipe(map(([o, l]) => [...o, ...l]));
|
this.item,
|
||||||
this.bitstreamsObs.subscribe(
|
'ORIGINAL',
|
||||||
(files) =>
|
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
|
||||||
files.forEach(
|
followLink( 'format')
|
||||||
|
).pipe(
|
||||||
|
getFirstSucceededRemoteListPayload(),
|
||||||
|
startWith([])
|
||||||
|
);
|
||||||
|
const licenses$ = this.bitstreamDataService.findAllByItemAndBundleName(
|
||||||
|
this.item,
|
||||||
|
'LICENSE',
|
||||||
|
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
|
||||||
|
followLink( 'format')
|
||||||
|
).pipe(
|
||||||
|
getFirstSucceededRemoteListPayload(),
|
||||||
|
startWith([])
|
||||||
|
);
|
||||||
|
this.bitstreams$ = observableCombineLatest(originals$, licenses$).pipe(
|
||||||
|
map(([o, l]) => [...o, ...l]),
|
||||||
|
map((files: Bitstream[]) =>
|
||||||
|
files.map(
|
||||||
(original) => {
|
(original) => {
|
||||||
const thumbnail: Observable<Bitstream> = this.item.getThumbnailForOriginal(original);
|
original.thumbnail = this.bitstreamDataService.getMatchingThumbnail(this.item, original);
|
||||||
this.thumbnails.set(original.id, thumbnail);
|
return original;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import { ItemDataService } from '../core/data/item-data.service';
|
|||||||
import { Item } from '../core/shared/item.model';
|
import { Item } from '../core/shared/item.model';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { find } from 'rxjs/operators';
|
import { find } from 'rxjs/operators';
|
||||||
|
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a resolver that requests a specific item before the route is activated
|
* This class represents a resolver that requests a specific item before the route is activated
|
||||||
@@ -23,9 +24,12 @@ export class ItemPageResolver implements Resolve<RemoteData<Item>> {
|
|||||||
* or an error if something went wrong
|
* or an error if something went wrong
|
||||||
*/
|
*/
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
|
||||||
return this.itemService.findById(route.params.id)
|
return this.itemService.findById(route.params.id,
|
||||||
.pipe(
|
followLink('owningCollection'),
|
||||||
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
|
followLink('bundles'),
|
||||||
);
|
followLink('relationships')
|
||||||
|
).pipe(
|
||||||
|
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<ng-container *ngVar="(bitstreamsObs | async) as bitstreams">
|
<ng-container *ngVar="(bitstreams$ | async) as bitstreams">
|
||||||
<ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate">
|
<ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate">
|
||||||
<div class="file-section">
|
<div class="file-section">
|
||||||
<a *ngFor="let file of bitstreams; let last=last;" [href]="file?.content" [download]="file?.name">
|
<a *ngFor="let file of bitstreams; let last=last;" [href]="file?._links.content.href" [download]="file?.name">
|
||||||
<span>{{file?.name}}</span>
|
<span>{{file?.name}}</span>
|
||||||
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
||||||
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the file section of the item
|
* This component renders the file section of the item
|
||||||
@@ -20,14 +22,21 @@ export class FileSectionComponent implements OnInit {
|
|||||||
|
|
||||||
separator = '<br/>';
|
separator = '<br/>';
|
||||||
|
|
||||||
bitstreamsObs: Observable<Bitstream[]>;
|
bitstreams$: Observable<Bitstream[]>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected bitstreamDataService: BitstreamDataService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(): void {
|
initialize(): void {
|
||||||
this.bitstreamsObs = this.item.getFiles();
|
this.bitstreams$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL').pipe(
|
||||||
|
getFirstSucceededRemoteListPayload()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
|
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
|
||||||
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
||||||
|
@@ -1,20 +1,34 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
|
|
||||||
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
|
||||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
|
||||||
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
|
||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
import { CommunityDataService } from '../../../../core/data/community-data.service';
|
||||||
|
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
|
||||||
|
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
|
||||||
|
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
|
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||||
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
||||||
|
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||||
|
import { UUIDService } from '../../../../core/shared/uuid.service';
|
||||||
|
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||||
|
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
||||||
|
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||||
|
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
||||||
import { createRelationshipsObservable } from '../shared/item.component.spec';
|
import { createRelationshipsObservable } from '../shared/item.component.spec';
|
||||||
import { PublicationComponent } from './publication.component';
|
import { PublicationComponent } from './publication.component';
|
||||||
import { MetadataMap } from '../../../../core/shared/metadata.models';
|
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
|
||||||
|
|
||||||
const mockItem: Item = Object.assign(new Item(), {
|
const mockItem: Item = Object.assign(new Item(), {
|
||||||
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
bundles: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
|
||||||
@@ -27,6 +41,11 @@ describe('PublicationComponent', () => {
|
|||||||
let fixture: ComponentFixture<PublicationComponent>;
|
let fixture: ComponentFixture<PublicationComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
|
const mockBitstreamDataService = {
|
||||||
|
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
|
||||||
|
return createSuccessfulRemoteDataObject$(new Bitstream());
|
||||||
|
}
|
||||||
|
};
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
@@ -36,14 +55,25 @@ describe('PublicationComponent', () => {
|
|||||||
})],
|
})],
|
||||||
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ItemDataService, useValue: {}},
|
{ provide: ItemDataService, useValue: {} },
|
||||||
{provide: TruncatableService, useValue: {}},
|
{ provide: TruncatableService, useValue: {} },
|
||||||
{provide: RelationshipService, useValue: {}}
|
{ provide: RelationshipService, useValue: {} },
|
||||||
|
{ provide: ObjectCacheService, useValue: {} },
|
||||||
|
{ provide: UUIDService, useValue: {} },
|
||||||
|
{ provide: Store, useValue: {} },
|
||||||
|
{ provide: RemoteDataBuildService, useValue: {} },
|
||||||
|
{ provide: CommunityDataService, useValue: {} },
|
||||||
|
{ provide: HALEndpointService, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: {} },
|
||||||
|
{ provide: HttpClient, useValue: {} },
|
||||||
|
{ provide: DSOChangeAnalyzer, useValue: {} },
|
||||||
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
|
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(PublicationComponent, {
|
}).overrideComponent(PublicationComponent, {
|
||||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@@ -17,4 +17,5 @@ import { listableObjectComponent } from '../../../../shared/object-collection/sh
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class PublicationComponent extends ItemComponent {
|
export class PublicationComponent extends ItemComponent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { getSucceededRemoteData } from '../../../../core/shared/operators';
|
|
||||||
import { hasValue } from '../../../../shared/empty.util';
|
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
|
||||||
import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
|
|
||||||
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
import { combineLatest as observableCombineLatest, zip as observableZip } from 'rxjs';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { distinctUntilChanged, flatMap, map, switchMap } from 'rxjs/operators';
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { getFinishedRemoteData, getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||||
|
import { hasValue } from '../../../../shared/empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operator for comparing arrays using a mapping function
|
* Operator for comparing arrays using a mapping function
|
||||||
@@ -74,8 +74,8 @@ export const paginatedRelationsToItems = (thisId: string) =>
|
|||||||
source.pipe(
|
source.pipe(
|
||||||
getSucceededRemoteData(),
|
getSucceededRemoteData(),
|
||||||
switchMap((relationshipsRD: RemoteData<PaginatedList<Relationship>>) => {
|
switchMap((relationshipsRD: RemoteData<PaginatedList<Relationship>>) => {
|
||||||
return observableZip(
|
return observableCombineLatest(
|
||||||
...relationshipsRD.payload.page.map((rel: Relationship) => observableCombineLatest(rel.leftItem, rel.rightItem))
|
...relationshipsRD.payload.page.map((rel: Relationship) => observableCombineLatest(rel.leftItem.pipe(getFinishedRemoteData()), rel.rightItem.pipe(getFinishedRemoteData())))
|
||||||
).pipe(
|
).pipe(
|
||||||
map((arr) =>
|
map((arr) =>
|
||||||
arr
|
arr
|
||||||
|
@@ -1,29 +1,36 @@
|
|||||||
import { Item } from '../../../../core/shared/item.model';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
import { Store } from '@ngrx/store';
|
||||||
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
|
|
||||||
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
|
||||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
|
||||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
|
||||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
|
||||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
|
||||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
|
||||||
import { ItemComponent } from './item.component';
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { VarDirective } from '../../../../shared/utils/var.directive';
|
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
|
||||||
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
|
||||||
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
|
import { CommunityDataService } from '../../../../core/data/community-data.service';
|
||||||
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
|
||||||
|
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
|
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||||
import { RelationshipService } from '../../../../core/data/relationship.service';
|
import { RelationshipService } from '../../../../core/data/relationship.service';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
|
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
|
||||||
|
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||||
|
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||||
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||||
|
import { UUIDService } from '../../../../core/shared/uuid.service';
|
||||||
|
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||||
|
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
|
||||||
|
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
||||||
|
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||||
|
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
||||||
|
import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
|
||||||
|
import { ItemComponent } from './item.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a generic test for an item-page-fields component using a mockItem and the type of component
|
* Create a generic test for an item-page-fields component using a mockItem and the type of component
|
||||||
@@ -38,6 +45,11 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
|||||||
let fixture: ComponentFixture<any>;
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
|
const mockBitstreamDataService = {
|
||||||
|
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
|
||||||
|
return createSuccessfulRemoteDataObject$(new Bitstream());
|
||||||
|
}
|
||||||
|
};
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot({
|
imports: [TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
@@ -47,14 +59,25 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
|
|||||||
})],
|
})],
|
||||||
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ItemDataService, useValue: {}},
|
{ provide: ItemDataService, useValue: {} },
|
||||||
{provide: TruncatableService, useValue: {}},
|
{ provide: TruncatableService, useValue: {} },
|
||||||
{provide: RelationshipService, useValue: {}}
|
{ provide: RelationshipService, useValue: {} },
|
||||||
|
{ provide: ObjectCacheService, useValue: {} },
|
||||||
|
{ provide: UUIDService, useValue: {} },
|
||||||
|
{ provide: Store, useValue: {} },
|
||||||
|
{ provide: RemoteDataBuildService, useValue: {} },
|
||||||
|
{ provide: CommunityDataService, useValue: {} },
|
||||||
|
{ provide: HALEndpointService, useValue: {} },
|
||||||
|
{ provide: HttpClient, useValue: {} },
|
||||||
|
{ provide: DSOChangeAnalyzer, useValue: {} },
|
||||||
|
{ provide: NotificationsService, useValue: {} },
|
||||||
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
|
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(component, {
|
}).overrideComponent(component, {
|
||||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -102,6 +125,7 @@ export function createRelationshipsObservable() {
|
|||||||
})
|
})
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('ItemComponent', () => {
|
describe('ItemComponent', () => {
|
||||||
const arr1 = [
|
const arr1 = [
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item',
|
selector: 'ds-item',
|
||||||
@@ -10,4 +14,14 @@ import { Item } from '../../../../core/shared/item.model';
|
|||||||
*/
|
*/
|
||||||
export class ItemComponent {
|
export class ItemComponent {
|
||||||
@Input() object: Item;
|
@Input() object: Item;
|
||||||
|
|
||||||
|
constructor(protected bitstreamDataService: BitstreamDataService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||||
|
getThumbnail(): Observable<Bitstream> {
|
||||||
|
return this.bitstreamDataService.getThumbnailFor(this.object).pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ import { Relationship } from '../../../core/shared/item-relationships/relationsh
|
|||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||||
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||||
|
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
|
import { AbstractIncrementalListComponent } from '../abstract-incremental-list/abstract-incremental-list.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -81,7 +82,7 @@ export class MetadataRepresentationListComponent extends AbstractIncrementalList
|
|||||||
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
|
||||||
.map((metadatum: MetadataValue) => {
|
.map((metadatum: MetadataValue) => {
|
||||||
if (metadatum.isVirtual) {
|
if (metadatum.isVirtual) {
|
||||||
return this.relationshipService.findById(metadatum.virtualValue).pipe(
|
return this.relationshipService.findById(metadatum.virtualValue, followLink('leftItem'), followLink('rightItem')).pipe(
|
||||||
getSucceededRemoteData(),
|
getSucceededRemoteData(),
|
||||||
switchMap((relRD: RemoteData<Relationship>) =>
|
switchMap((relRD: RemoteData<Relationship>) =>
|
||||||
observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe(
|
observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe(
|
||||||
|
@@ -29,7 +29,7 @@ describe('TabbedRelatedEntitiesSearchComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule, NgbModule.forRoot()],
|
imports: [TranslateModule.forRoot(), NoopAnimationsModule, NgbModule],
|
||||||
declarations: [TabbedRelatedEntitiesSearchComponent, VarDirective],
|
declarations: [TabbedRelatedEntitiesSearchComponent, VarDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { LookupGuard } from './lookup-guard';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { IdentifierType } from '../core/data/request.models';
|
import { IdentifierType } from '../core/data/request.models';
|
||||||
|
import { LookupGuard } from './lookup-guard';
|
||||||
|
|
||||||
describe('LookupGuard', () => {
|
describe('LookupGuard', () => {
|
||||||
let dsoService: any;
|
let dsoService: any;
|
||||||
@@ -8,13 +8,13 @@ describe('LookupGuard', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dsoService = {
|
dsoService = {
|
||||||
findById: jasmine.createSpy('findById').and.returnValue(observableOf({ hasFailed: false,
|
findByIdAndIDType: jasmine.createSpy('findByIdAndIDType').and.returnValue(observableOf({ hasFailed: false,
|
||||||
hasSucceeded: true }))
|
hasSucceeded: true }))
|
||||||
};
|
};
|
||||||
guard = new LookupGuard(dsoService);
|
guard = new LookupGuard(dsoService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call findById with handle params', () => {
|
it('should call findByIdAndIDType with handle params', () => {
|
||||||
const scopedRoute = {
|
const scopedRoute = {
|
||||||
params: {
|
params: {
|
||||||
id: '1234',
|
id: '1234',
|
||||||
@@ -22,10 +22,10 @@ describe('LookupGuard', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
guard.canActivate(scopedRoute as any, undefined);
|
guard.canActivate(scopedRoute as any, undefined);
|
||||||
expect(dsoService.findById).toHaveBeenCalledWith('123456789/1234', IdentifierType.HANDLE)
|
expect(dsoService.findByIdAndIDType).toHaveBeenCalledWith('123456789/1234', IdentifierType.HANDLE)
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call findById with handle params', () => {
|
it('should call findByIdAndIDType with handle params', () => {
|
||||||
const scopedRoute = {
|
const scopedRoute = {
|
||||||
params: {
|
params: {
|
||||||
id: '123456789%2F1234',
|
id: '123456789%2F1234',
|
||||||
@@ -33,10 +33,10 @@ describe('LookupGuard', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
guard.canActivate(scopedRoute as any, undefined);
|
guard.canActivate(scopedRoute as any, undefined);
|
||||||
expect(dsoService.findById).toHaveBeenCalledWith('123456789%2F1234', IdentifierType.HANDLE)
|
expect(dsoService.findByIdAndIDType).toHaveBeenCalledWith('123456789%2F1234', IdentifierType.HANDLE)
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call findById with UUID params', () => {
|
it('should call findByIdAndIDType with UUID params', () => {
|
||||||
const scopedRoute = {
|
const scopedRoute = {
|
||||||
params: {
|
params: {
|
||||||
id: '34cfed7c-f597-49ef-9cbe-ea351f0023c2',
|
id: '34cfed7c-f597-49ef-9cbe-ea351f0023c2',
|
||||||
@@ -44,7 +44,7 @@ describe('LookupGuard', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
guard.canActivate(scopedRoute as any, undefined);
|
guard.canActivate(scopedRoute as any, undefined);
|
||||||
expect(dsoService.findById).toHaveBeenCalledWith('34cfed7c-f597-49ef-9cbe-ea351f0023c2', IdentifierType.UUID)
|
expect(dsoService.findByIdAndIDType).toHaveBeenCalledWith('34cfed7c-f597-49ef-9cbe-ea351f0023c2', IdentifierType.UUID)
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -20,7 +20,7 @@ export class LookupGuard implements CanActivate {
|
|||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||||
const params = this.getLookupParams(route);
|
const params = this.getLookupParams(route);
|
||||||
return this.dsoService.findById(params.id, params.type).pipe(
|
return this.dsoService.findByIdAndIDType(params.id, params.type).pipe(
|
||||||
map((response: RemoteData<FindByIDRequest>) => response.hasFailed)
|
map((response: RemoteData<FindByIDRequest>) => response.hasFailed)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -84,7 +84,7 @@ describe('MyDSpacePageComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()],
|
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule],
|
||||||
declarations: [MyDSpacePageComponent, RoleDirective],
|
declarations: [MyDSpacePageComponent, RoleDirective],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: SearchService, useValue: searchServiceStub },
|
{ provide: SearchService, useValue: searchServiceStub },
|
||||||
|
@@ -5,6 +5,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .search-controls {
|
::ng-deep .search-controls {
|
||||||
margin-bottom: $spacer;
|
margin-bottom: $spacer;
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,7 @@ const routeServiceStub = {
|
|||||||
|
|
||||||
export function configureSearchComponentTestingModule(compType) {
|
export function configureSearchComponentTestingModule(compType) {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()],
|
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule],
|
||||||
declarations: [compType],
|
declarations: [compType],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: SearchService, useValue: searchServiceStub },
|
{ provide: SearchService, useValue: searchServiceStub },
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
[options]="config.notifications">
|
[options]="config.notifications">
|
||||||
</ds-notifications-board>
|
</ds-notifications-board>
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
<div class="container" *ngIf="isLoading">
|
<div class="container" *ngIf="isLoading$ | async">
|
||||||
<ds-loading message="{{'loading.default' | translate}}"></ds-loading>
|
<ds-loading message="{{'loading.default' | translate}}"></ds-loading>
|
||||||
</div>
|
</div>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
@@ -19,7 +19,7 @@ import variables from '../styles/_exposed_variables.scss';
|
|||||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||||
import { MenuService } from './shared/menu/menu.service';
|
import { MenuService } from './shared/menu/menu.service';
|
||||||
import { MenuID } from './shared/menu/initial-menus-state';
|
import { MenuID } from './shared/menu/initial-menus-state';
|
||||||
import { combineLatest as combineLatestObservable, Observable, of } from 'rxjs';
|
import { BehaviorSubject, combineLatest as combineLatestObservable, Observable, of } from 'rxjs';
|
||||||
import { slideSidebarPadding } from './shared/animations/slide';
|
import { slideSidebarPadding } from './shared/animations/slide';
|
||||||
import { HostWindowService } from './shared/host-window.service';
|
import { HostWindowService } from './shared/host-window.service';
|
||||||
import { Theme } from '../config/theme.inferface';
|
import { Theme } from '../config/theme.inferface';
|
||||||
@@ -38,7 +38,7 @@ export const LANG_COOKIE = 'language_cookie';
|
|||||||
animations: [slideSidebarPadding]
|
animations: [slideSidebarPadding]
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, AfterViewInit {
|
export class AppComponent implements OnInit, AfterViewInit {
|
||||||
isLoading = true;
|
isLoading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
||||||
sidebarVisible: Observable<boolean>;
|
sidebarVisible: Observable<boolean>;
|
||||||
slideSidebarOver: Observable<boolean>;
|
slideSidebarOver: Observable<boolean>;
|
||||||
collapsedSidebarWidth: Observable<string>;
|
collapsedSidebarWidth: Observable<string>;
|
||||||
@@ -131,12 +131,12 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
delay(0)
|
delay(0)
|
||||||
).subscribe((event) => {
|
).subscribe((event) => {
|
||||||
if (event instanceof NavigationStart) {
|
if (event instanceof NavigationStart) {
|
||||||
this.isLoading = true;
|
this.isLoading$.next(true);
|
||||||
} else if (
|
} else if (
|
||||||
event instanceof NavigationEnd ||
|
event instanceof NavigationEnd ||
|
||||||
event instanceof NavigationCancel
|
event instanceof NavigationCancel
|
||||||
) {
|
) {
|
||||||
this.isLoading = false;
|
this.isLoading$.next(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { isNotEmpty } from './shared/empty.util';
|
|
||||||
import { StoreActionTypes } from './store.actions';
|
import { StoreActionTypes } from './store.actions';
|
||||||
|
|
||||||
// fallback ngrx debugger
|
// fallback ngrx debugger
|
||||||
|
@@ -6,14 +6,12 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
|||||||
|
|
||||||
import { EffectsModule } from '@ngrx/effects';
|
import { EffectsModule } from '@ngrx/effects';
|
||||||
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||||
import { META_REDUCERS, MetaReducer, StoreModule } from '@ngrx/store';
|
import { META_REDUCERS, MetaReducer, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
|
||||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||||
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
||||||
|
|
||||||
import { storeFreeze } from 'ngrx-store-freeze';
|
|
||||||
|
|
||||||
import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../config';
|
import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../config';
|
||||||
import { AdminSidebarSectionComponent } from './+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
|
import { AdminSidebarSectionComponent } from './+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
|
||||||
import { AdminSidebarComponent } from './+admin/admin-sidebar/admin-sidebar.component';
|
import { AdminSidebarComponent } from './+admin/admin-sidebar/admin-sidebar.component';
|
||||||
@@ -23,7 +21,7 @@ import { AppRoutingModule } from './app-routing.module';
|
|||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
import { appEffects } from './app.effects';
|
import { appEffects } from './app.effects';
|
||||||
import { appMetaReducers, debugMetaReducers } from './app.metareducers';
|
import { appMetaReducers, debugMetaReducers, universalMetaReducer } from './app.metareducers';
|
||||||
import { appReducers, AppState } from './app.reducer';
|
import { appReducers, AppState } from './app.reducer';
|
||||||
|
|
||||||
import { CoreModule } from './core/core.module';
|
import { CoreModule } from './core/core.module';
|
||||||
@@ -51,8 +49,7 @@ export function getBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getMetaReducers(config: GlobalConfig): Array<MetaReducer<AppState>> {
|
export function getMetaReducers(config: GlobalConfig): Array<MetaReducer<AppState>> {
|
||||||
const metaReducers: Array<MetaReducer<AppState>> = config.production ? appMetaReducers : [...appMetaReducers, storeFreeze];
|
return config.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
|
||||||
return config.debug ? [...metaReducers, ...debugMetaReducers] : metaReducers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const IMPORTS = [
|
const IMPORTS = [
|
||||||
@@ -63,11 +60,11 @@ const IMPORTS = [
|
|||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
CoreModule.forRoot(),
|
CoreModule.forRoot(),
|
||||||
ScrollToModule.forRoot(),
|
ScrollToModule.forRoot(),
|
||||||
NgbModule.forRoot(),
|
NgbModule,
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
EffectsModule.forRoot(appEffects),
|
EffectsModule.forRoot(appEffects),
|
||||||
StoreModule.forRoot(appReducers),
|
StoreModule.forRoot(appReducers),
|
||||||
StoreRouterConnectingModule,
|
StoreRouterConnectingModule.forRoot(),
|
||||||
];
|
];
|
||||||
|
|
||||||
const ENTITY_IMPORTS = [
|
const ENTITY_IMPORTS = [
|
||||||
@@ -92,7 +89,7 @@ const PROVIDERS = [
|
|||||||
useFactory: (getBase)
|
useFactory: (getBase)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: META_REDUCERS,
|
provide: USER_PROVIDED_META_REDUCERS,
|
||||||
useFactory: getMetaReducers,
|
useFactory: getMetaReducers,
|
||||||
deps: [GLOBAL_CONFIG]
|
deps: [GLOBAL_CONFIG]
|
||||||
},
|
},
|
||||||
|
@@ -190,8 +190,6 @@ describe('CommunityListService', () => {
|
|||||||
service = new CommunityListService(communityDataServiceStub, collectionDataServiceStub, store);
|
service = new CommunityListService(communityDataServiceStub, collectionDataServiceStub, store);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
afterAll(() => service = new CommunityListService(communityDataServiceStub, collectionDataServiceStub, store));
|
|
||||||
|
|
||||||
it('should create', inject([CommunityListService], (serviceIn: CommunityListService) => {
|
it('should create', inject([CommunityListService], (serviceIn: CommunityListService) => {
|
||||||
expect(serviceIn).toBeTruthy();
|
expect(serviceIn).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
|
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import { EPersonDataService } from '../eperson/eperson-data.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { GLOBAL_CONFIG } from '../../../config';
|
import { GLOBAL_CONFIG } from '../../../config';
|
||||||
|
@@ -3,15 +3,16 @@ import { async, TestBed } from '@angular/core/testing';
|
|||||||
import { Store, StoreModule } from '@ngrx/store';
|
import { Store, StoreModule } from '@ngrx/store';
|
||||||
|
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
import { AuthStatusResponse } from '../cache/response.models';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
|
||||||
import { AuthStatus } from './models/auth-status.model';
|
|
||||||
import { AuthResponseParsingService } from './auth-response-parsing.service';
|
|
||||||
import { AuthGetRequest, AuthPostRequest } from '../data/request.models';
|
|
||||||
import { MockStore } from '../../shared/testing/mock-store';
|
import { MockStore } from '../../shared/testing/mock-store';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { AuthStatusResponse } from '../cache/response.models';
|
||||||
|
import { AuthGetRequest, AuthPostRequest } from '../data/request.models';
|
||||||
|
import { AuthResponseParsingService } from './auth-response-parsing.service';
|
||||||
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
|
|
||||||
describe('AuthResponseParsingService', () => {
|
describe('AuthResponseParsingService', () => {
|
||||||
let service: AuthResponseParsingService;
|
let service: AuthResponseParsingService;
|
||||||
|
let linkServiceStub: any;
|
||||||
|
|
||||||
const EnvConfig: GlobalConfig = { cache: { msToLive: 1000 } } as any;
|
const EnvConfig: GlobalConfig = { cache: { msToLive: 1000 } } as any;
|
||||||
let store: any;
|
let store: any;
|
||||||
@@ -30,7 +31,10 @@ describe('AuthResponseParsingService', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
store = TestBed.get(Store);
|
store = TestBed.get(Store);
|
||||||
objectCacheService = new ObjectCacheService(store as any);
|
linkServiceStub = jasmine.createSpyObj({
|
||||||
|
removeResolvedLinks: {}
|
||||||
|
});
|
||||||
|
objectCacheService = new ObjectCacheService(store as any, linkServiceStub);
|
||||||
service = new AuthResponseParsingService(EnvConfig, objectCacheService);
|
service = new AuthResponseParsingService(EnvConfig, objectCacheService);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -141,6 +145,7 @@ describe('AuthResponseParsingService', () => {
|
|||||||
it('should return a AuthStatusResponse if data contains a valid endpoint response', () => {
|
it('should return a AuthStatusResponse if data contains a valid endpoint response', () => {
|
||||||
const response = service.parse(validRequest2, validResponse2);
|
const response = service.parse(validRequest2, validResponse2);
|
||||||
expect(response.constructor).toBe(AuthStatusResponse);
|
expect(response.constructor).toBe(AuthStatusResponse);
|
||||||
|
expect(linkServiceStub.removeResolvedLinks).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a AuthStatusResponse if data contains an empty 404 endpoint response', () => {
|
it('should return a AuthStatusResponse if data contains an empty 404 endpoint response', () => {
|
||||||
|
@@ -10,8 +10,6 @@ import { ObjectCacheService } from '../cache/object-cache.service';
|
|||||||
import { ResponseParsingService } from '../data/parsing.service';
|
import { ResponseParsingService } from '../data/parsing.service';
|
||||||
import { RestRequest } from '../data/request.models';
|
import { RestRequest } from '../data/request.models';
|
||||||
import { AuthStatus } from './models/auth-status.model';
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
import { NormalizedAuthStatus } from './models/normalized-auth-status.model';
|
|
||||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthResponseParsingService extends BaseResponseParsingService implements ResponseParsingService {
|
export class AuthResponseParsingService extends BaseResponseParsingService implements ResponseParsingService {
|
||||||
@@ -25,10 +23,10 @@ export class AuthResponseParsingService extends BaseResponseParsingService imple
|
|||||||
|
|
||||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||||
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && (data.statusCode === 200)) {
|
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && (data.statusCode === 200)) {
|
||||||
const response = this.process<NormalizedObject<AuthStatus>>(data.payload, request);
|
const response = this.process<AuthStatus>(data.payload, request);
|
||||||
return new AuthStatusResponse(response, data.statusCode, data.statusText);
|
return new AuthStatusResponse(response, data.statusCode, data.statusText);
|
||||||
} else {
|
} else {
|
||||||
return new AuthStatusResponse(data.payload as NormalizedAuthStatus, data.statusCode, data.statusText);
|
return new AuthStatusResponse(data.payload as AuthStatus, data.statusCode, data.statusText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { Store, StoreModule } from '@ngrx/store';
|
import { Store, StoreModule } from '@ngrx/store';
|
||||||
import { REQUEST } from '@nguniversal/express-engine/tokens';
|
import { REQUEST } from '@nguniversal/express-engine/tokens';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { LinkService } from '../cache/builders/link.service';
|
||||||
|
|
||||||
import { authReducer, AuthState } from './auth.reducer';
|
import { authReducer, AuthState } from './auth.reducer';
|
||||||
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
|
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
|
||||||
@@ -40,7 +41,7 @@ describe('AuthService test', () => {
|
|||||||
let storage: CookieService;
|
let storage: CookieService;
|
||||||
let token: AuthTokenInfo;
|
let token: AuthTokenInfo;
|
||||||
let authenticatedState;
|
let authenticatedState;
|
||||||
let rdbService;
|
let linkService;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
mockStore = jasmine.createSpyObj('store', {
|
mockStore = jasmine.createSpyObj('store', {
|
||||||
@@ -60,8 +61,10 @@ describe('AuthService test', () => {
|
|||||||
};
|
};
|
||||||
authRequest = new AuthRequestServiceStub();
|
authRequest = new AuthRequestServiceStub();
|
||||||
routeStub = new ActivatedRouteStub();
|
routeStub = new ActivatedRouteStub();
|
||||||
rdbService = getMockRemoteDataBuildService();
|
linkService = {
|
||||||
spyOn(rdbService, 'build').and.returnValue({authenticated: true, eperson: observableOf({payload: {}})});
|
resolveLinks: {}
|
||||||
|
};
|
||||||
|
spyOn(linkService, 'resolveLinks').and.returnValue({authenticated: true, eperson: observableOf({payload: {}})});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +85,7 @@ describe('AuthService test', () => {
|
|||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
{ provide: Store, useValue: mockStore },
|
{ provide: Store, useValue: mockStore },
|
||||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
{ provide: LinkService, useValue: linkService },
|
||||||
CookieService,
|
CookieService,
|
||||||
AuthService
|
AuthService
|
||||||
],
|
],
|
||||||
@@ -165,7 +168,7 @@ describe('AuthService test', () => {
|
|||||||
{ provide: REQUEST, useValue: {} },
|
{ provide: REQUEST, useValue: {} },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
{ provide: RemoteDataBuildService, useValue: linkService },
|
||||||
CookieService,
|
CookieService,
|
||||||
AuthService
|
AuthService
|
||||||
]
|
]
|
||||||
@@ -178,7 +181,7 @@ describe('AuthService test', () => {
|
|||||||
(state as any).core = Object.create({});
|
(state as any).core = Object.create({});
|
||||||
(state as any).core.auth = authenticatedState;
|
(state as any).core.auth = authenticatedState;
|
||||||
});
|
});
|
||||||
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, rdbService);
|
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, linkService);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should return true when user is logged in', () => {
|
it('should return true when user is logged in', () => {
|
||||||
@@ -217,7 +220,7 @@ describe('AuthService test', () => {
|
|||||||
{ provide: REQUEST, useValue: {} },
|
{ provide: REQUEST, useValue: {} },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: RemoteDataBuildService, useValue: rdbService },
|
{ provide: RemoteDataBuildService, useValue: linkService },
|
||||||
ClientCookieService,
|
ClientCookieService,
|
||||||
CookieService,
|
CookieService,
|
||||||
AuthService
|
AuthService
|
||||||
@@ -240,7 +243,7 @@ describe('AuthService test', () => {
|
|||||||
(state as any).core = Object.create({});
|
(state as any).core = Object.create({});
|
||||||
(state as any).core.auth = authenticatedState;
|
(state as any).core.auth = authenticatedState;
|
||||||
});
|
});
|
||||||
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, rdbService);
|
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, linkService);
|
||||||
storage = (authService as any).storage;
|
storage = (authService as any).storage;
|
||||||
routeServiceMock = TestBed.get(RouteService);
|
routeServiceMock = TestBed.get(RouteService);
|
||||||
routerStub = TestBed.get(Router);
|
routerStub = TestBed.get(Router);
|
||||||
|
@@ -8,6 +8,8 @@ import { distinctUntilChanged, filter, map, startWith, switchMap, take, withLate
|
|||||||
import { RouterReducerState } from '@ngrx/router-store';
|
import { RouterReducerState } from '@ngrx/router-store';
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
import { CookieAttributes } from 'js-cookie';
|
import { CookieAttributes } from 'js-cookie';
|
||||||
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
|
import { LinkService } from '../cache/builders/link.service';
|
||||||
|
|
||||||
import { EPerson } from '../eperson/models/eperson.model';
|
import { EPerson } from '../eperson/models/eperson.model';
|
||||||
import { AuthRequestService } from './auth-request.service';
|
import { AuthRequestService } from './auth-request.service';
|
||||||
@@ -55,7 +57,7 @@ export class AuthService {
|
|||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected storage: CookieService,
|
protected storage: CookieService,
|
||||||
protected store: Store<AppState>,
|
protected store: Store<AppState>,
|
||||||
protected rdbService: RemoteDataBuildService
|
protected linkService: LinkService
|
||||||
) {
|
) {
|
||||||
this.store.pipe(
|
this.store.pipe(
|
||||||
select(isAuthenticated),
|
select(isAuthenticated),
|
||||||
@@ -154,7 +156,7 @@ export class AuthService {
|
|||||||
headers = headers.append('Authorization', `Bearer ${token.accessToken}`);
|
headers = headers.append('Authorization', `Bearer ${token.accessToken}`);
|
||||||
options.headers = headers;
|
options.headers = headers;
|
||||||
return this.authRequestService.getRequest('status', options).pipe(
|
return this.authRequestService.getRequest('status', options).pipe(
|
||||||
map((status) => this.rdbService.build(status)),
|
map((status) => this.linkService.resolveLinks(status, followLink<AuthStatus>('eperson'))),
|
||||||
switchMap((status: AuthStatus) => {
|
switchMap((status: AuthStatus) => {
|
||||||
if (status.authenticated) {
|
if (status.authenticated) {
|
||||||
return status.eperson.pipe(map((eperson) => eperson.payload));
|
return status.eperson.pipe(map((eperson) => eperson.payload));
|
||||||
|
@@ -1,57 +1,86 @@
|
|||||||
import { AuthError } from './auth-error.model';
|
import { autoserialize, deserialize, deserializeAs } from 'cerialize';
|
||||||
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 { Observable } from 'rxjs';
|
||||||
|
import { link, typedObject } from '../../cache/builders/build-decorators';
|
||||||
|
import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer';
|
||||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||||
|
import { RemoteData } from '../../data/remote-data';
|
||||||
|
import { EPerson } from '../../eperson/models/eperson.model';
|
||||||
|
import { EPERSON } from '../../eperson/models/eperson.resource-type';
|
||||||
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
import { excludeFromEquals } from '../../utilities/equals.decorators';
|
||||||
|
import { AuthError } from './auth-error.model';
|
||||||
|
import { AUTH_STATUS } from './auth-status.resource-type';
|
||||||
|
import { AuthTokenInfo } from './auth-token-info.model';
|
||||||
import { AuthMethod } from './auth.method';
|
import { AuthMethod } from './auth.method';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object that represents the authenticated status of a user
|
* Object that represents the authenticated status of a user
|
||||||
*/
|
*/
|
||||||
|
@typedObject
|
||||||
export class AuthStatus implements CacheableObject {
|
export class AuthStatus implements CacheableObject {
|
||||||
static type = new ResourceType('status');
|
static type = AUTH_STATUS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unique identifier of this auth status
|
* The unique identifier of this auth status
|
||||||
*/
|
*/
|
||||||
|
@autoserialize
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unique uuid of this auth status
|
* The type for this AuthStatus
|
||||||
*/
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The UUID of this auth status
|
||||||
|
* This UUID is generated client-side and isn't used by the backend.
|
||||||
|
* It is based on the ID, so it will be the same for each refresh.
|
||||||
|
*/
|
||||||
|
@deserializeAs(new IDToUUIDSerializer('auth-status'), 'id')
|
||||||
uuid: string;
|
uuid: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if REST API is up and running, should never return false
|
* True if REST API is up and running, should never return false
|
||||||
*/
|
*/
|
||||||
|
@autoserialize
|
||||||
okay: boolean;
|
okay: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the auth status represents an authenticated state
|
* If the auth status represents an authenticated state
|
||||||
*/
|
*/
|
||||||
|
@autoserialize
|
||||||
authenticated: boolean;
|
authenticated: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authentication error if there was one for this status
|
* The {@link HALLink}s for this AuthStatus
|
||||||
*/
|
*/
|
||||||
error?: AuthError;
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink;
|
||||||
|
eperson: HALLink;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The eperson of this auth status
|
* The EPerson of this auth status
|
||||||
|
* Will be undefined unless the eperson {@link HALLink} has been resolved.
|
||||||
*/
|
*/
|
||||||
eperson: Observable<RemoteData<EPerson>>;
|
@link(EPERSON)
|
||||||
|
eperson?: Observable<RemoteData<EPerson>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the token is valid, false if there was no token or the token wasn't valid
|
* True if the token is valid, false if there was no token or the token wasn't valid
|
||||||
*/
|
*/
|
||||||
|
@autoserialize
|
||||||
token?: AuthTokenInfo;
|
token?: AuthTokenInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The self link of this auth status' REST object
|
* Authentication error if there was one for this status
|
||||||
*/
|
*/
|
||||||
self: string;
|
// TODO should be refactored to use the RemoteData error
|
||||||
|
error?: AuthError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All authentication methods enabled at the backend
|
* All authentication methods enabled at the backend
|
||||||
|
9
src/app/core/auth/models/auth-status.resource-type.ts
Normal file
9
src/app/core/auth/models/auth-status.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for AuthStatus
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const AUTH_STATUS = new ResourceType('status');
|
@@ -1,48 +0,0 @@
|
|||||||
import { AuthStatus } from './auth-status.model';
|
|
||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
|
||||||
import { mapsTo, relationship } from '../../cache/builders/build-decorators';
|
|
||||||
import { NormalizedObject } from '../../cache/models/normalized-object.model';
|
|
||||||
import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer';
|
|
||||||
import { EPerson } from '../../eperson/models/eperson.model';
|
|
||||||
import { AuthMethod } from './auth.method';
|
|
||||||
|
|
||||||
@mapsTo(AuthStatus)
|
|
||||||
@inheritSerialization(NormalizedObject)
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if REST API is up and running, should never return false
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
okay: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the token is valid, false if there was no token or the token wasn't valid
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
authenticated: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The self link to the eperson of this auth status
|
|
||||||
*/
|
|
||||||
@relationship(EPerson, false)
|
|
||||||
@autoserialize
|
|
||||||
eperson: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All authentication methods enabled at the backend
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
authMethods: AuthMethod[];
|
|
||||||
}
|
|
||||||
|
@@ -4,13 +4,13 @@ import { HttpHeaders } from '@angular/common/http';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { filter, map, switchMap, take } from 'rxjs/operators';
|
import { filter, map, switchMap, take } from 'rxjs/operators';
|
||||||
|
|
||||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
|
||||||
import { AuthStatus } from './models/auth-status.model';
|
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { AuthService } from './auth.service';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { AuthTokenInfo } from './models/auth-token-info.model';
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
import { EPerson } from '../eperson/models/eperson.model';
|
import { EPerson } from '../eperson/models/eperson.model';
|
||||||
import { NormalizedAuthStatus } from './models/normalized-auth-status.model';
|
import { AuthService } from './auth.service';
|
||||||
|
import { AuthStatus } from './models/auth-status.model';
|
||||||
|
import { AuthTokenInfo } from './models/auth-token-info.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The auth service.
|
* The auth service.
|
||||||
@@ -35,7 +35,7 @@ export class ServerAuthService extends AuthService {
|
|||||||
|
|
||||||
options.headers = headers;
|
options.headers = headers;
|
||||||
return this.authRequestService.getRequest('status', options).pipe(
|
return this.authRequestService.getRequest('status', options).pipe(
|
||||||
map((status) => this.rdbService.build(status)),
|
map((status) => this.linkService.resolveLinks(status, followLink<AuthStatus>('eperson'))),
|
||||||
switchMap((status: AuthStatus) => {
|
switchMap((status: AuthStatus) => {
|
||||||
if (status.authenticated) {
|
if (status.authenticated) {
|
||||||
return status.eperson.pipe(map((eperson) => eperson.payload));
|
return status.eperson.pipe(map((eperson) => eperson.payload));
|
||||||
@@ -61,7 +61,7 @@ export class ServerAuthService extends AuthService {
|
|||||||
options.headers = headers;
|
options.headers = headers;
|
||||||
options.withCredentials = true;
|
options.withCredentials = true;
|
||||||
return this.authRequestService.getRequest('status', options).pipe(
|
return this.authRequestService.getRequest('status', options).pipe(
|
||||||
map((status: NormalizedAuthStatus) => Object.assign(new AuthStatus(), status))
|
map((status: AuthStatus) => Object.assign(new AuthStatus(), status))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { BrowseEndpointRequest, BrowseEntriesRequest, BrowseItemsRequest } from '../data/request.models';
|
import { BrowseEndpointRequest, BrowseEntriesRequest, BrowseItemsRequest } from '../data/request.models';
|
||||||
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
import { BrowseService } from './browse.service';
|
|
||||||
import { BrowseEntrySearchOptions } from './browse-entry-search-options.model';
|
import { BrowseEntrySearchOptions } from './browse-entry-search-options.model';
|
||||||
import { RequestEntry } from '../data/request.reducer';
|
import { BrowseService } from './browse.service';
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
|
|
||||||
describe('BrowseService', () => {
|
describe('BrowseService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
@@ -44,8 +44,8 @@ describe('BrowseService', () => {
|
|||||||
'dc.date.issued'
|
'dc.date.issued'
|
||||||
],
|
],
|
||||||
_links: {
|
_links: {
|
||||||
self: 'https://rest.api/discover/browses/dateissued',
|
self: { href: 'https://rest.api/discover/browses/dateissued' },
|
||||||
items: 'https://rest.api/discover/browses/dateissued/items'
|
items: { href: 'https://rest.api/discover/browses/dateissued/items' }
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Object.assign(new BrowseDefinition(), {
|
Object.assign(new BrowseDefinition(), {
|
||||||
@@ -72,9 +72,9 @@ describe('BrowseService', () => {
|
|||||||
'dc.creator'
|
'dc.creator'
|
||||||
],
|
],
|
||||||
_links: {
|
_links: {
|
||||||
self: 'https://rest.api/discover/browses/author',
|
self: { href: 'https://rest.api/discover/browses/author' },
|
||||||
entries: 'https://rest.api/discover/browses/author/entries',
|
entries: { href: 'https://rest.api/discover/browses/author/entries' },
|
||||||
items: 'https://rest.api/discover/browses/author/items'
|
items: { href: 'https://rest.api/discover/browses/author/items' }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@@ -125,9 +125,11 @@ describe('BrowseService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return a RemoteData object containing the correct BrowseDefinition[]', () => {
|
it('should return a RemoteData object containing the correct BrowseDefinition[]', () => {
|
||||||
const expected = cold('--a-', { a: {
|
const expected = cold('--a-', {
|
||||||
payload: browseDefinitions
|
a: {
|
||||||
}});
|
payload: browseDefinitions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
expect(service.getBrowseDefinitions()).toBeObservable(expected);
|
expect(service.getBrowseDefinitions()).toBeObservable(expected);
|
||||||
});
|
});
|
||||||
@@ -142,15 +144,17 @@ describe('BrowseService', () => {
|
|||||||
rdbService = getMockRemoteDataBuildService();
|
rdbService = getMockRemoteDataBuildService();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
spyOn(service, 'getBrowseDefinitions').and
|
spyOn(service, 'getBrowseDefinitions').and
|
||||||
.returnValue(hot('--a-', { a: {
|
.returnValue(hot('--a-', {
|
||||||
|
a: {
|
||||||
payload: browseDefinitions
|
payload: browseDefinitions
|
||||||
}}));
|
}
|
||||||
|
}));
|
||||||
spyOn(rdbService, 'toRemoteDataObservable').and.callThrough();
|
spyOn(rdbService, 'toRemoteDataObservable').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when getBrowseEntriesFor is called with a valid browse definition id', () => {
|
describe('when getBrowseEntriesFor is called with a valid browse definition id', () => {
|
||||||
it('should configure a new BrowseEntriesRequest', () => {
|
it('should configure a new BrowseEntriesRequest', () => {
|
||||||
const expected = new BrowseEntriesRequest(requestService.generateRequestId(), browseDefinitions[1]._links.entries);
|
const expected = new BrowseEntriesRequest(requestService.generateRequestId(), browseDefinitions[1]._links.entries.href);
|
||||||
|
|
||||||
scheduler.schedule(() => service.getBrowseEntriesFor(new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe());
|
scheduler.schedule(() => service.getBrowseEntriesFor(new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe());
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
@@ -169,7 +173,7 @@ describe('BrowseService', () => {
|
|||||||
|
|
||||||
describe('when getBrowseItemsFor is called with a valid browse definition id', () => {
|
describe('when getBrowseItemsFor is called with a valid browse definition id', () => {
|
||||||
it('should configure a new BrowseItemsRequest', () => {
|
it('should configure a new BrowseItemsRequest', () => {
|
||||||
const expected = new BrowseItemsRequest(requestService.generateRequestId(), browseDefinitions[1]._links.items + '?filterValue=' + mockAuthorName);
|
const expected = new BrowseItemsRequest(requestService.generateRequestId(), browseDefinitions[1]._links.items.href + '?filterValue=' + mockAuthorName);
|
||||||
|
|
||||||
scheduler.schedule(() => service.getBrowseItemsFor(mockAuthorName, new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe());
|
scheduler.schedule(() => service.getBrowseItemsFor(mockAuthorName, new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe());
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
@@ -215,9 +219,11 @@ describe('BrowseService', () => {
|
|||||||
rdbService = getMockRemoteDataBuildService();
|
rdbService = getMockRemoteDataBuildService();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
spyOn(service, 'getBrowseDefinitions').and
|
spyOn(service, 'getBrowseDefinitions').and
|
||||||
.returnValue(hot('--a-', { a: {
|
.returnValue(hot('--a-', {
|
||||||
|
a: {
|
||||||
payload: browseDefinitions
|
payload: browseDefinitions
|
||||||
}}));
|
}
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the URL for the given metadataKey and linkPath', () => {
|
it('should return the URL for the given metadataKey and linkPath', () => {
|
||||||
@@ -288,14 +294,16 @@ describe('BrowseService', () => {
|
|||||||
rdbService = getMockRemoteDataBuildService();
|
rdbService = getMockRemoteDataBuildService();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
spyOn(service, 'getBrowseDefinitions').and
|
spyOn(service, 'getBrowseDefinitions').and
|
||||||
.returnValue(hot('--a-', { a: {
|
.returnValue(hot('--a-', {
|
||||||
|
a: {
|
||||||
payload: browseDefinitions
|
payload: browseDefinitions
|
||||||
}}));
|
}
|
||||||
|
}));
|
||||||
spyOn(rdbService, 'toRemoteDataObservable').and.callThrough();
|
spyOn(rdbService, 'toRemoteDataObservable').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when getFirstItemFor is called with a valid browse definition id', () => {
|
describe('when getFirstItemFor is called with a valid browse definition id', () => {
|
||||||
const expectedURL = browseDefinitions[1]._links.items + '?page=0&size=1';
|
const expectedURL = browseDefinitions[1]._links.items.href + '?page=0&size=1';
|
||||||
|
|
||||||
it('should configure a new BrowseItemsRequest', () => {
|
it('should configure a new BrowseItemsRequest', () => {
|
||||||
const expected = new BrowseItemsRequest(requestService.generateRequestId(), expectedURL);
|
const expected = new BrowseItemsRequest(requestService.generateRequestId(), expectedURL);
|
||||||
|
@@ -10,18 +10,16 @@ import {
|
|||||||
isNotEmptyOperator
|
isNotEmptyOperator
|
||||||
} from '../../shared/empty.util';
|
} from '../../shared/empty.util';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { GenericSuccessResponse } from '../cache/response.models';
|
||||||
import { PaginatedList } from '../data/paginated-list';
|
import { PaginatedList } from '../data/paginated-list';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import {
|
import { BrowseEndpointRequest, BrowseEntriesRequest, BrowseItemsRequest, RestRequest } from '../data/request.models';
|
||||||
BrowseEndpointRequest,
|
|
||||||
BrowseEntriesRequest,
|
|
||||||
BrowseItemsRequest,
|
|
||||||
RestRequest
|
|
||||||
} from '../data/request.models';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
import { BrowseEntry } from '../shared/browse-entry.model';
|
import { BrowseEntry } from '../shared/browse-entry.model';
|
||||||
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { Item } from '../shared/item.model';
|
||||||
import {
|
import {
|
||||||
configureRequest,
|
configureRequest,
|
||||||
filterSuccessfulResponses,
|
filterSuccessfulResponses,
|
||||||
@@ -31,10 +29,7 @@ import {
|
|||||||
getRequestFromRequestHref
|
getRequestFromRequestHref
|
||||||
} from '../shared/operators';
|
} from '../shared/operators';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
import { Item } from '../shared/item.model';
|
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
|
||||||
import { BrowseEntrySearchOptions } from './browse-entry-search-options.model';
|
import { BrowseEntrySearchOptions } from './browse-entry-search-options.model';
|
||||||
import { GenericSuccessResponse } from '../cache/response.models';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service handling all browse requests
|
* The service handling all browse requests
|
||||||
@@ -81,10 +76,11 @@ export class BrowseService {
|
|||||||
map((response: GenericSuccessResponse<BrowseDefinition[]>) => response.payload),
|
map((response: GenericSuccessResponse<BrowseDefinition[]>) => response.payload),
|
||||||
ensureArrayHasValue(),
|
ensureArrayHasValue(),
|
||||||
map((definitions: BrowseDefinition[]) => definitions
|
map((definitions: BrowseDefinition[]) => definitions
|
||||||
.map((definition: BrowseDefinition) => Object.assign(new BrowseDefinition(), definition))),
|
.map((definition: BrowseDefinition) => {
|
||||||
distinctUntilChanged()
|
return Object.assign(new BrowseDefinition(), definition)
|
||||||
|
})),
|
||||||
|
distinctUntilChanged(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +92,10 @@ export class BrowseService {
|
|||||||
return this.getBrowseDefinitions().pipe(
|
return this.getBrowseDefinitions().pipe(
|
||||||
getBrowseDefinitionLinks(options.metadataDefinition),
|
getBrowseDefinitionLinks(options.metadataDefinition),
|
||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
map((_links: any) => _links.entries),
|
map((_links: any) => {
|
||||||
|
const entriesLink = _links.entries.href || _links.entries;
|
||||||
|
return entriesLink;
|
||||||
|
}),
|
||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
map((href: string) => {
|
map((href: string) => {
|
||||||
// TODO nearly identical to PaginatedSearchOptions => refactor
|
// TODO nearly identical to PaginatedSearchOptions => refactor
|
||||||
@@ -133,7 +132,10 @@ export class BrowseService {
|
|||||||
return this.getBrowseDefinitions().pipe(
|
return this.getBrowseDefinitions().pipe(
|
||||||
getBrowseDefinitionLinks(options.metadataDefinition),
|
getBrowseDefinitionLinks(options.metadataDefinition),
|
||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
map((_links: any) => _links.items),
|
map((_links: any) => {
|
||||||
|
const itemsLink = _links.items.href || _links.items;
|
||||||
|
return itemsLink;
|
||||||
|
}),
|
||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
map((href: string) => {
|
map((href: string) => {
|
||||||
const args = [];
|
const args = [];
|
||||||
@@ -171,7 +173,10 @@ export class BrowseService {
|
|||||||
return this.getBrowseDefinitions().pipe(
|
return this.getBrowseDefinitions().pipe(
|
||||||
getBrowseDefinitionLinks(definition),
|
getBrowseDefinitionLinks(definition),
|
||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
map((_links: any) => _links.items),
|
map((_links: any) => {
|
||||||
|
const itemsLink = _links.items.href || _links.items;
|
||||||
|
return itemsLink;
|
||||||
|
}),
|
||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
map((href: string) => {
|
map((href: string) => {
|
||||||
const args = [];
|
const args = [];
|
||||||
@@ -249,7 +254,7 @@ export class BrowseService {
|
|||||||
if (isEmpty(def) || isEmpty(def._links) || isEmpty(def._links[linkPath])) {
|
if (isEmpty(def) || isEmpty(def._links) || isEmpty(def._links[linkPath])) {
|
||||||
throw new Error(`A browse endpoint for ${linkPath} on ${metadataKey} isn't configured`);
|
throw new Error(`A browse endpoint for ${linkPath} on ${metadataKey} isn't configured`);
|
||||||
} else {
|
} else {
|
||||||
return def._links[linkPath];
|
return def._links[linkPath] || def._links[linkPath].href;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
startWith(undefined),
|
startWith(undefined),
|
||||||
|
83
src/app/core/cache/builders/build-decorators.spec.ts
vendored
Normal file
83
src/app/core/cache/builders/build-decorators.spec.ts
vendored
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
import {
|
||||||
|
dataService,
|
||||||
|
getDataServiceFor,
|
||||||
|
getLinkDefinition,
|
||||||
|
link,
|
||||||
|
} from './build-decorators';
|
||||||
|
|
||||||
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
class TestService {}
|
||||||
|
class AnotherTestService {}
|
||||||
|
class TestHALResource implements HALResource {
|
||||||
|
_links: {
|
||||||
|
self: HALLink;
|
||||||
|
foo: HALLink;
|
||||||
|
};
|
||||||
|
|
||||||
|
bar?: any
|
||||||
|
}
|
||||||
|
let testType;
|
||||||
|
|
||||||
|
describe('build decorators', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testType = new ResourceType('testType-' + new Date().getTime());
|
||||||
|
});
|
||||||
|
describe('@dataService/getDataServiceFor', () => {
|
||||||
|
|
||||||
|
it('should register a resourcetype for a dataservice', () => {
|
||||||
|
dataService(testType)(TestService);
|
||||||
|
expect(getDataServiceFor(testType)).toBe(TestService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`when the resource type isn't specified`, () => {
|
||||||
|
it(`should throw an error`, () => {
|
||||||
|
expect(() => {
|
||||||
|
dataService(undefined)(TestService);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`when there already is a registered dataservice for a resourcetype`, () => {
|
||||||
|
it(`should throw an error`, () => {
|
||||||
|
dataService(testType)(TestService);
|
||||||
|
expect(() => {
|
||||||
|
dataService(testType)(AnotherTestService);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`@link/getLinkDefinitions`, () => {
|
||||||
|
it(`should register a link`, () => {
|
||||||
|
const target = new TestHALResource();
|
||||||
|
link(testType, true, 'foo')(target, 'bar');
|
||||||
|
const result = getLinkDefinition(TestHALResource, 'foo');
|
||||||
|
expect(result.resourceType).toBe(testType);
|
||||||
|
expect(result.isList).toBe(true);
|
||||||
|
expect(result.linkName).toBe('foo');
|
||||||
|
expect(result.propertyName).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`when the linkname isn't specified`, () => {
|
||||||
|
it(`should use the propertyname`, () => {
|
||||||
|
const target = new TestHALResource();
|
||||||
|
link(testType)(target, 'foo');
|
||||||
|
const result = getLinkDefinition(TestHALResource, 'foo');
|
||||||
|
expect(result.linkName).toBe('foo');
|
||||||
|
expect(result.propertyName).toBe('foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`when there's no @link`, () => {
|
||||||
|
it(`should return undefined`, () => {
|
||||||
|
const result = getLinkDefinition(TestHALResource, 'self');
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
/* tslint:enable:max-classes-per-file */
|
181
src/app/core/cache/builders/build-decorators.ts
vendored
181
src/app/core/cache/builders/build-decorators.ts
vendored
@@ -1,80 +1,161 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
import { hasNoValue, hasValue } from '../../../shared/empty.util';
|
||||||
|
import { DataService } from '../../data/data.service';
|
||||||
|
|
||||||
import { GenericConstructor } from '../../shared/generic-constructor';
|
import { GenericConstructor } from '../../shared/generic-constructor';
|
||||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
||||||
|
|
||||||
const mapsToMetadataKey = Symbol('mapsTo');
|
const resolvedLinkKey = Symbol('resolvedLink');
|
||||||
const relationshipKey = Symbol('relationship');
|
|
||||||
|
|
||||||
const relationshipMap = new Map();
|
const resolvedLinkMap = new Map();
|
||||||
const typeMap = new Map();
|
const typeMap = new Map();
|
||||||
|
const dataServiceMap = new Map();
|
||||||
|
const linkMap = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorator function to map a normalized class to it's not-normalized counter part class
|
* Decorator function to map a ResourceType to its class
|
||||||
* It will also maps a type to the matching class
|
* @param target The contructor of the typed class to map
|
||||||
* @param value The not-normalized class to map to
|
|
||||||
*/
|
*/
|
||||||
export function mapsTo(value: GenericConstructor<TypedObject>) {
|
export function typedObject(target: typeof TypedObject) {
|
||||||
return function decorator(objectConstructor: GenericConstructor<TypedObject>) {
|
typeMap.set(target.type.value, target);
|
||||||
Reflect.defineMetadata(mapsToMetadataKey, value, objectConstructor);
|
|
||||||
mapsToType((value as any).type, objectConstructor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a type to the matching class
|
|
||||||
* @param value The resourse type
|
|
||||||
* @param objectConstructor The class to map to
|
|
||||||
*/
|
|
||||||
function mapsToType(value: ResourceType, objectConstructor: GenericConstructor<TypedObject>) {
|
|
||||||
if (!objectConstructor || !value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
typeMap.set(value.value, objectConstructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the mapped class for the given normalized class
|
|
||||||
* @param target The normalized class
|
|
||||||
*/
|
|
||||||
export function getMapsTo(target: any) {
|
|
||||||
return Reflect.getOwnMetadata(mapsToMetadataKey, target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mapped class for the given type
|
* Returns the mapped class for the given type
|
||||||
* @param type The resource type
|
* @param type The resource type
|
||||||
*/
|
*/
|
||||||
export function getMapsToType(type: string | ResourceType) {
|
export function getClassForType(type: string | ResourceType) {
|
||||||
if (typeof(type) === 'object') {
|
if (typeof(type) === 'object') {
|
||||||
type = (type as ResourceType).value;
|
type = (type as ResourceType).value;
|
||||||
}
|
}
|
||||||
return typeMap.get(type);
|
return typeMap.get(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function relationship<T extends CacheableObject>(value: GenericConstructor<T>, isList: boolean = false): any {
|
/**
|
||||||
return function r(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
* A class decorator to indicate that this class is a dataservice
|
||||||
if (!target || !propertyKey) {
|
* for a given resource type.
|
||||||
return;
|
*
|
||||||
|
* "dataservice" in this context means that it has findByHref and
|
||||||
|
* findAllByHref methods.
|
||||||
|
*
|
||||||
|
* @param resourceType the resource type the class is a dataservice for
|
||||||
|
*/
|
||||||
|
export function dataService(resourceType: ResourceType): any {
|
||||||
|
return (target: any) => {
|
||||||
|
if (hasNoValue(resourceType)) {
|
||||||
|
throw new Error(`Invalid @dataService annotation on ${target}, resourceType needs to be defined`);
|
||||||
|
}
|
||||||
|
const existingDataservice = dataServiceMap.get(resourceType.value);
|
||||||
|
|
||||||
|
if (hasValue(existingDataservice)) {
|
||||||
|
throw new Error(`Multiple dataservices for ${resourceType.value}: ${existingDataservice} and ${target}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const metaDataList: string[] = relationshipMap.get(target.constructor) || [];
|
dataServiceMap.set(resourceType.value, target);
|
||||||
if (metaDataList.indexOf(propertyKey) === -1) {
|
|
||||||
metaDataList.push(propertyKey);
|
|
||||||
}
|
|
||||||
relationshipMap.set(target.constructor, metaDataList);
|
|
||||||
return Reflect.metadata(relationshipKey, {
|
|
||||||
resourceType: (value as any).type.value,
|
|
||||||
isList
|
|
||||||
}).apply(this, arguments);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRelationMetadata(target: any, propertyKey: string) {
|
/**
|
||||||
return Reflect.getMetadata(relationshipKey, target, propertyKey);
|
* Return the dataservice matching the given resource type
|
||||||
|
*
|
||||||
|
* @param resourceType the resource type you want the matching dataservice for
|
||||||
|
*/
|
||||||
|
export function getDataServiceFor<T extends CacheableObject>(resourceType: ResourceType) {
|
||||||
|
return dataServiceMap.get(resourceType.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRelationships(target: any) {
|
/**
|
||||||
return relationshipMap.get(target);
|
* A class to represent the data that can be set by the @link decorator
|
||||||
|
*/
|
||||||
|
export class LinkDefinition<T extends HALResource> {
|
||||||
|
resourceType: ResourceType;
|
||||||
|
isList = false;
|
||||||
|
linkName: keyof T['_links'];
|
||||||
|
propertyName: keyof T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A property decorator to indicate that a certain property is the placeholder
|
||||||
|
* where the contents of a resolved link should be stored.
|
||||||
|
*
|
||||||
|
* e.g. if an Item has an hal link for bundles, and an item.bundles property
|
||||||
|
* this decorator should decorate that item.bundles property.
|
||||||
|
*
|
||||||
|
* @param resourceType the resource type of the object(s) the link retrieves
|
||||||
|
* @param isList an optional boolean indicating whether or not it concerns a list,
|
||||||
|
* defaults to false
|
||||||
|
* @param linkName an optional string in case the {@link HALLink} name differs from the
|
||||||
|
* property name
|
||||||
|
*/
|
||||||
|
export const link = <T extends HALResource>(
|
||||||
|
resourceType: ResourceType,
|
||||||
|
isList = false,
|
||||||
|
linkName?: keyof T['_links'],
|
||||||
|
) => {
|
||||||
|
return (target: T, propertyName: string) => {
|
||||||
|
let targetMap = linkMap.get(target.constructor);
|
||||||
|
|
||||||
|
if (hasNoValue(targetMap)) {
|
||||||
|
targetMap = new Map<keyof T['_links'],LinkDefinition<T>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNoValue(linkName)) {
|
||||||
|
linkName = propertyName as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetMap.set(linkName, {
|
||||||
|
resourceType,
|
||||||
|
isList,
|
||||||
|
linkName,
|
||||||
|
propertyName
|
||||||
|
});
|
||||||
|
|
||||||
|
linkMap.set(target.constructor, targetMap);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all LinkDefinitions for a model class
|
||||||
|
* @param source
|
||||||
|
*/
|
||||||
|
export const getLinkDefinitions = <T extends HALResource>(source: GenericConstructor<T>): Map<keyof T['_links'], LinkDefinition<T>> => {
|
||||||
|
return linkMap.get(source);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a specific LinkDefinition for a model class
|
||||||
|
*
|
||||||
|
* @param source the model class
|
||||||
|
* @param linkName the name of the link
|
||||||
|
*/
|
||||||
|
export const getLinkDefinition = <T extends HALResource>(source: GenericConstructor<T>, linkName: keyof T['_links']): LinkDefinition<T> => {
|
||||||
|
const sourceMap = linkMap.get(source);
|
||||||
|
if (hasValue(sourceMap)) {
|
||||||
|
return sourceMap.get(linkName);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class level decorator to indicate you want to inherit @link annotations
|
||||||
|
* from a parent class.
|
||||||
|
*
|
||||||
|
* @param parent the parent class to inherit @link annotations from
|
||||||
|
*/
|
||||||
|
export function inheritLinkAnnotations(parent: any): any {
|
||||||
|
return (child: any) => {
|
||||||
|
const parentMap: Map<string, LinkDefinition<any>> = linkMap.get(parent) || new Map();
|
||||||
|
const childMap: Map<string, LinkDefinition<any>> = linkMap.get(child) || new Map();
|
||||||
|
|
||||||
|
parentMap.forEach((value, key) => {
|
||||||
|
if (!childMap.has(key)) {
|
||||||
|
childMap.set(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
linkMap.set(child, childMap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
222
src/app/core/cache/builders/link.service.spec.ts
vendored
Normal file
222
src/app/core/cache/builders/link.service.spec.ts
vendored
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||||
|
import { FindListOptions } from '../../data/request.models';
|
||||||
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
import * as decorators from './build-decorators';
|
||||||
|
import { getDataServiceFor } from './build-decorators';
|
||||||
|
import { LinkService } from './link.service';
|
||||||
|
|
||||||
|
const spyOnFunction = <T>(obj: T, func: keyof T) => {
|
||||||
|
const spy = jasmine.createSpy(func as string);
|
||||||
|
spyOnProperty(obj, func, 'get').and.returnValue(spy);
|
||||||
|
|
||||||
|
return spy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEST_MODEL = new ResourceType('testmodel');
|
||||||
|
let result: any;
|
||||||
|
|
||||||
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
class TestModel implements HALResource {
|
||||||
|
static type = TEST_MODEL;
|
||||||
|
|
||||||
|
type = TEST_MODEL;
|
||||||
|
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
_links: {
|
||||||
|
self: HALLink;
|
||||||
|
predecessor: HALLink;
|
||||||
|
successor: HALLink;
|
||||||
|
};
|
||||||
|
|
||||||
|
predecessor?: TestModel;
|
||||||
|
successor?: TestModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
class TestDataService {
|
||||||
|
findAllByHref(href: string, findListOptions: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<any>>) {
|
||||||
|
return 'findAllByHref'
|
||||||
|
}
|
||||||
|
findByHref(href: string, ...linksToFollow: Array<FollowLinkConfig<any>>) {
|
||||||
|
return 'findByHref'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let testDataService: TestDataService;
|
||||||
|
|
||||||
|
let testModel: TestModel;
|
||||||
|
|
||||||
|
describe('LinkService', () => {
|
||||||
|
let service: LinkService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testModel = Object.assign(new TestModel(), {
|
||||||
|
value: 'a test value',
|
||||||
|
_links: {
|
||||||
|
self: {
|
||||||
|
href: 'http://self.link'
|
||||||
|
},
|
||||||
|
predecessor: {
|
||||||
|
href: 'http://predecessor.link'
|
||||||
|
},
|
||||||
|
successor: {
|
||||||
|
href: 'http://successor.link'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testDataService = new TestDataService();
|
||||||
|
spyOn(testDataService, 'findAllByHref').and.callThrough();
|
||||||
|
spyOn(testDataService, 'findByHref').and.callThrough();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [LinkService, {
|
||||||
|
provide: TestDataService,
|
||||||
|
useValue: testDataService
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
service = TestBed.get(LinkService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resolveLink', () => {
|
||||||
|
describe(`when the linkdefinition concerns a single object`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({
|
||||||
|
resourceType: TEST_MODEL,
|
||||||
|
linkName: 'predecessor',
|
||||||
|
propertyName: 'predecessor'
|
||||||
|
});
|
||||||
|
spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService);
|
||||||
|
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')))
|
||||||
|
});
|
||||||
|
it('should call dataservice.findByHref with the correct href and nested links', () => {
|
||||||
|
expect(testDataService.findByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, followLink('successor'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe(`when the linkdefinition concerns a list`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({
|
||||||
|
resourceType: TEST_MODEL,
|
||||||
|
linkName: 'predecessor',
|
||||||
|
propertyName: 'predecessor',
|
||||||
|
isList: true
|
||||||
|
});
|
||||||
|
spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService);
|
||||||
|
service.resolveLink(testModel, followLink('predecessor', { some: 'options '} as any, followLink('successor')))
|
||||||
|
});
|
||||||
|
it('should call dataservice.findAllByHref with the correct href, findListOptions, and nested links', () => {
|
||||||
|
expect(testDataService.findAllByHref).toHaveBeenCalledWith(testModel._links.predecessor.href, { some: 'options '} as any, followLink('successor'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('either way', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({
|
||||||
|
resourceType: TEST_MODEL,
|
||||||
|
linkName: 'predecessor',
|
||||||
|
propertyName: 'predecessor'
|
||||||
|
});
|
||||||
|
spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService);
|
||||||
|
result = service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')))
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call getLinkDefinition with the correct model and link', () => {
|
||||||
|
expect(decorators.getLinkDefinition).toHaveBeenCalledWith(testModel.constructor as any, 'predecessor');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call getDataServiceFor with the correct resource type', () => {
|
||||||
|
expect(decorators.getDataServiceFor).toHaveBeenCalledWith(TEST_MODEL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the model with the resolved link', () => {
|
||||||
|
expect(result.type).toBe(TEST_MODEL);
|
||||||
|
expect(result.value).toBe('a test value');
|
||||||
|
expect(result._links.self.href).toBe('http://self.link');
|
||||||
|
expect(result.predecessor).toBe('findByHref');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`when the specified link doesn't exist on the model's class`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOnFunction(decorators, 'getLinkDefinition').and.returnValue(undefined);
|
||||||
|
});
|
||||||
|
it('should throw an error', () => {
|
||||||
|
expect(() => {
|
||||||
|
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')))
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`when there is no dataservice for the resourcetype in the link`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({
|
||||||
|
resourceType: TEST_MODEL,
|
||||||
|
linkName: 'predecessor',
|
||||||
|
propertyName: 'predecessor'
|
||||||
|
});
|
||||||
|
spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(undefined);
|
||||||
|
});
|
||||||
|
it('should throw an error', () => {
|
||||||
|
expect(() => {
|
||||||
|
service.resolveLink(testModel, followLink('predecessor', {}, followLink('successor')))
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resolveLinks', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(service, 'resolveLink');
|
||||||
|
service.resolveLinks(testModel, followLink('predecessor'), followLink('successor'))
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call resolveLink with the model for each of the provided links', () => {
|
||||||
|
expect(service.resolveLink).toHaveBeenCalledWith(testModel, followLink('predecessor'));
|
||||||
|
expect(service.resolveLink).toHaveBeenCalledWith(testModel, followLink('successor'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the model', () => {
|
||||||
|
expect(result.type).toBe(TEST_MODEL);
|
||||||
|
expect(result.value).toBe('a test value');
|
||||||
|
expect(result._links.self.href).toBe('http://self.link');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeResolvedLinks', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testModel.predecessor = 'predecessor value' as any;
|
||||||
|
testModel.successor = 'successor value' as any;
|
||||||
|
spyOnFunction(decorators, 'getLinkDefinitions').and.returnValue([
|
||||||
|
{
|
||||||
|
resourceType: TEST_MODEL,
|
||||||
|
linkName: 'predecessor',
|
||||||
|
propertyName: 'predecessor',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resourceType: TEST_MODEL,
|
||||||
|
linkName: 'successor',
|
||||||
|
propertyName: 'successor',
|
||||||
|
}
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a new version of the object without any resolved links', () => {
|
||||||
|
result = service.removeResolvedLinks(testModel);
|
||||||
|
expect(result.value).toBe(testModel.value);
|
||||||
|
expect(result.type).toBe(testModel.type);
|
||||||
|
expect(result._links).toBe(testModel._links);
|
||||||
|
expect(result.predecessor).toBeUndefined();
|
||||||
|
expect(result.successor).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should leave the original object untouched', () => {
|
||||||
|
service.removeResolvedLinks(testModel);
|
||||||
|
expect(testModel.predecessor as any).toBe('predecessor value');
|
||||||
|
expect(testModel.successor as any).toBe('successor value');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
/* tslint:enable:max-classes-per-file */
|
90
src/app/core/cache/builders/link.service.ts
vendored
Normal file
90
src/app/core/cache/builders/link.service.ts
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { Injectable, Injector } from '@angular/core';
|
||||||
|
import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||||
|
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||||
|
import { GenericConstructor } from '../../shared/generic-constructor';
|
||||||
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
|
import { getDataServiceFor, getLinkDefinition, getLinkDefinitions, LinkDefinition } from './build-decorators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Service to handle the resolving and removing
|
||||||
|
* of resolved {@link HALLink}s on HALResources
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class LinkService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected parentInjector: Injector,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the given {@link FollowLinkConfig}s for the given model
|
||||||
|
*
|
||||||
|
* @param model the {@link HALResource} to resolve the links for
|
||||||
|
* @param linksToFollow the {@link FollowLinkConfig}s to resolve
|
||||||
|
*/
|
||||||
|
public resolveLinks<T extends HALResource>(model: T, ...linksToFollow: Array<FollowLinkConfig<T>>): T {
|
||||||
|
linksToFollow.forEach((linkToFollow: FollowLinkConfig<T>) => {
|
||||||
|
this.resolveLink(model, linkToFollow);
|
||||||
|
});
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the given {@link FollowLinkConfig} for the given model
|
||||||
|
*
|
||||||
|
* @param model the {@link HALResource} to resolve the link for
|
||||||
|
* @param linkToFollow the {@link FollowLinkConfig} to resolve
|
||||||
|
*/
|
||||||
|
public resolveLink<T extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): T {
|
||||||
|
const matchingLinkDef = getLinkDefinition(model.constructor, linkToFollow.name);
|
||||||
|
|
||||||
|
if (hasNoValue(matchingLinkDef)) {
|
||||||
|
throw new Error(`followLink('${linkToFollow.name}') was used for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`);
|
||||||
|
} else {
|
||||||
|
const provider = getDataServiceFor(matchingLinkDef.resourceType);
|
||||||
|
|
||||||
|
if (hasNoValue(provider)) {
|
||||||
|
throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models uses the resource type ${matchingLinkDef.resourceType.value.toUpperCase()}, but there is no service with an @dataService(${matchingLinkDef.resourceType.value.toUpperCase()}) annotation in order to retrieve it`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = Injector.create({
|
||||||
|
providers: [],
|
||||||
|
parent: this.parentInjector
|
||||||
|
}).get(provider);
|
||||||
|
|
||||||
|
const href = model._links[matchingLinkDef.linkName].href;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (matchingLinkDef.isList) {
|
||||||
|
model[linkToFollow.name] = service.findAllByHref(href, linkToFollow.findListOptions, ...linkToFollow.linksToFollow);
|
||||||
|
} else {
|
||||||
|
model[linkToFollow.name] = service.findByHref(href, ...linkToFollow.linksToFollow);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} from ${href}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any resolved links that the model may have.
|
||||||
|
*
|
||||||
|
* @param model the {@link HALResource} to remove the links from
|
||||||
|
* @returns a copy of the given model, without resolved links.
|
||||||
|
*/
|
||||||
|
public removeResolvedLinks<T extends HALResource>(model: T): T {
|
||||||
|
const result = Object.assign(new (model.constructor as GenericConstructor<T>)(), model);
|
||||||
|
const linkDefs = getLinkDefinitions(model.constructor as GenericConstructor<T>);
|
||||||
|
if (isNotEmpty(linkDefs)) {
|
||||||
|
linkDefs.forEach((linkDef: LinkDefinition<T>) => {
|
||||||
|
result[linkDef.propertyName] = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,48 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { NormalizedObject } from '../models/normalized-object.model';
|
|
||||||
import { getMapsToType, getRelationships } from './build-decorators';
|
|
||||||
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
|
||||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if halObj has a value for `_links.self`
|
|
||||||
*
|
|
||||||
* @param {any} halObj The object to test
|
|
||||||
*/
|
|
||||||
export function isRestDataObject(halObj: any): boolean {
|
|
||||||
return isNotEmpty(halObj._links) && hasValue(halObj._links.self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if halObj has a value for `page` and `_embedded`
|
|
||||||
*
|
|
||||||
* @param {any} halObj The object to test
|
|
||||||
*/
|
|
||||||
export function isRestPaginatedList(halObj: any): boolean {
|
|
||||||
return hasValue(halObj.page) && hasValue(halObj._embedded);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A service to turn domain models in to their normalized
|
|
||||||
* counterparts.
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class NormalizedObjectBuildService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the normalized model that corresponds to the given domain model
|
|
||||||
*
|
|
||||||
* @param {TDomain} domainModel a domain model
|
|
||||||
*/
|
|
||||||
normalize<T extends CacheableObject>(domainModel: T): NormalizedObject<T> {
|
|
||||||
const normalizedConstructor = getMapsToType((domainModel as any).type);
|
|
||||||
const relationships = getRelationships(normalizedConstructor) || [];
|
|
||||||
const normalizedModel = Object.assign({}, domainModel) as any;
|
|
||||||
relationships.forEach((key: string) => {
|
|
||||||
if (hasValue(normalizedModel[key])) {
|
|
||||||
normalizedModel[key] = normalizedModel._links[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return normalizedModel;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +1,10 @@
|
|||||||
import { RemoteDataBuildService } from './remote-data-build.service';
|
|
||||||
import { Item } from '../../shared/item.model';
|
|
||||||
import { PaginatedList } from '../../data/paginated-list';
|
|
||||||
import { PageInfo } from '../../shared/page-info.model';
|
|
||||||
import { RemoteData } from '../../data/remote-data';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { createSuccessfulRemoteDataObject } from '../../../shared/testing/utils';
|
import { createSuccessfulRemoteDataObject } from '../../../shared/testing/utils';
|
||||||
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
|
import { RemoteData } from '../../data/remote-data';
|
||||||
|
import { Item } from '../../shared/item.model';
|
||||||
|
import { PageInfo } from '../../shared/page-info.model';
|
||||||
|
import { RemoteDataBuildService } from './remote-data-build.service';
|
||||||
|
|
||||||
const pageInfo = new PageInfo();
|
const pageInfo = new PageInfo();
|
||||||
const array = [
|
const array = [
|
||||||
@@ -37,7 +37,7 @@ describe('RemoteDataBuildService', () => {
|
|||||||
let service: RemoteDataBuildService;
|
let service: RemoteDataBuildService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = new RemoteDataBuildService(undefined, undefined);
|
service = new RemoteDataBuildService(undefined, undefined, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when toPaginatedList is called', () => {
|
describe('when toPaginatedList is called', () => {
|
||||||
|
@@ -1,36 +1,46 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf, race as observableRace } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable, of as observableOf, race as observableRace } from 'rxjs';
|
||||||
import { distinctUntilChanged, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
|
||||||
|
import {
|
||||||
import { hasValue, hasValueOperator, isEmpty, isNotEmpty, isNotUndefined } from '../../../shared/empty.util';
|
hasValue,
|
||||||
|
hasValueOperator,
|
||||||
|
isEmpty,
|
||||||
|
isNotEmpty,
|
||||||
|
isNotUndefined
|
||||||
|
} from '../../../shared/empty.util';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
||||||
|
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||||
import { PaginatedList } from '../../data/paginated-list';
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
import { RemoteDataError } from '../../data/remote-data-error';
|
import { RemoteDataError } from '../../data/remote-data-error';
|
||||||
import { GetRequest } from '../../data/request.models';
|
|
||||||
import { RequestEntry } from '../../data/request.reducer';
|
import { RequestEntry } from '../../data/request.reducer';
|
||||||
import { RequestService } from '../../data/request.service';
|
import { RequestService } from '../../data/request.service';
|
||||||
import { NormalizedObject } from '../models/normalized-object.model';
|
|
||||||
import { ObjectCacheService } from '../object-cache.service';
|
|
||||||
import { DSOSuccessResponse, ErrorResponse } from '../response.models';
|
|
||||||
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
|
|
||||||
import { PageInfo } from '../../shared/page-info.model';
|
|
||||||
import {
|
import {
|
||||||
filterSuccessfulResponses,
|
filterSuccessfulResponses,
|
||||||
getRequestFromRequestHref,
|
getRequestFromRequestHref,
|
||||||
getRequestFromRequestUUID,
|
getRequestFromRequestUUID,
|
||||||
getResourceLinksFromResponse
|
getResourceLinksFromResponse
|
||||||
} from '../../shared/operators';
|
} from '../../shared/operators';
|
||||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
import { PageInfo } from '../../shared/page-info.model';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
|
import { CacheableObject } from '../object-cache.reducer';
|
||||||
|
import { ObjectCacheService } from '../object-cache.service';
|
||||||
|
import { DSOSuccessResponse, ErrorResponse } from '../response.models';
|
||||||
|
import { LinkService } from './link.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RemoteDataBuildService {
|
export class RemoteDataBuildService {
|
||||||
constructor(protected objectCache: ObjectCacheService,
|
constructor(protected objectCache: ObjectCacheService,
|
||||||
|
protected linkService: LinkService,
|
||||||
protected requestService: RequestService) {
|
protected requestService: RequestService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildSingle<T extends CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<T>> {
|
/**
|
||||||
|
* Creates a single {@link RemoteData} object based on the response of a request to the REST server, with a list of
|
||||||
|
* {@link FollowLinkConfig} that indicate which embedded info should be added to the object
|
||||||
|
* @param href$ Observable href of object we want to retrieve
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
|
*/
|
||||||
|
buildSingle<T extends CacheableObject>(href$: string | Observable<string>, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<T>> {
|
||||||
if (typeof href$ === 'string') {
|
if (typeof href$ === 'string') {
|
||||||
href$ = observableOf(href$);
|
href$ = observableOf(href$);
|
||||||
}
|
}
|
||||||
@@ -70,9 +80,9 @@ export class RemoteDataBuildService {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
map((normalized: NormalizedObject<T>) => {
|
map((obj: T) =>
|
||||||
return this.build<T>(normalized);
|
this.linkService.resolveLinks(obj, ...linksToFollow)
|
||||||
}),
|
),
|
||||||
startWith(undefined),
|
startWith(undefined),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
@@ -108,7 +118,13 @@ export class RemoteDataBuildService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildList<T extends CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<PaginatedList<T>>> {
|
/**
|
||||||
|
* Creates a list of {@link RemoteData} objects based on the response of a request to the REST server, with a list of
|
||||||
|
* {@link FollowLinkConfig} that indicate which embedded info should be added to the objects
|
||||||
|
* @param href$ Observable href of objects we want to retrieve
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
|
*/
|
||||||
|
buildList<T extends CacheableObject>(href$: string | Observable<string>, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
if (typeof href$ === 'string') {
|
if (typeof href$ === 'string') {
|
||||||
href$ = observableOf(href$);
|
href$ = observableOf(href$);
|
||||||
}
|
}
|
||||||
@@ -118,10 +134,10 @@ export class RemoteDataBuildService {
|
|||||||
getResourceLinksFromResponse(),
|
getResourceLinksFromResponse(),
|
||||||
switchMap((resourceUUIDs: string[]) => {
|
switchMap((resourceUUIDs: string[]) => {
|
||||||
return this.objectCache.getList(resourceUUIDs).pipe(
|
return this.objectCache.getList(resourceUUIDs).pipe(
|
||||||
map((normList: Array<NormalizedObject<T>>) => {
|
map((objs: T[]) => {
|
||||||
return normList.map((normalized: NormalizedObject<T>) => {
|
return objs.map((obj: T) =>
|
||||||
return this.build<T>(normalized);
|
this.linkService.resolveLinks(obj, ...linksToFollow)
|
||||||
});
|
);
|
||||||
}));
|
}));
|
||||||
}),
|
}),
|
||||||
startWith([]),
|
startWith([]),
|
||||||
@@ -150,54 +166,6 @@ export class RemoteDataBuildService {
|
|||||||
return this.toRemoteDataObservable(requestEntry$, payload$);
|
return this.toRemoteDataObservable(requestEntry$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
build<T extends CacheableObject>(normalized: NormalizedObject<T>): T {
|
|
||||||
const links: any = {};
|
|
||||||
const relationships = getRelationships(normalized.constructor) || [];
|
|
||||||
|
|
||||||
relationships.forEach((relationship: string) => {
|
|
||||||
let result;
|
|
||||||
if (hasValue(normalized[relationship])) {
|
|
||||||
const { resourceType, isList } = getRelationMetadata(normalized, relationship);
|
|
||||||
const objectList = normalized[relationship].page || normalized[relationship];
|
|
||||||
if (typeof objectList !== 'string') {
|
|
||||||
objectList.forEach((href: string) => {
|
|
||||||
this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), href))
|
|
||||||
});
|
|
||||||
|
|
||||||
const rdArr = [];
|
|
||||||
objectList.forEach((href: string) => {
|
|
||||||
rdArr.push(this.buildSingle(href));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isList) {
|
|
||||||
result = this.aggregate(rdArr);
|
|
||||||
} else if (rdArr.length === 1) {
|
|
||||||
result = rdArr[0];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), objectList));
|
|
||||||
|
|
||||||
// The rest API can return a single URL to represent a list of resources (e.g. /items/:id/bitstreams)
|
|
||||||
// in that case only 1 href will be stored in the normalized obj (so the isArray above fails),
|
|
||||||
// but it should still be built as a list
|
|
||||||
if (isList) {
|
|
||||||
result = this.buildList(objectList);
|
|
||||||
} else {
|
|
||||||
result = this.buildSingle(objectList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasValue(normalized[relationship].page)) {
|
|
||||||
links[relationship] = this.toPaginatedList(result, normalized[relationship].pageInfo);
|
|
||||||
} else {
|
|
||||||
links[relationship] = result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const domainModel = getMapsTo(normalized.constructor);
|
|
||||||
return Object.assign(new domainModel(), normalized, links);
|
|
||||||
}
|
|
||||||
|
|
||||||
aggregate<T>(input: Array<Observable<RemoteData<T>>>): Observable<RemoteData<T[]>> {
|
aggregate<T>(input: Array<Observable<RemoteData<T>>>): Observable<RemoteData<T[]>> {
|
||||||
|
|
||||||
if (isEmpty(input)) {
|
if (isEmpty(input)) {
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
|
||||||
import { ItemType } from '../../../shared/item-relationships/item-type.model';
|
|
||||||
import { mapsTo } from '../../builders/build-decorators';
|
|
||||||
import { NormalizedObject } from '../normalized-object.model';
|
|
||||||
import { IDToUUIDSerializer } from '../../id-to-uuid-serializer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a DSpace ItemType
|
|
||||||
*/
|
|
||||||
@mapsTo(ItemType)
|
|
||||||
@inheritSerialization(NormalizedObject)
|
|
||||||
export class NormalizedItemType extends NormalizedObject<ItemType> {
|
|
||||||
/**
|
|
||||||
* The label that describes the ResourceType of the Item
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
label: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The identifier of this ItemType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The universally unique identifier of this ItemType
|
|
||||||
*/
|
|
||||||
@autoserializeAs(new IDToUUIDSerializer(ItemType.type.value), 'id')
|
|
||||||
uuid: string;
|
|
||||||
}
|
|
@@ -1,77 +0,0 @@
|
|||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
|
||||||
import { RelationshipType } from '../../../shared/item-relationships/relationship-type.model';
|
|
||||||
import { ResourceType } from '../../../shared/resource-type';
|
|
||||||
import { mapsTo, relationship } from '../../builders/build-decorators';
|
|
||||||
import { NormalizedDSpaceObject } from '../normalized-dspace-object.model';
|
|
||||||
import { NormalizedObject } from '../normalized-object.model';
|
|
||||||
import { IDToUUIDSerializer } from '../../id-to-uuid-serializer';
|
|
||||||
import { ItemType } from '../../../shared/item-relationships/item-type.model';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a DSpace RelationshipType
|
|
||||||
*/
|
|
||||||
@mapsTo(RelationshipType)
|
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
|
||||||
export class NormalizedRelationshipType extends NormalizedObject<RelationshipType> {
|
|
||||||
/**
|
|
||||||
* The identifier of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The label that describes the Relation to the left of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
leftwardType: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum amount of Relationships allowed to the left of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
leftMaxCardinality: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The minimum amount of Relationships allowed to the left of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
leftMinCardinality: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The label that describes the Relation to the right of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
rightwardType: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum amount of Relationships allowed to the right of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
rightMaxCardinality: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The minimum amount of Relationships allowed to the right of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
rightMinCardinality: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of Item found to the left of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(ItemType, false)
|
|
||||||
leftType: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of Item found to the right of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(ItemType, false)
|
|
||||||
rightType: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The universally unique identifier of this RelationshipType
|
|
||||||
*/
|
|
||||||
@autoserializeAs(new IDToUUIDSerializer(RelationshipType.type.value), 'id')
|
|
||||||
uuid: string;
|
|
||||||
}
|
|
@@ -1,72 +0,0 @@
|
|||||||
import { autoserialize, deserialize, deserializeAs, inheritSerialization } from 'cerialize';
|
|
||||||
import { Relationship } from '../../../shared/item-relationships/relationship.model';
|
|
||||||
import { mapsTo, relationship } from '../../builders/build-decorators';
|
|
||||||
import { NormalizedObject } from '../normalized-object.model';
|
|
||||||
import { IDToUUIDSerializer } from '../../id-to-uuid-serializer';
|
|
||||||
import { RelationshipType } from '../../../shared/item-relationships/relationship-type.model';
|
|
||||||
import { Item } from '../../../shared/item.model';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a DSpace Relationship
|
|
||||||
*/
|
|
||||||
@mapsTo(Relationship)
|
|
||||||
@inheritSerialization(NormalizedObject)
|
|
||||||
export class NormalizedRelationship extends NormalizedObject<Relationship> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The identifier of this Relationship
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The item to the left of this relationship
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Item, false)
|
|
||||||
leftItem: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The item to the right of this relationship
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Item, false)
|
|
||||||
rightItem: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The place of the Item to the left side of this Relationship
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
leftPlace: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The place of the Item to the right side of this Relationship
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
rightPlace: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name variant of the Item to the left side of this Relationship
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
leftwardValue: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name variant of the Item to the right side of this Relationship
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
rightwardValue: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of Relationship
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(RelationshipType, false)
|
|
||||||
relationshipType: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The universally unique identifier of this Relationship
|
|
||||||
*/
|
|
||||||
@deserializeAs(new IDToUUIDSerializer(Relationship.type.value), 'id')
|
|
||||||
uuid: string;
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
|
||||||
import { BitstreamFormat } from '../../shared/bitstream-format.model';
|
|
||||||
|
|
||||||
import { mapsTo } from '../builders/build-decorators';
|
|
||||||
import { IDToUUIDSerializer } from '../id-to-uuid-serializer';
|
|
||||||
import { NormalizedObject } from './normalized-object.model';
|
|
||||||
import { BitstreamFormatSupportLevel } from '../../shared/bitstream-format-support-level';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a Bitstream Format
|
|
||||||
*/
|
|
||||||
@mapsTo(BitstreamFormat)
|
|
||||||
@inheritSerialization(NormalizedObject)
|
|
||||||
export class NormalizedBitstreamFormat extends NormalizedObject<BitstreamFormat> {
|
|
||||||
/**
|
|
||||||
* Short description of this Bitstream Format
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
shortDescription: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Description of this Bitstream Format
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
description: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* String representing the MIME type of this Bitstream Format
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
mimetype: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The level of support the system offers for this Bitstream Format
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
supportLevel: BitstreamFormatSupportLevel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the Bitstream Format is used to store system information, rather than the content of items in the system
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
internal: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* String representing this Bitstream Format's file extension
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
extensions: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Identifier for this Bitstream Format
|
|
||||||
* Note that this ID is unique for bitstream formats,
|
|
||||||
* but might not be unique across different object types
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Universally unique identifier for this Bitstream Format
|
|
||||||
* Consist of a prefix and the id field to ensure the identifier is unique across all object types
|
|
||||||
*/
|
|
||||||
@autoserializeAs(new IDToUUIDSerializer('bitstream-format'), 'id')
|
|
||||||
uuid: string;
|
|
||||||
}
|
|
@@ -1,60 +0,0 @@
|
|||||||
import { autoserialize, inheritSerialization } from 'cerialize';
|
|
||||||
|
|
||||||
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
|
|
||||||
import { Bitstream } from '../../shared/bitstream.model';
|
|
||||||
import { mapsTo, relationship } from '../builders/build-decorators';
|
|
||||||
import { Item } from '../../shared/item.model';
|
|
||||||
import { BitstreamFormat } from '../../shared/bitstream-format.model';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a DSpace Bitstream
|
|
||||||
*/
|
|
||||||
@mapsTo(Bitstream)
|
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
|
||||||
export class NormalizedBitstream extends NormalizedDSpaceObject<Bitstream> {
|
|
||||||
/**
|
|
||||||
* The size of this bitstream in bytes
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
sizeBytes: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The relative path to this Bitstream's file
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
content: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The format of this Bitstream
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(BitstreamFormat, false)
|
|
||||||
format: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The description of this Bitstream
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
description: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of Bundles that are direct parents of this Bitstream
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(Item, true)
|
|
||||||
parents: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Bundle that owns this Bitstream
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(Item, false)
|
|
||||||
owner: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the Bundle this Bitstream is part of
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
bundleName: string;
|
|
||||||
|
|
||||||
}
|
|
@@ -1,45 +0,0 @@
|
|||||||
import { autoserialize, inheritSerialization } from 'cerialize';
|
|
||||||
|
|
||||||
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
|
|
||||||
import { Bundle } from '../../shared/bundle.model';
|
|
||||||
import { mapsTo, relationship } from '../builders/build-decorators';
|
|
||||||
import { Bitstream } from '../../shared/bitstream.model';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a DSpace Bundle
|
|
||||||
*/
|
|
||||||
@mapsTo(Bundle)
|
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
|
||||||
export class NormalizedBundle extends NormalizedDSpaceObject<Bundle> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The bundle's name
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The primary bitstream of this Bundle
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(Bitstream, false)
|
|
||||||
primaryBitstream: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of Items that are direct parents of this Bundle
|
|
||||||
*/
|
|
||||||
parents: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Item that owns this Bundle
|
|
||||||
*/
|
|
||||||
owner: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of Bitstreams that are part of this Bundle
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(Bitstream, true)
|
|
||||||
bitstreams: string[];
|
|
||||||
|
|
||||||
}
|
|
@@ -1,71 +0,0 @@
|
|||||||
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
|
||||||
|
|
||||||
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
|
|
||||||
import { Collection } from '../../shared/collection.model';
|
|
||||||
import { mapsTo, relationship } from '../builders/build-decorators';
|
|
||||||
import { NormalizedResourcePolicy } from './normalized-resource-policy.model';
|
|
||||||
import { NormalizedBitstream } from './normalized-bitstream.model';
|
|
||||||
import { NormalizedCommunity } from './normalized-community.model';
|
|
||||||
import { NormalizedItem } from './normalized-item.model';
|
|
||||||
import { License } from '../../shared/license.model';
|
|
||||||
import { ResourcePolicy } from '../../shared/resource-policy.model';
|
|
||||||
import { Bitstream } from '../../shared/bitstream.model';
|
|
||||||
import { Community } from '../../shared/community.model';
|
|
||||||
import { Item } from '../../shared/item.model';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a DSpace Collection
|
|
||||||
*/
|
|
||||||
@mapsTo(Collection)
|
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
|
||||||
export class NormalizedCollection extends NormalizedDSpaceObject<Collection> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A string representing the unique handle of this Collection
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
handle: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Bitstream that represents the license of this Collection
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(License, false)
|
|
||||||
license: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Bitstream that represents the default Access Conditions of this Collection
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
@relationship(ResourcePolicy, false)
|
|
||||||
defaultAccessConditions: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Bitstream that represents the logo of this Collection
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Bitstream, false)
|
|
||||||
logo: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of Communities that are direct parents of this Collection
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Community, true)
|
|
||||||
parents: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Community that owns this Collection
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Community, false)
|
|
||||||
owner: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of Items that are part of (not necessarily owned by) this Collection
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Item, true)
|
|
||||||
items: string[];
|
|
||||||
|
|
||||||
}
|
|
@@ -1,56 +0,0 @@
|
|||||||
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
|
||||||
|
|
||||||
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
|
|
||||||
import { Community } from '../../shared/community.model';
|
|
||||||
import { mapsTo, relationship } from '../builders/build-decorators';
|
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
|
||||||
import { NormalizedBitstream } from './normalized-bitstream.model';
|
|
||||||
import { NormalizedCollection } from './normalized-collection.model';
|
|
||||||
import { Bitstream } from '../../shared/bitstream.model';
|
|
||||||
import { Collection } from '../../shared/collection.model';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for a DSpace Community
|
|
||||||
*/
|
|
||||||
@mapsTo(Community)
|
|
||||||
@inheritSerialization(NormalizedDSpaceObject)
|
|
||||||
export class NormalizedCommunity extends NormalizedDSpaceObject<Community> {
|
|
||||||
/**
|
|
||||||
* A string representing the unique handle of this Community
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
handle: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Bitstream that represents the logo of this Community
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Bitstream, false)
|
|
||||||
logo: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of Communities that are direct parents of this Community
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Community, true)
|
|
||||||
parents: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Community that owns this Community
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Community, false)
|
|
||||||
owner: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of Collections that are owned by this Community
|
|
||||||
*/
|
|
||||||
@deserialize
|
|
||||||
@relationship(Collection, true)
|
|
||||||
collections: string[];
|
|
||||||
|
|
||||||
@deserialize
|
|
||||||
@relationship(Community, true)
|
|
||||||
subcommunities: string[];
|
|
||||||
|
|
||||||
}
|
|
@@ -1,72 +0,0 @@
|
|||||||
import { autoserializeAs, deserializeAs, autoserialize } from 'cerialize';
|
|
||||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
|
||||||
import { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models';
|
|
||||||
import { mapsTo } from '../builders/build-decorators';
|
|
||||||
import { NormalizedObject } from './normalized-object.model';
|
|
||||||
import { TypedObject } from '../object-cache.reducer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An model class for a DSpaceObject.
|
|
||||||
*/
|
|
||||||
@mapsTo(DSpaceObject)
|
|
||||||
export class NormalizedDSpaceObject<T extends DSpaceObject> extends NormalizedObject<T> implements TypedObject {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The link to the rest endpoint where this object can be found
|
|
||||||
*
|
|
||||||
* Repeated here to make the serialization work,
|
|
||||||
* inheritSerialization doesn't seem to work for more than one level
|
|
||||||
*/
|
|
||||||
@deserializeAs(String)
|
|
||||||
self: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The human-readable identifier of this DSpaceObject
|
|
||||||
*
|
|
||||||
* Currently mapped to uuid but left in to leave room
|
|
||||||
* for a shorter, more user friendly type of id
|
|
||||||
*/
|
|
||||||
@autoserializeAs(String, 'uuid')
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The universally unique identifier of this DSpaceObject
|
|
||||||
*/
|
|
||||||
@autoserializeAs(String)
|
|
||||||
uuid: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A string representing the kind of DSpaceObject, e.g. community, item, …
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All metadata of this DSpaceObject
|
|
||||||
*/
|
|
||||||
@autoserializeAs(MetadataMapSerializer)
|
|
||||||
metadata: MetadataMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
|
||||||
*/
|
|
||||||
@deserializeAs(String)
|
|
||||||
parents: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The DSpaceObject that owns this DSpaceObject
|
|
||||||
*/
|
|
||||||
@deserializeAs(String)
|
|
||||||
owner: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The links to all related resources returned by the rest api.
|
|
||||||
*
|
|
||||||
* Repeated here to make the serialization work,
|
|
||||||
* inheritSerialization doesn't seem to work for more than one level
|
|
||||||
*/
|
|
||||||
@deserializeAs(Object)
|
|
||||||
_links: {
|
|
||||||
[name: string]: string
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +0,0 @@
|
|||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
|
||||||
import { NormalizedObject } from './normalized-object.model';
|
|
||||||
import { ExternalSourceEntry } from '../../shared/external-source-entry.model';
|
|
||||||
import { mapsTo } from '../builders/build-decorators';
|
|
||||||
import { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalized model class for an external source entry
|
|
||||||
*/
|
|
||||||
@mapsTo(ExternalSourceEntry)
|
|
||||||
@inheritSerialization(NormalizedObject)
|
|
||||||
export class NormalizedExternalSourceEntry extends NormalizedObject<ExternalSourceEntry> {
|
|
||||||
/**
|
|
||||||
* Unique identifier
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value to display
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
display: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value to store the entry with
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
value: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ID of the external source this entry originates from
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
externalSource: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Metadata of the entry
|
|
||||||
*/
|
|
||||||
@autoserializeAs(MetadataMapSerializer)
|
|
||||||
metadata: MetadataMap;
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user