mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-13 21:13:07 +00:00
Merge remote-tracking branch 'upstream/main' into w2p-74199_Admin-search-dialogs
This commit is contained in:
@@ -17,10 +17,9 @@ coverage:
|
|||||||
# Configuration for patch-level checks. This checks the relative coverage of the new PR code ONLY.
|
# Configuration for patch-level checks. This checks the relative coverage of the new PR code ONLY.
|
||||||
patch:
|
patch:
|
||||||
default:
|
default:
|
||||||
# For each PR, make sure the coverage of the new code is within 1% of current overall coverage.
|
# Enable informational mode, which just provides info to reviewers & always passes
|
||||||
# We let 'patch' be more lenient as we only require *project* coverage to not drop significantly.
|
# https://docs.codecov.io/docs/commit-status#section-informational
|
||||||
target: auto
|
informational: true
|
||||||
threshold: 1%
|
|
||||||
|
|
||||||
# Turn PR comments "off". This feature adds the code coverage summary as a
|
# Turn PR comments "off". This feature adds the code coverage summary as a
|
||||||
# comment on each PR. See https://docs.codecov.io/docs/pull-request-comments
|
# comment on each PR. See https://docs.codecov.io/docs/pull-request-comments
|
||||||
|
@@ -12,3 +12,6 @@ trim_trailing_whitespace = true
|
|||||||
[*.md]
|
[*.md]
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
quote_type = single
|
||||||
|
@@ -88,6 +88,7 @@
|
|||||||
"debug-loader": "^0.0.1",
|
"debug-loader": "^0.0.1",
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
"express": "4.16.2",
|
"express": "4.16.2",
|
||||||
|
"express-rate-limit": "^5.1.3",
|
||||||
"fast-json-patch": "^2.0.7",
|
"fast-json-patch": "^2.0.7",
|
||||||
"file-saver": "^1.3.8",
|
"file-saver": "^1.3.8",
|
||||||
"filesize": "^6.1.0",
|
"filesize": "^6.1.0",
|
||||||
|
@@ -54,13 +54,6 @@ import(environmentFilePath)
|
|||||||
function generateEnvironmentFile(file: GlobalConfig): void {
|
function generateEnvironmentFile(file: GlobalConfig): void {
|
||||||
file.production = production;
|
file.production = production;
|
||||||
buildBaseUrls(file);
|
buildBaseUrls(file);
|
||||||
|
|
||||||
// TODO remove workaround in beta 5
|
|
||||||
if (file.rest.nameSpace.match("(.*)/api/?$") !== null) {
|
|
||||||
file.rest.nameSpace = getNameSpace(file.rest.nameSpace);
|
|
||||||
console.log(colors.white.bgMagenta.bold(`The rest.nameSpace property in your environment file or in your DSPACE_REST_NAMESPACE environment variable ends with '/api'.\nThis is deprecated. As '/api' isn't configurable on the rest side, it shouldn't be repeated in every environment file.\nPlease change the rest nameSpace to '${file.rest.nameSpace}'`));
|
|
||||||
}
|
|
||||||
|
|
||||||
const contents = `export const environment = ` + JSON.stringify(file);
|
const contents = `export const environment = ` + JSON.stringify(file);
|
||||||
writeFile(targetPath, contents, (err) => {
|
writeFile(targetPath, contents, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -119,16 +112,5 @@ function getPort(port: number): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNameSpace(nameSpace: string): string {
|
function getNameSpace(nameSpace: string): string {
|
||||||
// TODO remove workaround in beta 5
|
return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : '';
|
||||||
const apiMatches = nameSpace.match("(.*)/api/?$");
|
|
||||||
if (apiMatches != null) {
|
|
||||||
let newValue = '/'
|
|
||||||
if (hasValue(apiMatches[1])) {
|
|
||||||
newValue = apiMatches[1];
|
|
||||||
}
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
21
server.ts
21
server.ts
@@ -28,12 +28,13 @@ import * as compression from 'compression';
|
|||||||
import * as cookieParser from 'cookie-parser';
|
import * as cookieParser from 'cookie-parser';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { enableProdMode, NgModuleFactory, Type } from '@angular/core';
|
import { enableProdMode } from '@angular/core';
|
||||||
|
|
||||||
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
|
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
|
||||||
import { environment } from './src/environments/environment';
|
import { environment } from './src/environments/environment';
|
||||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
import { createProxyMiddleware } from 'http-proxy-middleware';
|
||||||
import { hasValue, hasNoValue } from './src/app/shared/empty.util';
|
import { hasNoValue, hasValue } from './src/app/shared/empty.util';
|
||||||
|
import { UIServerConfig } from './src/config/ui-server-config.interface';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set path for the browser application's dist folder
|
* Set path for the browser application's dist folder
|
||||||
@@ -121,6 +122,19 @@ function cacheControl(req, res, next) {
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the rateLimiter property is present
|
||||||
|
* When it is present, the rateLimiter will be enabled. When it is undefined, the rateLimiter will be disabled.
|
||||||
|
*/
|
||||||
|
if (hasValue((environment.ui as UIServerConfig).rateLimiter)) {
|
||||||
|
const RateLimit = require('express-rate-limit');
|
||||||
|
const limiter = new RateLimit({
|
||||||
|
windowMs: (environment.ui as UIServerConfig).rateLimiter.windowMs,
|
||||||
|
max: (environment.ui as UIServerConfig).rateLimiter.max
|
||||||
|
});
|
||||||
|
app.use(limiter);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Serve static resources (images, i18n messages, …)
|
* Serve static resources (images, i18n messages, …)
|
||||||
*/
|
*/
|
||||||
@@ -209,8 +223,9 @@ if (environment.ui.ssl) {
|
|||||||
certificate: certificate
|
certificate: certificate
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
console.warn('Disabling certificate validation and proceeding with a self-signed certificate. If this is a production server, it is recommended that you configure a valid certificate instead.');
|
||||||
|
|
||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // lgtm[js/disabling-certificate-validation]
|
||||||
|
|
||||||
pem.createCertificate({
|
pem.createCertificate({
|
||||||
days: 1,
|
days: 1,
|
||||||
|
@@ -13,6 +13,7 @@ import { Collection } from '../../../../../core/shared/collection.model';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { getCollectionEditRoute } from '../../../../../+collection-page/collection-page-routing-paths';
|
import { getCollectionEditRoute } from '../../../../../+collection-page/collection-page-routing-paths';
|
||||||
|
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||||
|
|
||||||
describe('CollectionAdminSearchResultGridElementComponent', () => {
|
describe('CollectionAdminSearchResultGridElementComponent', () => {
|
||||||
let component: CollectionAdminSearchResultGridElementComponent;
|
let component: CollectionAdminSearchResultGridElementComponent;
|
||||||
@@ -26,6 +27,11 @@ describe('CollectionAdminSearchResultGridElementComponent', () => {
|
|||||||
searchResult.indexableObject = new Collection();
|
searchResult.indexableObject = new Collection();
|
||||||
searchResult.indexableObject.uuid = id;
|
searchResult.indexableObject.uuid = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const linkService = jasmine.createSpyObj('linkService', {
|
||||||
|
resolveLink: {}
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
init();
|
init();
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -39,6 +45,7 @@ describe('CollectionAdminSearchResultGridElementComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: TruncatableService, useValue: mockTruncatableService },
|
{ provide: TruncatableService, useValue: mockTruncatableService },
|
||||||
{ provide: BitstreamDataService, useValue: {} },
|
{ provide: BitstreamDataService, useValue: {} },
|
||||||
|
{ provide: LinkService, useValue: linkService}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
@@ -14,8 +14,8 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||||||
import { CommunityAdminSearchResultGridElementComponent } from './community-admin-search-result-grid-element.component';
|
import { CommunityAdminSearchResultGridElementComponent } from './community-admin-search-result-grid-element.component';
|
||||||
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
import { CommunitySearchResult } from '../../../../../shared/object-collection/shared/community-search-result.model';
|
||||||
import { Community } from '../../../../../core/shared/community.model';
|
import { Community } from '../../../../../core/shared/community.model';
|
||||||
import { CommunityAdminSearchResultListElementComponent } from '../../admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component';
|
|
||||||
import { getCommunityEditRoute } from '../../../../../+community-page/community-page-routing-paths';
|
import { getCommunityEditRoute } from '../../../../../+community-page/community-page-routing-paths';
|
||||||
|
import { LinkService } from '../../../../../core/cache/builders/link.service';
|
||||||
|
|
||||||
describe('CommunityAdminSearchResultGridElementComponent', () => {
|
describe('CommunityAdminSearchResultGridElementComponent', () => {
|
||||||
let component: CommunityAdminSearchResultGridElementComponent;
|
let component: CommunityAdminSearchResultGridElementComponent;
|
||||||
@@ -29,6 +29,11 @@ describe('CommunityAdminSearchResultGridElementComponent', () => {
|
|||||||
searchResult.indexableObject = new Community();
|
searchResult.indexableObject = new Community();
|
||||||
searchResult.indexableObject.uuid = id;
|
searchResult.indexableObject.uuid = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const linkService = jasmine.createSpyObj('linkService', {
|
||||||
|
resolveLink: {}
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
init();
|
init();
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -42,6 +47,7 @@ describe('CommunityAdminSearchResultGridElementComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: TruncatableService, useValue: mockTruncatableService },
|
{ provide: TruncatableService, useValue: mockTruncatableService },
|
||||||
{ provide: BitstreamDataService, useValue: {} },
|
{ provide: BitstreamDataService, useValue: {} },
|
||||||
|
{ provide: LinkService, useValue: linkService}
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
})
|
})
|
||||||
|
@@ -12,6 +12,7 @@ import { CommunityDataService } from '../../core/data/community-data.service';
|
|||||||
import { CreateCollectionPageComponent } from './create-collection-page.component';
|
import { CreateCollectionPageComponent } from './create-collection-page.component';
|
||||||
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 {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
describe('CreateCollectionPageComponent', () => {
|
describe('CreateCollectionPageComponent', () => {
|
||||||
let comp: CreateCollectionPageComponent;
|
let comp: CreateCollectionPageComponent;
|
||||||
@@ -29,7 +30,8 @@ describe('CreateCollectionPageComponent', () => {
|
|||||||
},
|
},
|
||||||
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
||||||
{ provide: Router, useValue: {} },
|
{ provide: Router, useValue: {} },
|
||||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: RequestService, useValue: {}}
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -7,6 +7,7 @@ import { Collection } from '../../core/shared/collection.model';
|
|||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can create a new Collection
|
* Component that represents the page where a user can create a new Collection
|
||||||
@@ -26,8 +27,9 @@ export class CreateCollectionPageComponent extends CreateComColPageComponent<Col
|
|||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected notificationsService: NotificationsService,
|
protected notificationsService: NotificationsService,
|
||||||
protected translate: TranslateService
|
protected translate: TranslateService,
|
||||||
|
protected requestService: RequestService
|
||||||
) {
|
) {
|
||||||
super(collectionDataService, communityDataService, routeService, router, notificationsService, translate);
|
super(collectionDataService, communityDataService, routeService, router, notificationsService, translate, requestService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import { of as observableOf } from 'rxjs';
|
|||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { DeleteCollectionPageComponent } from './delete-collection-page.component';
|
import { DeleteCollectionPageComponent } from './delete-collection-page.component';
|
||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||||
|
import {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
describe('DeleteCollectionPageComponent', () => {
|
describe('DeleteCollectionPageComponent', () => {
|
||||||
let comp: DeleteCollectionPageComponent;
|
let comp: DeleteCollectionPageComponent;
|
||||||
@@ -22,6 +23,7 @@ describe('DeleteCollectionPageComponent', () => {
|
|||||||
{ provide: CollectionDataService, useValue: {} },
|
{ provide: CollectionDataService, useValue: {} },
|
||||||
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
||||||
{ provide: NotificationsService, useValue: {} },
|
{ provide: NotificationsService, useValue: {} },
|
||||||
|
{ provide: RequestService, useValue: {} }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -5,6 +5,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
|
|||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||||
import { Collection } from '../../core/shared/collection.model';
|
import { Collection } from '../../core/shared/collection.model';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can delete an existing Collection
|
* Component that represents the page where a user can delete an existing Collection
|
||||||
@@ -22,8 +23,9 @@ export class DeleteCollectionPageComponent extends DeleteComColPageComponent<Col
|
|||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
protected notifications: NotificationsService,
|
protected notifications: NotificationsService,
|
||||||
protected translate: TranslateService
|
protected translate: TranslateService,
|
||||||
|
protected requestService: RequestService
|
||||||
) {
|
) {
|
||||||
super(dsoDataService, router, route, notifications, translate);
|
super(dsoDataService, router, route, notifications, translate, requestService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ import { CommunityDataService } from '../../core/data/community-data.service';
|
|||||||
import { CreateCommunityPageComponent } from './create-community-page.component';
|
import { CreateCommunityPageComponent } from './create-community-page.component';
|
||||||
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 {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
describe('CreateCommunityPageComponent', () => {
|
describe('CreateCommunityPageComponent', () => {
|
||||||
let comp: CreateCommunityPageComponent;
|
let comp: CreateCommunityPageComponent;
|
||||||
@@ -25,7 +26,8 @@ describe('CreateCommunityPageComponent', () => {
|
|||||||
{ provide: CommunityDataService, useValue: { findById: () => observableOf({}) } },
|
{ provide: CommunityDataService, useValue: { findById: () => observableOf({}) } },
|
||||||
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
{ provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
|
||||||
{ provide: Router, useValue: {} },
|
{ provide: Router, useValue: {} },
|
||||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: RequestService, useValue: {} }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -6,6 +6,7 @@ import { Router } from '@angular/router';
|
|||||||
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can create a new Community
|
* Component that represents the page where a user can create a new Community
|
||||||
@@ -24,8 +25,9 @@ export class CreateCommunityPageComponent extends CreateComColPageComponent<Comm
|
|||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected notificationsService: NotificationsService,
|
protected notificationsService: NotificationsService,
|
||||||
protected translate: TranslateService
|
protected translate: TranslateService,
|
||||||
|
protected requestService: RequestService
|
||||||
) {
|
) {
|
||||||
super(communityDataService, communityDataService, routeService, router, notificationsService, translate);
|
super(communityDataService, communityDataService, routeService, router, notificationsService, translate, requestService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import { CommunityDataService } from '../../core/data/community-data.service';
|
|||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
import { DeleteCommunityPageComponent } from './delete-community-page.component';
|
import { DeleteCommunityPageComponent } from './delete-community-page.component';
|
||||||
|
import {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
describe('DeleteCommunityPageComponent', () => {
|
describe('DeleteCommunityPageComponent', () => {
|
||||||
let comp: DeleteCommunityPageComponent;
|
let comp: DeleteCommunityPageComponent;
|
||||||
@@ -22,6 +23,7 @@ describe('DeleteCommunityPageComponent', () => {
|
|||||||
{ provide: CommunityDataService, useValue: {} },
|
{ provide: CommunityDataService, useValue: {} },
|
||||||
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
|
||||||
{ provide: NotificationsService, useValue: {} },
|
{ provide: NotificationsService, useValue: {} },
|
||||||
|
{ provide: RequestService, useValue: {}}
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -5,6 +5,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { DeleteComColPageComponent } from '../../shared/comcol-forms/delete-comcol-page/delete-comcol-page.component';
|
import { DeleteComColPageComponent } from '../../shared/comcol-forms/delete-comcol-page/delete-comcol-page.component';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import {RequestService} from '../../core/data/request.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that represents the page where a user can delete an existing Community
|
* Component that represents the page where a user can delete an existing Community
|
||||||
@@ -22,8 +23,10 @@ export class DeleteCommunityPageComponent extends DeleteComColPageComponent<Comm
|
|||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
protected notifications: NotificationsService,
|
protected notifications: NotificationsService,
|
||||||
protected translate: TranslateService
|
protected translate: TranslateService,
|
||||||
|
protected requestService: RequestService
|
||||||
) {
|
) {
|
||||||
super(dsoDataService, router, route, notifications, translate);
|
super(dsoDataService, router, route, notifications, translate, requestService);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,6 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
import { PaginatedList } from '../../core/data/paginated-list';
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
@@ -8,7 +8,6 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
import { MetadataService } from '../../core/metadata/metadata.service';
|
import { MetadataService } from '../../core/metadata/metadata.service';
|
||||||
import { VarDirective } from '../../shared/utils/var.directive';
|
import { VarDirective } from '../../shared/utils/var.directive';
|
||||||
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 { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import { NgZone } from '@angular/core';
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
import { FindListOptions } from '../core/data/request.models';
|
import { FindListOptions } from '../core/data/request.models';
|
||||||
|
import { hasValue } from '../shared/empty.util';
|
||||||
import { CommunityListService, FlatNode } from './community-list-service';
|
import { CommunityListService, FlatNode } from './community-list-service';
|
||||||
import { CollectionViewer, DataSource } from '@angular/cdk/typings/collections';
|
import { CollectionViewer, DataSource } from '@angular/cdk/typings/collections';
|
||||||
import { BehaviorSubject, Observable, } from 'rxjs';
|
import { BehaviorSubject, Observable, } from 'rxjs';
|
||||||
import { finalize, take, } from 'rxjs/operators';
|
import { finalize } from 'rxjs/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DataSource object needed by a CDK Tree to render its nodes.
|
* DataSource object needed by a CDK Tree to render its nodes.
|
||||||
@@ -15,9 +16,9 @@ export class CommunityListDatasource implements DataSource<FlatNode> {
|
|||||||
|
|
||||||
private communityList$ = new BehaviorSubject<FlatNode[]>([]);
|
private communityList$ = new BehaviorSubject<FlatNode[]>([]);
|
||||||
public loading$ = new BehaviorSubject<boolean>(false);
|
public loading$ = new BehaviorSubject<boolean>(false);
|
||||||
|
private subLoadCommunities: Subscription;
|
||||||
|
|
||||||
constructor(private communityListService: CommunityListService,
|
constructor(private communityListService: CommunityListService) {
|
||||||
private zone: NgZone) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
|
connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
|
||||||
@@ -26,13 +27,13 @@ export class CommunityListDatasource implements DataSource<FlatNode> {
|
|||||||
|
|
||||||
loadCommunities(findOptions: FindListOptions, expandedNodes: FlatNode[]) {
|
loadCommunities(findOptions: FindListOptions, expandedNodes: FlatNode[]) {
|
||||||
this.loading$.next(true);
|
this.loading$.next(true);
|
||||||
this.zone.runOutsideAngular(() => {
|
if (hasValue(this.subLoadCommunities)) {
|
||||||
this.communityListService.loadCommunities(findOptions, expandedNodes).pipe(
|
this.subLoadCommunities.unsubscribe();
|
||||||
take(1),
|
}
|
||||||
finalize(() => this.zone.run(() => this.loading$.next(false))),
|
this.subLoadCommunities = this.communityListService.loadCommunities(findOptions, expandedNodes).pipe(
|
||||||
).subscribe((flatNodes: FlatNode[]) => {
|
finalize(() => this.loading$.next(false)),
|
||||||
this.zone.run(() => this.communityList$.next(flatNodes));
|
).subscribe((flatNodes: FlatNode[]) => {
|
||||||
});
|
this.communityList$.next(flatNodes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,13 +2,12 @@ import { Injectable } from '@angular/core';
|
|||||||
import { createSelector, Store } from '@ngrx/store';
|
import { createSelector, Store } from '@ngrx/store';
|
||||||
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
import { AppState } from '../app.reducer';
|
import { AppState } from '../app.reducer';
|
||||||
import { CommunityDataService } from '../core/data/community-data.service';
|
import { CommunityDataService } from '../core/data/community-data.service';
|
||||||
import { FindListOptions } from '../core/data/request.models';
|
import { FindListOptions } from '../core/data/request.models';
|
||||||
import { map, flatMap } from 'rxjs/operators';
|
|
||||||
import { Community } from '../core/shared/community.model';
|
import { Community } from '../core/shared/community.model';
|
||||||
import { Collection } from '../core/shared/collection.model';
|
import { Collection } from '../core/shared/collection.model';
|
||||||
import { getSucceededRemoteData } from '../core/shared/operators';
|
|
||||||
import { PageInfo } from '../core/shared/page-info.model';
|
import { PageInfo } from '../core/shared/page-info.model';
|
||||||
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
@@ -148,7 +147,7 @@ export class CommunityListService {
|
|||||||
return new PaginatedList(newPageInfo, newPage);
|
return new PaginatedList(newPageInfo, newPage);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return topComs$.pipe(flatMap((topComs: PaginatedList<Community>) => this.transformListOfCommunities(topComs, 0, null, expandedNodes)));
|
return topComs$.pipe(switchMap((topComs: PaginatedList<Community>) => this.transformListOfCommunities(topComs, 0, null, expandedNodes)));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -228,9 +227,13 @@ export class CommunityListService {
|
|||||||
currentPage: i
|
currentPage: i
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
getSucceededRemoteData(),
|
switchMap((rd: RemoteData<PaginatedList<Community>>) => {
|
||||||
flatMap((rd: RemoteData<PaginatedList<Community>>) =>
|
if (hasValue(rd) && hasValue(rd.payload)) {
|
||||||
this.transformListOfCommunities(rd.payload, level + 1, communityFlatNode, expandedNodes))
|
return this.transformListOfCommunities(rd.payload, level + 1, communityFlatNode, expandedNodes);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
subcoms = [...subcoms, nextSetOfSubcommunitiesPage];
|
subcoms = [...subcoms, nextSetOfSubcommunitiesPage];
|
||||||
@@ -246,14 +249,17 @@ export class CommunityListService {
|
|||||||
currentPage: i
|
currentPage: i
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
getSucceededRemoteData(),
|
|
||||||
map((rd: RemoteData<PaginatedList<Collection>>) => {
|
map((rd: RemoteData<PaginatedList<Collection>>) => {
|
||||||
let nodes = rd.payload.page
|
if (hasValue(rd) && hasValue(rd.payload)) {
|
||||||
.map((collection: Collection) => toFlatNode(collection, observableOf(false), level + 1, false, communityFlatNode));
|
let nodes = rd.payload.page
|
||||||
if (currentCollectionPage < rd.payload.totalPages && currentCollectionPage === rd.payload.currentPage) {
|
.map((collection: Collection) => toFlatNode(collection, observableOf(false), level + 1, false, communityFlatNode));
|
||||||
nodes = [...nodes, showMoreFlatNode('collection', level + 1, communityFlatNode)];
|
if (currentCollectionPage < rd.payload.totalPages && currentCollectionPage === rd.payload.currentPage) {
|
||||||
|
nodes = [...nodes, showMoreFlatNode('collection', level + 1, communityFlatNode)];
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
return nodes;
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
collections = [...collections, nextSetOfCollectionsPage];
|
collections = [...collections, nextSetOfCollectionsPage];
|
||||||
@@ -275,14 +281,24 @@ export class CommunityListService {
|
|||||||
let hasColls$: Observable<boolean>;
|
let hasColls$: Observable<boolean>;
|
||||||
hasSubcoms$ = this.communityDataService.findByParent(community.uuid, { elementsPerPage: 1 })
|
hasSubcoms$ = this.communityDataService.findByParent(community.uuid, { elementsPerPage: 1 })
|
||||||
.pipe(
|
.pipe(
|
||||||
getSucceededRemoteData(),
|
map((rd: RemoteData<PaginatedList<Community>>) => {
|
||||||
map((results) => results.payload.totalElements > 0),
|
if (hasValue(rd) && hasValue(rd.payload)) {
|
||||||
|
return rd.payload.totalElements > 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
hasColls$ = this.collectionDataService.findByParent(community.uuid, { elementsPerPage: 1 })
|
hasColls$ = this.collectionDataService.findByParent(community.uuid, { elementsPerPage: 1 })
|
||||||
.pipe(
|
.pipe(
|
||||||
getSucceededRemoteData(),
|
map((rd: RemoteData<PaginatedList<Collection>>) => {
|
||||||
map((results) => results.payload.totalElements > 0),
|
if (hasValue(rd) && hasValue(rd.payload)) {
|
||||||
|
return rd.payload.totalElements > 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let hasChildren$: Observable<boolean>;
|
let hasChildren$: Observable<boolean>;
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
<button type="button" class="btn btn-default" cdkTreeNodeToggle
|
<button type="button" class="btn btn-default" cdkTreeNodeToggle
|
||||||
[attr.aria-label]="'toggle ' + node.name"
|
[attr.aria-label]="'toggle ' + node.name"
|
||||||
(click)="toggleExpanded(node)"
|
(click)="toggleExpanded(node)"
|
||||||
[ngClass]="(node.isExpandable$ | async) ? 'visible' : 'invisible'">
|
[ngClass]="(hasChild(null, node)| async) ? 'visible' : 'invisible'">
|
||||||
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
<span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
|
||||||
aria-hidden="true"></span>
|
aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { FindListOptions } from '../../core/data/request.models';
|
import { FindListOptions } from '../../core/data/request.models';
|
||||||
@@ -24,15 +24,14 @@ export class CommunityListComponent implements OnInit, OnDestroy {
|
|||||||
public loadingNode: FlatNode;
|
public loadingNode: FlatNode;
|
||||||
|
|
||||||
treeControl = new FlatTreeControl<FlatNode>(
|
treeControl = new FlatTreeControl<FlatNode>(
|
||||||
(node) => node.level, (node) => true
|
(node: FlatNode) => node.level, (node: FlatNode) => true
|
||||||
);
|
);
|
||||||
|
|
||||||
dataSource: CommunityListDatasource;
|
dataSource: CommunityListDatasource;
|
||||||
|
|
||||||
paginationConfig: FindListOptions;
|
paginationConfig: FindListOptions;
|
||||||
|
|
||||||
constructor(private communityListService: CommunityListService,
|
constructor(private communityListService: CommunityListService) {
|
||||||
private zone: NgZone) {
|
|
||||||
this.paginationConfig = new FindListOptions();
|
this.paginationConfig = new FindListOptions();
|
||||||
this.paginationConfig.elementsPerPage = 2;
|
this.paginationConfig.elementsPerPage = 2;
|
||||||
this.paginationConfig.currentPage = 1;
|
this.paginationConfig.currentPage = 1;
|
||||||
@@ -40,7 +39,7 @@ export class CommunityListComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.dataSource = new CommunityListDatasource(this.communityListService, this.zone);
|
this.dataSource = new CommunityListDatasource(this.communityListService);
|
||||||
this.communityListService.getLoadingNodeFromStore().pipe(take(1)).subscribe((result) => {
|
this.communityListService.getLoadingNodeFromStore().pipe(take(1)).subscribe((result) => {
|
||||||
this.loadingNode = result;
|
this.loadingNode = result;
|
||||||
});
|
});
|
||||||
@@ -65,7 +64,7 @@ export class CommunityListComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the expanded variable of a node, adds it to the exapanded nodes list and reloads the tree so this node is expanded
|
* Toggles the expanded variable of a node, adds it to the expanded nodes list and reloads the tree so this node is expanded
|
||||||
* @param node Node we want to expand
|
* @param node Node we want to expand
|
||||||
*/
|
*/
|
||||||
toggleExpanded(node: FlatNode) {
|
toggleExpanded(node: FlatNode) {
|
||||||
|
@@ -6,7 +6,6 @@ import { HALLink } from '../../shared/hal-link.model';
|
|||||||
import { HALResource } from '../../shared/hal-resource.model';
|
import { HALResource } from '../../shared/hal-resource.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import * as decorators from './build-decorators';
|
import * as decorators from './build-decorators';
|
||||||
import { getDataServiceFor } from './build-decorators';
|
|
||||||
import { LinkService } from './link.service';
|
import { LinkService } from './link.service';
|
||||||
|
|
||||||
const spyOnFunction = <T>(obj: T, func: keyof T) => {
|
const spyOnFunction = <T>(obj: T, func: keyof T) => {
|
||||||
|
2
src/app/core/cache/builders/link.service.ts
vendored
2
src/app/core/cache/builders/link.service.ts
vendored
@@ -27,7 +27,7 @@ export class LinkService {
|
|||||||
*/
|
*/
|
||||||
public resolveLinks<T extends HALResource>(model: T, ...linksToFollow: Array<FollowLinkConfig<T>>): T {
|
public resolveLinks<T extends HALResource>(model: T, ...linksToFollow: Array<FollowLinkConfig<T>>): T {
|
||||||
linksToFollow.forEach((linkToFollow: FollowLinkConfig<T>) => {
|
linksToFollow.forEach((linkToFollow: FollowLinkConfig<T>) => {
|
||||||
this.resolveLink(model, linkToFollow);
|
this.resolveLink(model, linkToFollow);
|
||||||
});
|
});
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
|||||||
import { FindByIDRequest, FindListOptions } from './request.models';
|
import { FindByIDRequest, FindListOptions } from './request.models';
|
||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry } from './request.reducer';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
|
import {createNoContentRemoteDataObject$, createSuccessfulRemoteDataObject$} from '../../shared/remote-data.utils';
|
||||||
|
|
||||||
const LINK_NAME = 'test';
|
const LINK_NAME = 'test';
|
||||||
|
|
||||||
@@ -51,7 +52,9 @@ describe('ComColDataService', () => {
|
|||||||
let objectCache: ObjectCacheService;
|
let objectCache: ObjectCacheService;
|
||||||
let halService: any = {};
|
let halService: any = {};
|
||||||
|
|
||||||
const rdbService = {} as RemoteDataBuildService;
|
const rdbService = {
|
||||||
|
buildSingle : () => null
|
||||||
|
} as any;
|
||||||
const store = {} as Store<CoreState>;
|
const store = {} as Store<CoreState>;
|
||||||
const notificationsService = {} as NotificationsService;
|
const notificationsService = {} as NotificationsService;
|
||||||
const http = {} as HttpClient;
|
const http = {} as HttpClient;
|
||||||
@@ -178,6 +181,90 @@ describe('ComColDataService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('cache refresh', () => {
|
||||||
|
let communityWithoutParentHref;
|
||||||
|
let data;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
halService = {
|
||||||
|
getEndpoint: (linkPath) => 'https://rest.api/core/' + linkPath
|
||||||
|
};
|
||||||
|
service = initTestService();
|
||||||
|
|
||||||
|
})
|
||||||
|
describe('cache refreshed top level community', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(rdbService, 'buildSingle').and.returnValue(createNoContentRemoteDataObject$());
|
||||||
|
data = {
|
||||||
|
dso: Object.assign(new Community(), {
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'top level community'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
_links: {
|
||||||
|
parentCommunity: {
|
||||||
|
href: 'topLevel/parentCommunity'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
communityWithoutParentHref = {
|
||||||
|
dso: Object.assign(new Community(), {
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'top level community'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
_links: {}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
it('top level community cache refreshed', () => {
|
||||||
|
scheduler.schedule(() => (service as any).refreshCache(data));
|
||||||
|
scheduler.flush();
|
||||||
|
expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith('https://rest.api/core/communities/search/top');
|
||||||
|
});
|
||||||
|
it('top level community without parent link, cache not refreshed', () => {
|
||||||
|
scheduler.schedule(() => (service as any).refreshCache(communityWithoutParentHref));
|
||||||
|
scheduler.flush();
|
||||||
|
expect(requestService.removeByHrefSubstring).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cache refreshed child community', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const parentCommunity = Object.assign(new Community(), {
|
||||||
|
uuid: 'a20da287-e174-466a-9926-f66as300d399',
|
||||||
|
id: 'a20da287-e174-466a-9926-f66as300d399',
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'parent community'
|
||||||
|
}],
|
||||||
|
_links: {}
|
||||||
|
});
|
||||||
|
spyOn(rdbService, 'buildSingle').and.returnValue(createSuccessfulRemoteDataObject$(parentCommunity));
|
||||||
|
data = {
|
||||||
|
dso: Object.assign(new Community(), {
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'child community'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
_links: {
|
||||||
|
parentCommunity: {
|
||||||
|
href: 'child/parentCommunity'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
it('child level community cache refreshed', () => {
|
||||||
|
scheduler.schedule(() => (service as any).refreshCache(data));
|
||||||
|
scheduler.flush();
|
||||||
|
expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith('a20da287-e174-466a-9926-f66as300d399');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -21,12 +21,14 @@ import {
|
|||||||
configureRequest,
|
configureRequest,
|
||||||
getRemoteDataPayload,
|
getRemoteDataPayload,
|
||||||
getResponseFromEntry,
|
getResponseFromEntry,
|
||||||
|
getSucceededOrNoContentResponse,
|
||||||
getSucceededRemoteData
|
getSucceededRemoteData
|
||||||
} from '../shared/operators';
|
} from '../shared/operators';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { RestResponse } from '../cache/response.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
import { Bitstream } from '../shared/bitstream.model';
|
import { Bitstream } from '../shared/bitstream.model';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
|
import {Collection} from '../shared/collection.model';
|
||||||
|
|
||||||
export abstract class ComColDataService<T extends CacheableObject> extends DataService<T> {
|
export abstract class ComColDataService<T extends CacheableObject> extends DataService<T> {
|
||||||
protected abstract cds: CommunityDataService;
|
protected abstract cds: CommunityDataService;
|
||||||
@@ -119,4 +121,23 @@ export abstract class ComColDataService<T extends CacheableObject> extends DataS
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public refreshCache(dso: T) {
|
||||||
|
const parentCommunityUrl = this.parentCommunityUrlLookup(dso as any);
|
||||||
|
if (!hasValue(parentCommunityUrl)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.findByHref(parentCommunityUrl).pipe(
|
||||||
|
getSucceededOrNoContentResponse(),
|
||||||
|
take(1),
|
||||||
|
).subscribe((rd: RemoteData<any>) => {
|
||||||
|
const href = rd.hasSucceeded && !isEmpty(rd.payload.id) ? rd.payload.id : this.halService.getEndpoint('communities/search/top');
|
||||||
|
this.requestService.removeByHrefSubstring(href)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private parentCommunityUrlLookup(dso: Collection | Community) {
|
||||||
|
const parentCommunity = dso._links.parentCommunity;
|
||||||
|
return isNotEmpty(parentCommunity) ? parentCommunity.href : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,4 +55,8 @@ export class RemoteData<T> {
|
|||||||
return this.state === RemoteDataState.Success;
|
return this.state === RemoteDataState.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get hasNoContent(): boolean {
|
||||||
|
return this.statusCode === 204;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -75,6 +75,10 @@ export const getSucceededRemoteWithNotEmptyData = () =>
|
|||||||
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
|
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
|
||||||
source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded && isNotEmpty(rd.payload)));
|
source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded && isNotEmpty(rd.payload)));
|
||||||
|
|
||||||
|
export const getSucceededOrNoContentResponse = () =>
|
||||||
|
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
|
||||||
|
source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded || rd.hasNoContent));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first successful remotely retrieved object
|
* Get the first successful remotely retrieved object
|
||||||
*
|
*
|
||||||
|
@@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
import { RouteService } from '../../../core/services/route.service';
|
import { RouteService } from '../../../core/services/route.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
import { SharedModule } from '../../shared.module';
|
import { SharedModule } from '../../shared.module';
|
||||||
@@ -12,12 +12,14 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
|
|||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { CreateComColPageComponent } from './create-comcol-page.component';
|
import { CreateComColPageComponent } from './create-comcol-page.component';
|
||||||
import {
|
import {
|
||||||
createFailedRemoteDataObject$,
|
createFailedRemoteDataObject$, createNoContentRemoteDataObject$,
|
||||||
createSuccessfulRemoteDataObject$
|
createSuccessfulRemoteDataObject$
|
||||||
} from '../../remote-data.utils';
|
} from '../../remote-data.utils';
|
||||||
import { ComColDataService } from '../../../core/data/comcol-data.service';
|
import { ComColDataService } from '../../../core/data/comcol-data.service';
|
||||||
import { NotificationsService } from '../../notifications/notifications.service';
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
||||||
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
|
import {getTestScheduler} from 'jasmine-marbles';
|
||||||
|
|
||||||
describe('CreateComColPageComponent', () => {
|
describe('CreateComColPageComponent', () => {
|
||||||
let comp: CreateComColPageComponent<DSpaceObject>;
|
let comp: CreateComColPageComponent<DSpaceObject>;
|
||||||
@@ -29,9 +31,12 @@ describe('CreateComColPageComponent', () => {
|
|||||||
|
|
||||||
let community;
|
let community;
|
||||||
let newCommunity;
|
let newCommunity;
|
||||||
|
let parentCommunity;
|
||||||
let communityDataServiceStub;
|
let communityDataServiceStub;
|
||||||
let routeServiceStub;
|
let routeServiceStub;
|
||||||
let routerStub;
|
let routerStub;
|
||||||
|
let requestServiceStub;
|
||||||
|
let scheduler;
|
||||||
|
|
||||||
const logoEndpoint = 'rest/api/logo/endpoint';
|
const logoEndpoint = 'rest/api/logo/endpoint';
|
||||||
|
|
||||||
@@ -41,7 +46,18 @@ describe('CreateComColPageComponent', () => {
|
|||||||
metadata: [{
|
metadata: [{
|
||||||
key: 'dc.title',
|
key: 'dc.title',
|
||||||
value: 'test community'
|
value: 'test community'
|
||||||
}]
|
}],
|
||||||
|
_links: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
parentCommunity = Object.assign(new Community(), {
|
||||||
|
uuid: 'a20da287-e174-466a-9926-f66as300d399',
|
||||||
|
id: 'a20da287-e174-466a-9926-f66as300d399',
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'parent community'
|
||||||
|
}],
|
||||||
|
_links: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
newCommunity = Object.assign(new Community(), {
|
newCommunity = Object.assign(new Community(), {
|
||||||
@@ -49,7 +65,8 @@ describe('CreateComColPageComponent', () => {
|
|||||||
metadata: [{
|
metadata: [{
|
||||||
key: 'dc.title',
|
key: 'dc.title',
|
||||||
value: 'new community'
|
value: 'new community'
|
||||||
}]
|
}],
|
||||||
|
_links: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
communityDataServiceStub = {
|
communityDataServiceStub = {
|
||||||
@@ -61,7 +78,9 @@ describe('CreateComColPageComponent', () => {
|
|||||||
}]
|
}]
|
||||||
})),
|
})),
|
||||||
create: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity),
|
create: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity),
|
||||||
getLogoEndpoint: () => observableOf(logoEndpoint)
|
getLogoEndpoint: () => observableOf(logoEndpoint),
|
||||||
|
findByHref: () => null,
|
||||||
|
refreshCache: () => {return}
|
||||||
};
|
};
|
||||||
|
|
||||||
routeServiceStub = {
|
routeServiceStub = {
|
||||||
@@ -71,6 +90,10 @@ describe('CreateComColPageComponent', () => {
|
|||||||
navigate: (commands) => commands
|
navigate: (commands) => commands
|
||||||
};
|
};
|
||||||
|
|
||||||
|
requestServiceStub = jasmine.createSpyObj('RequestService', {
|
||||||
|
removeByHrefSubstring: jasmine.createSpy('removeByHrefSubstring'),
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
@@ -82,7 +105,8 @@ describe('CreateComColPageComponent', () => {
|
|||||||
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: RequestService, useValue: requestServiceStub}
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -97,6 +121,7 @@ describe('CreateComColPageComponent', () => {
|
|||||||
communityDataService = (comp as any).communityDataService;
|
communityDataService = (comp as any).communityDataService;
|
||||||
routeService = (comp as any).routeService;
|
routeService = (comp as any).routeService;
|
||||||
router = (comp as any).router;
|
router = (comp as any).router;
|
||||||
|
scheduler = getTestScheduler();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('onSubmit', () => {
|
describe('onSubmit', () => {
|
||||||
@@ -111,6 +136,7 @@ describe('CreateComColPageComponent', () => {
|
|||||||
value: 'test'
|
value: 'test'
|
||||||
}]
|
}]
|
||||||
}),
|
}),
|
||||||
|
_links: {},
|
||||||
uploader: {
|
uploader: {
|
||||||
options: {
|
options: {
|
||||||
url: ''
|
url: ''
|
||||||
@@ -123,19 +149,23 @@ describe('CreateComColPageComponent', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate when successful', () => {
|
it('should navigate and refresh cache when successful', () => {
|
||||||
spyOn(router, 'navigate');
|
spyOn(router, 'navigate');
|
||||||
comp.onSubmit(data);
|
spyOn((dsoDataService as any), 'refreshCache')
|
||||||
fixture.detectChanges();
|
scheduler.schedule(() => comp.onSubmit(data));
|
||||||
|
scheduler.flush();
|
||||||
expect(router.navigate).toHaveBeenCalled();
|
expect(router.navigate).toHaveBeenCalled();
|
||||||
|
expect((dsoDataService as any).refreshCache).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not navigate on failure', () => {
|
it('should neither navigate nor refresh cache on failure', () => {
|
||||||
spyOn(router, 'navigate');
|
spyOn(router, 'navigate');
|
||||||
spyOn(dsoDataService, 'create').and.returnValue(createFailedRemoteDataObject$(newCommunity));
|
spyOn(dsoDataService, 'create').and.returnValue(createFailedRemoteDataObject$(newCommunity));
|
||||||
comp.onSubmit(data);
|
spyOn(dsoDataService, 'refreshCache')
|
||||||
fixture.detectChanges();
|
scheduler.schedule(() => comp.onSubmit(data));
|
||||||
|
scheduler.flush();
|
||||||
expect(router.navigate).not.toHaveBeenCalled();
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
|
expect((dsoDataService as any).refreshCache).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -148,6 +178,7 @@ describe('CreateComColPageComponent', () => {
|
|||||||
value: 'test'
|
value: 'test'
|
||||||
}]
|
}]
|
||||||
}),
|
}),
|
||||||
|
_links: {},
|
||||||
uploader: {
|
uploader: {
|
||||||
options: {
|
options: {
|
||||||
url: ''
|
url: ''
|
||||||
@@ -164,21 +195,21 @@ describe('CreateComColPageComponent', () => {
|
|||||||
|
|
||||||
it('should not navigate', () => {
|
it('should not navigate', () => {
|
||||||
spyOn(router, 'navigate');
|
spyOn(router, 'navigate');
|
||||||
comp.onSubmit(data);
|
scheduler.schedule(() => comp.onSubmit(data));
|
||||||
fixture.detectChanges();
|
scheduler.flush();
|
||||||
expect(router.navigate).not.toHaveBeenCalled();
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the uploader\'s url to the logo\'s endpoint', () => {
|
it('should set the uploader\'s url to the logo\'s endpoint', () => {
|
||||||
comp.onSubmit(data);
|
scheduler.schedule(() => comp.onSubmit(data));
|
||||||
fixture.detectChanges();
|
scheduler.flush();
|
||||||
expect(data.uploader.options.url).toEqual(logoEndpoint);
|
expect(data.uploader.options.url).toEqual(logoEndpoint);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call the uploader\'s uploadAll', () => {
|
it('should call the uploader\'s uploadAll', () => {
|
||||||
spyOn(data.uploader, 'uploadAll');
|
spyOn(data.uploader, 'uploadAll');
|
||||||
comp.onSubmit(data);
|
scheduler.schedule(() => comp.onSubmit(data));
|
||||||
fixture.detectChanges();
|
scheduler.flush();
|
||||||
expect(data.uploader.uploadAll).toHaveBeenCalled();
|
expect(data.uploader.uploadAll).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -2,18 +2,21 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { take } from 'rxjs/operators';
|
import {flatMap, take} from 'rxjs/operators';
|
||||||
import { ComColDataService } from '../../../core/data/comcol-data.service';
|
import { ComColDataService } from '../../../core/data/comcol-data.service';
|
||||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { RouteService } from '../../../core/services/route.service';
|
import { RouteService } from '../../../core/services/route.service';
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { getSucceededRemoteData } from '../../../core/shared/operators';
|
import {
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
} from '../../../core/shared/operators';
|
||||||
import { ResourceType } from '../../../core/shared/resource-type';
|
import { ResourceType } from '../../../core/shared/resource-type';
|
||||||
import { hasValue, isNotEmpty, isNotUndefined } from '../../empty.util';
|
import {hasValue, isNotEmpty, isNotUndefined} from '../../empty.util';
|
||||||
import { NotificationsService } from '../../notifications/notifications.service';
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
import { RequestParam } from '../../../core/cache/models/request-param.model';
|
import { RequestParam } from '../../../core/cache/models/request-param.model';
|
||||||
|
import {RequestService} from '../../../core/data/request.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component representing the create page for communities and collections
|
* Component representing the create page for communities and collections
|
||||||
@@ -54,7 +57,8 @@ export class CreateComColPageComponent<TDomain extends DSpaceObject> implements
|
|||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected notificationsService: NotificationsService,
|
protected notificationsService: NotificationsService,
|
||||||
protected translate: TranslateService
|
protected translate: TranslateService,
|
||||||
|
protected requestService: RequestService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -76,25 +80,29 @@ export class CreateComColPageComponent<TDomain extends DSpaceObject> implements
|
|||||||
const dso = event.dso;
|
const dso = event.dso;
|
||||||
const uploader = event.uploader;
|
const uploader = event.uploader;
|
||||||
|
|
||||||
this.parentUUID$.pipe(take(1)).subscribe((uuid: string) => {
|
this.parentUUID$.pipe(
|
||||||
|
take(1),
|
||||||
|
flatMap((uuid: string) => {
|
||||||
const params = uuid ? [new RequestParam('parent', uuid)] : [];
|
const params = uuid ? [new RequestParam('parent', uuid)] : [];
|
||||||
this.dsoDataService.create(dso, ...params)
|
return this.dsoDataService.create(dso, ...params)
|
||||||
.pipe(getSucceededRemoteData())
|
.pipe(getFirstSucceededRemoteDataPayload()
|
||||||
.subscribe((dsoRD: RemoteData<TDomain>) => {
|
)
|
||||||
if (isNotUndefined(dsoRD)) {
|
}))
|
||||||
this.newUUID = dsoRD.payload.uuid;
|
.subscribe((dsoRD: TDomain) => {
|
||||||
if (uploader.queue.length > 0) {
|
if (isNotUndefined(dsoRD)) {
|
||||||
this.dsoDataService.getLogoEndpoint(this.newUUID).pipe(take(1)).subscribe((href: string) => {
|
this.newUUID = dsoRD.uuid;
|
||||||
uploader.options.url = href;
|
if (uploader.queue.length > 0) {
|
||||||
uploader.uploadAll();
|
this.dsoDataService.getLogoEndpoint(this.newUUID).pipe(take(1)).subscribe((href: string) => {
|
||||||
});
|
uploader.options.url = href;
|
||||||
} else {
|
uploader.uploadAll();
|
||||||
this.navigateToNewPage();
|
});
|
||||||
}
|
} else {
|
||||||
this.notificationsService.success(null, this.translate.get(this.type.value + '.create.notifications.success'));
|
this.navigateToNewPage();
|
||||||
}
|
}
|
||||||
});
|
this.dsoDataService.refreshCache(dsoRD);
|
||||||
});
|
}
|
||||||
|
this.notificationsService.success(null, this.translate.get(this.type.value + '.create.notifications.success'));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,5 +113,4 @@ export class CreateComColPageComponent<TDomain extends DSpaceObject> implements
|
|||||||
this.router.navigate([this.frontendURL + this.newUUID]);
|
this.router.navigate([this.frontendURL + this.newUUID]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { CommunityDataService } from '../../../core/data/community-data.service';
|
import { CommunityDataService } from '../../../core/data/community-data.service';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import {TranslateModule, TranslateService} from '@ngx-translate/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
import { SharedModule } from '../../shared.module';
|
import { SharedModule } from '../../shared.module';
|
||||||
@@ -13,6 +13,10 @@ import { DataService } from '../../../core/data/data.service';
|
|||||||
import { DeleteComColPageComponent } from './delete-comcol-page.component';
|
import { DeleteComColPageComponent } from './delete-comcol-page.component';
|
||||||
import { NotificationsService } from '../../notifications/notifications.service';
|
import { NotificationsService } from '../../notifications/notifications.service';
|
||||||
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
||||||
|
import {RequestService} from '../../../core/data/request.service';
|
||||||
|
import {getTestScheduler} from 'jasmine-marbles';
|
||||||
|
import {createNoContentRemoteDataObject$, createSuccessfulRemoteDataObject$} from '../../remote-data.utils';
|
||||||
|
import {ComColDataService} from '../../../core/data/comcol-data.service';
|
||||||
|
|
||||||
describe('DeleteComColPageComponent', () => {
|
describe('DeleteComColPageComponent', () => {
|
||||||
let comp: DeleteComColPageComponent<DSpaceObject>;
|
let comp: DeleteComColPageComponent<DSpaceObject>;
|
||||||
@@ -22,9 +26,15 @@ describe('DeleteComColPageComponent', () => {
|
|||||||
|
|
||||||
let community;
|
let community;
|
||||||
let newCommunity;
|
let newCommunity;
|
||||||
|
let parentCommunity;
|
||||||
let routerStub;
|
let routerStub;
|
||||||
let routeStub;
|
let routeStub;
|
||||||
let notificationsService;
|
let notificationsService;
|
||||||
|
let translateServiceStub;
|
||||||
|
let requestServiceStub;
|
||||||
|
|
||||||
|
let scheduler;
|
||||||
|
|
||||||
const validUUID = 'valid-uuid';
|
const validUUID = 'valid-uuid';
|
||||||
const invalidUUID = 'invalid-uuid';
|
const invalidUUID = 'invalid-uuid';
|
||||||
const frontendURL = '/testType';
|
const frontendURL = '/testType';
|
||||||
@@ -45,10 +55,21 @@ describe('DeleteComColPageComponent', () => {
|
|||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
parentCommunity = Object.assign(new Community(), {
|
||||||
|
uuid: 'a20da287-e174-466a-9926-f66as300d399',
|
||||||
|
id: 'a20da287-e174-466a-9926-f66as300d399',
|
||||||
|
metadata: [{
|
||||||
|
key: 'dc.title',
|
||||||
|
value: 'parent community'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
dsoDataService = jasmine.createSpyObj(
|
dsoDataService = jasmine.createSpyObj(
|
||||||
'dsoDataService',
|
'dsoDataService',
|
||||||
{
|
{
|
||||||
delete: observableOf({ isSuccessful: true })
|
delete: observableOf({ isSuccessful: true }),
|
||||||
|
findByHref: jasmine.createSpy('findByHref'),
|
||||||
|
refreshCache: jasmine.createSpy('refreshCache')
|
||||||
});
|
});
|
||||||
|
|
||||||
routerStub = {
|
routerStub = {
|
||||||
@@ -59,6 +80,14 @@ describe('DeleteComColPageComponent', () => {
|
|||||||
data: observableOf(community)
|
data: observableOf(community)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
requestServiceStub = jasmine.createSpyObj('RequestService', {
|
||||||
|
removeByHrefSubstring: jasmine.createSpy('removeByHrefSubstring')
|
||||||
|
});
|
||||||
|
|
||||||
|
translateServiceStub = jasmine.createSpyObj('TranslateService', {
|
||||||
|
instant: jasmine.createSpy('instant')
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
@@ -66,10 +95,12 @@ describe('DeleteComColPageComponent', () => {
|
|||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: DataService, useValue: dsoDataService },
|
{ provide: ComColDataService, useValue: dsoDataService },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||||
|
{ provide: TranslateService, useValue: translateServiceStub},
|
||||||
|
{ provide: RequestService, useValue: requestServiceStub}
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -82,43 +113,63 @@ describe('DeleteComColPageComponent', () => {
|
|||||||
notificationsService = (comp as any).notifications;
|
notificationsService = (comp as any).notifications;
|
||||||
(comp as any).frontendURL = frontendURL;
|
(comp as any).frontendURL = frontendURL;
|
||||||
router = (comp as any).router;
|
router = (comp as any).router;
|
||||||
|
scheduler = getTestScheduler();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('onConfirm', () => {
|
describe('onConfirm', () => {
|
||||||
let data1;
|
let data1;
|
||||||
let data2;
|
let data2;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
data1 = Object.assign(new Community(), {
|
data1 = {
|
||||||
uuid: validUUID,
|
dso: Object.assign(new Community(), {
|
||||||
metadata: [{
|
uuid: validUUID,
|
||||||
key: 'dc.title',
|
metadata: [{
|
||||||
value: 'test'
|
key: 'dc.title',
|
||||||
}]
|
value: 'test'
|
||||||
});
|
}]
|
||||||
|
}),
|
||||||
|
_links: {}
|
||||||
|
};
|
||||||
|
|
||||||
data2 = Object.assign(new Community(), {
|
data2 = {
|
||||||
uuid: invalidUUID,
|
dso: Object.assign(new Community(), {
|
||||||
metadata: [{
|
uuid: invalidUUID,
|
||||||
key: 'dc.title',
|
metadata: [{
|
||||||
value: 'test'
|
key: 'dc.title',
|
||||||
}]
|
value: 'test'
|
||||||
});
|
}]
|
||||||
|
}),
|
||||||
|
_links: {},
|
||||||
|
uploader: {
|
||||||
|
options: {
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
queue: [],
|
||||||
|
/* tslint:disable:no-empty */
|
||||||
|
uploadAll: () => {}
|
||||||
|
/* tslint:enable:no-empty */
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show an error notification on failure', () => {
|
it('should show an error notification on failure', () => {
|
||||||
(dsoDataService.delete as any).and.returnValue(observableOf({ isSuccessful: false }));
|
(dsoDataService.delete as any).and.returnValue(observableOf({ isSuccessful: false }));
|
||||||
spyOn(router, 'navigate');
|
spyOn(router, 'navigate');
|
||||||
comp.onConfirm(data2);
|
scheduler.schedule(() => comp.onConfirm(data2));
|
||||||
|
scheduler.flush();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(notificationsService.error).toHaveBeenCalled();
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
|
expect(dsoDataService.refreshCache).not.toHaveBeenCalled();
|
||||||
expect(router.navigate).toHaveBeenCalled();
|
expect(router.navigate).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show a success notification on success and navigate', () => {
|
it('should show a success notification on success and navigate', () => {
|
||||||
spyOn(router, 'navigate');
|
spyOn(router, 'navigate');
|
||||||
comp.onConfirm(data1);
|
scheduler.schedule(() => comp.onConfirm(data1));
|
||||||
|
scheduler.flush();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(notificationsService.success).toHaveBeenCalled();
|
expect(notificationsService.success).toHaveBeenCalled();
|
||||||
|
expect(dsoDataService.refreshCache).toHaveBeenCalled();
|
||||||
expect(router.navigate).toHaveBeenCalled();
|
expect(router.navigate).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import {RemoteData} from '../../../core/data/remote-data';
|
||||||
import { first, map } from 'rxjs/operators';
|
import {first, map} from 'rxjs/operators';
|
||||||
import { DataService } from '../../../core/data/data.service';
|
import {DSpaceObject} from '../../../core/shared/dspace-object.model';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import {NotificationsService} from '../../notifications/notifications.service';
|
||||||
import { NotificationsService } from '../../notifications/notifications.service';
|
import {TranslateService} from '@ngx-translate/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import {RestResponse} from '../../../core/cache/response.models';
|
||||||
import { RestResponse } from '../../../core/cache/response.models';
|
import {RequestService} from '../../../core/data/request.service';
|
||||||
|
import {ComColDataService} from '../../../core/data/comcol-data.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component representing the delete page for communities and collections
|
* Component representing the delete page for communities and collections
|
||||||
@@ -27,11 +28,12 @@ export class DeleteComColPageComponent<TDomain extends DSpaceObject> implements
|
|||||||
public dsoRD$: Observable<RemoteData<TDomain>>;
|
public dsoRD$: Observable<RemoteData<TDomain>>;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected dsoDataService: DataService<TDomain>,
|
protected dsoDataService: ComColDataService<TDomain>,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
protected notifications: NotificationsService,
|
protected notifications: NotificationsService,
|
||||||
protected translate: TranslateService
|
protected translate: TranslateService,
|
||||||
|
protected requestService: RequestService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +52,7 @@ export class DeleteComColPageComponent<TDomain extends DSpaceObject> implements
|
|||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
const successMessage = this.translate.instant((dso as any).type + '.delete.notification.success');
|
const successMessage = this.translate.instant((dso as any).type + '.delete.notification.success');
|
||||||
this.notifications.success(successMessage)
|
this.notifications.success(successMessage)
|
||||||
|
this.dsoDataService.refreshCache(dso);
|
||||||
} else {
|
} else {
|
||||||
const errorMessage = this.translate.instant((dso as any).type + '.delete.notification.fail');
|
const errorMessage = this.translate.instant((dso as any).type + '.delete.notification.fail');
|
||||||
this.notifications.error(errorMessage)
|
this.notifications.error(errorMessage)
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/collections/', object.id]" class="card-img-top">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/collections/', object.id]" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="object.logo">
|
<ds-grid-thumbnail [thumbnail]="(object.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="object.logo">
|
<ds-grid-thumbnail [thumbnail]="(object.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</span>
|
</span>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@@ -3,6 +3,7 @@ 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 { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
|
import { LinkService } from '../../../core/cache/builders/link.service';
|
||||||
|
|
||||||
let collectionGridElementComponent: CollectionGridElementComponent;
|
let collectionGridElementComponent: CollectionGridElementComponent;
|
||||||
let fixture: ComponentFixture<CollectionGridElementComponent>;
|
let fixture: ComponentFixture<CollectionGridElementComponent>;
|
||||||
@@ -29,12 +30,17 @@ const mockCollectionWithoutAbstract: Collection = Object.assign(new Collection()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const linkService = jasmine.createSpyObj('linkService', {
|
||||||
|
resolveLink: mockCollectionWithAbstract
|
||||||
|
});
|
||||||
|
|
||||||
describe('CollectionGridElementComponent', () => {
|
describe('CollectionGridElementComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ CollectionGridElementComponent ],
|
declarations: [ CollectionGridElementComponent ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: 'objectElementProvider', useValue: (mockCollectionWithAbstract)}
|
{ provide: 'objectElementProvider', useValue: (mockCollectionWithAbstract)},
|
||||||
|
{ provide: LinkService, useValue: linkService}
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
schemas: [ NO_ERRORS_SCHEMA ]
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { hasNoValue, hasValue } from '../../empty.util';
|
||||||
|
import { followLink } from '../../utils/follow-link-config.model';
|
||||||
|
import { LinkService } from '../../../core/cache/builders/link.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component representing a grid element for collection
|
* Component representing a grid element for collection
|
||||||
@@ -11,8 +13,29 @@ import { listableObjectComponent } from '../../object-collection/shared/listable
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-grid-element',
|
selector: 'ds-collection-grid-element',
|
||||||
styleUrls: ['./collection-grid-element.component.scss'],
|
styleUrls: ['./collection-grid-element.component.scss'],
|
||||||
templateUrl: './collection-grid-element.component.html'
|
templateUrl: './collection-grid-element.component.html',
|
||||||
})
|
})
|
||||||
|
|
||||||
@listableObjectComponent(Collection, ViewMode.GridElement)
|
@listableObjectComponent(Collection, ViewMode.GridElement)
|
||||||
export class CollectionGridElementComponent extends AbstractListableElementComponent<Collection> {}
|
export class CollectionGridElementComponent extends AbstractListableElementComponent<
|
||||||
|
Collection
|
||||||
|
> {
|
||||||
|
private _object: Collection;
|
||||||
|
|
||||||
|
constructor(private linkService: LinkService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() set object(object: Collection) {
|
||||||
|
this._object = object;
|
||||||
|
if (hasValue(this._object) && hasNoValue(this._object.logo)) {
|
||||||
|
this.linkService.resolveLink<Collection>(
|
||||||
|
this._object,
|
||||||
|
followLink('logo')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get object(): Collection {
|
||||||
|
return this._object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/communities/', object.id]" class="card-img-top">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/communities/', object.id]" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="object.logo">
|
<ds-grid-thumbnail [thumbnail]="(object.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="object.logo">
|
<ds-grid-thumbnail [thumbnail]="(object.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</span>
|
</span>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@@ -3,6 +3,7 @@ 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 { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
|
import { LinkService } from '../../../core/cache/builders/link.service';
|
||||||
|
|
||||||
let communityGridElementComponent: CommunityGridElementComponent;
|
let communityGridElementComponent: CommunityGridElementComponent;
|
||||||
let fixture: ComponentFixture<CommunityGridElementComponent>;
|
let fixture: ComponentFixture<CommunityGridElementComponent>;
|
||||||
@@ -29,12 +30,17 @@ const mockCommunityWithoutAbstract: Community = Object.assign(new Community(), {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const linkService = jasmine.createSpyObj('linkService', {
|
||||||
|
resolveLink: mockCommunityWithAbstract
|
||||||
|
});
|
||||||
|
|
||||||
describe('CommunityGridElementComponent', () => {
|
describe('CommunityGridElementComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ CommunityGridElementComponent ],
|
declarations: [ CommunityGridElementComponent ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: 'objectElementProvider', useValue: (mockCommunityWithAbstract)}
|
{ provide: 'objectElementProvider', useValue: (mockCommunityWithAbstract)},
|
||||||
|
{ provide: LinkService, useValue: linkService}
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
schemas: [ NO_ERRORS_SCHEMA ]
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { Community } from '../../../core/shared/community.model';
|
import { Community } from '../../../core/shared/community.model';
|
||||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { followLink } from '../../utils/follow-link-config.model';
|
||||||
|
import { LinkService } from '../../../core/cache/builders/link.service';
|
||||||
|
import { hasNoValue, hasValue } from '../../empty.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component representing a grid element for a community
|
* Component representing a grid element for a community
|
||||||
@@ -15,4 +17,21 @@ import { listableObjectComponent } from '../../object-collection/shared/listable
|
|||||||
})
|
})
|
||||||
|
|
||||||
@listableObjectComponent(Community, ViewMode.GridElement)
|
@listableObjectComponent(Community, ViewMode.GridElement)
|
||||||
export class CommunityGridElementComponent extends AbstractListableElementComponent<Community> {}
|
export class CommunityGridElementComponent extends AbstractListableElementComponent<Community> {
|
||||||
|
private _object: Community;
|
||||||
|
|
||||||
|
constructor( private linkService: LinkService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() set object(object: Community) {
|
||||||
|
this._object = object;
|
||||||
|
if (hasValue(this._object) && hasNoValue(this._object.logo)) {
|
||||||
|
this.linkService.resolveLink<Community>(this._object, followLink('logo'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get object(): Community {
|
||||||
|
return this._object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<img [src]="src | dsSafeUrl" (error)="errorHandler($event)"/>
|
<img [src]="src | dsSafeUrl" (error)="errorHandler($event)" />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,10 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnInit,
|
||||||
|
SimpleChanges,
|
||||||
|
} from '@angular/core';
|
||||||
import { Bitstream } from '../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||||
import { hasValue } from '../../empty.util';
|
import { hasValue } from '../../empty.util';
|
||||||
|
|
||||||
@@ -11,10 +17,9 @@ import { hasValue } from '../../empty.util';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-grid-thumbnail',
|
selector: 'ds-grid-thumbnail',
|
||||||
styleUrls: ['./grid-thumbnail.component.scss'],
|
styleUrls: ['./grid-thumbnail.component.scss'],
|
||||||
templateUrl: './grid-thumbnail.component.html'
|
templateUrl: './grid-thumbnail.component.html',
|
||||||
})
|
})
|
||||||
export class GridThumbnailComponent implements OnInit {
|
export class GridThumbnailComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
@Input() thumbnail: Bitstream;
|
@Input() thumbnail: Bitstream;
|
||||||
|
|
||||||
data: any = {};
|
data: any = {};
|
||||||
@@ -22,19 +27,47 @@ export class GridThumbnailComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The default 'holder.js' image
|
* The default 'holder.js' image
|
||||||
*/
|
*/
|
||||||
@Input() defaultImage? = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjYwIiBoZWlnaHQ9IjE4MCIgdmlld0JveD0iMCAwIDI2MCAxODAiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiPjwhLS0KU291cmNlIFVSTDogaG9sZGVyLmpzLzEwMCV4MTgwL3RleHQ6Tm8gVGh1bWJuYWlsCkNyZWF0ZWQgd2l0aCBIb2xkZXIuanMgMi42LjAuCkxlYXJuIG1vcmUgYXQgaHR0cDovL2hvbGRlcmpzLmNvbQooYykgMjAxMi0yMDE1IEl2YW4gTWFsb3BpbnNreSAtIGh0dHA6Ly9pbXNreS5jbwotLT48ZGVmcz48c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwhW0NEQVRBWyNob2xkZXJfMTVmNzJmMmFlMGIgdGV4dCB7IGZpbGw6I0FBQUFBQTtmb250LXdlaWdodDpib2xkO2ZvbnQtZmFtaWx5OkFyaWFsLCBIZWx2ZXRpY2EsIE9wZW4gU2Fucywgc2Fucy1zZXJpZiwgbW9ub3NwYWNlO2ZvbnQtc2l6ZToxM3B0IH0gXV0+PC9zdHlsZT48L2RlZnM+PGcgaWQ9ImhvbGRlcl8xNWY3MmYyYWUwYiI+PHJlY3Qgd2lkdGg9IjI2MCIgaGVpZ2h0PSIxODAiIGZpbGw9IiNFRUVFRUUiLz48Zz48dGV4dCB4PSI3Mi4yNDIxODc1IiB5PSI5NiI+Tm8gVGh1bWJuYWlsPC90ZXh0PjwvZz48L2c+PC9zdmc+';
|
@Input() defaultImage? =
|
||||||
|
'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjYwIiBoZWlnaHQ9IjE4MCIgdmlld0JveD0iMCAwIDI2MCAxODAiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiPjwhLS0KU291cmNlIFVSTDogaG9sZGVyLmpzLzEwMCV4MTgwL3RleHQ6Tm8gVGh1bWJuYWlsCkNyZWF0ZWQgd2l0aCBIb2xkZXIuanMgMi42LjAuCkxlYXJuIG1vcmUgYXQgaHR0cDovL2hvbGRlcmpzLmNvbQooYykgMjAxMi0yMDE1IEl2YW4gTWFsb3BpbnNreSAtIGh0dHA6Ly9pbXNreS5jbwotLT48ZGVmcz48c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwhW0NEQVRBWyNob2xkZXJfMTVmNzJmMmFlMGIgdGV4dCB7IGZpbGw6I0FBQUFBQTtmb250LXdlaWdodDpib2xkO2ZvbnQtZmFtaWx5OkFyaWFsLCBIZWx2ZXRpY2EsIE9wZW4gU2Fucywgc2Fucy1zZXJpZiwgbW9ub3NwYWNlO2ZvbnQtc2l6ZToxM3B0IH0gXV0+PC9zdHlsZT48L2RlZnM+PGcgaWQ9ImhvbGRlcl8xNWY3MmYyYWUwYiI+PHJlY3Qgd2lkdGg9IjI2MCIgaGVpZ2h0PSIxODAiIGZpbGw9IiNFRUVFRUUiLz48Zz48dGV4dCB4PSI3Mi4yNDIxODc1IiB5PSI5NiI+Tm8gVGh1bWJuYWlsPC90ZXh0PjwvZz48L2c+PC9zdmc+';
|
||||||
|
|
||||||
src: string;
|
src: string;
|
||||||
|
|
||||||
errorHandler(event) {
|
errorHandler(event) {
|
||||||
event.currentTarget.src = this.defaultImage;
|
event.currentTarget.src = this.defaultImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the src
|
||||||
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (hasValue(this.thumbnail) && hasValue(this.thumbnail._links) && this.thumbnail._links.content.href) {
|
this.src = this.defaultImage;
|
||||||
this.src = this.thumbnail._links.content.href;
|
|
||||||
} else {
|
this.checkThumbnail(this.thumbnail);
|
||||||
this.src = this.defaultImage
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the old input is undefined and the new one is a bitsream then set src
|
||||||
|
*/
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (
|
||||||
|
!hasValue(changes.thumbnail.previousValue) &&
|
||||||
|
hasValue(changes.thumbnail.currentValue)
|
||||||
|
) {
|
||||||
|
console.log('this.thumbnail', changes.thumbnail.currentValue);
|
||||||
|
this.checkThumbnail(changes.thumbnail.currentValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the Bitstream has any content than set the src
|
||||||
|
*/
|
||||||
|
checkThumbnail(thumbnail: Bitstream) {
|
||||||
|
if (
|
||||||
|
hasValue(thumbnail) &&
|
||||||
|
hasValue(thumbnail._links) &&
|
||||||
|
thumbnail._links.content.href
|
||||||
|
) {
|
||||||
|
this.src = thumbnail._links.content.href;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/collections/', dso.id]" class="card-img-top">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/collections/', dso.id]" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.logo">
|
<ds-grid-thumbnail [thumbnail]="(dso.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.logo">
|
<ds-grid-thumbnail [thumbnail]="(dso.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</span>
|
</span>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@@ -19,6 +19,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service';
|
|||||||
import { TruncatePipe } from '../../../utils/truncate.pipe';
|
import { TruncatePipe } from '../../../utils/truncate.pipe';
|
||||||
import { CollectionSearchResultGridElementComponent } from './collection-search-result-grid-element.component';
|
import { CollectionSearchResultGridElementComponent } from './collection-search-result-grid-element.component';
|
||||||
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
||||||
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
|
|
||||||
let collectionSearchResultGridElementComponent: CollectionSearchResultGridElementComponent;
|
let collectionSearchResultGridElementComponent: CollectionSearchResultGridElementComponent;
|
||||||
let fixture: ComponentFixture<CollectionSearchResultGridElementComponent>;
|
let fixture: ComponentFixture<CollectionSearchResultGridElementComponent>;
|
||||||
@@ -52,6 +53,9 @@ mockCollectionWithoutAbstract.indexableObject = Object.assign(new Collection(),
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const linkService = jasmine.createSpyObj('linkService', {
|
||||||
|
resolveLink: mockCollectionWithAbstract
|
||||||
|
});
|
||||||
|
|
||||||
describe('CollectionSearchResultGridElementComponent', () => {
|
describe('CollectionSearchResultGridElementComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
@@ -72,6 +76,7 @@ describe('CollectionSearchResultGridElementComponent', () => {
|
|||||||
{ provide: DSOChangeAnalyzer, useValue: {} },
|
{ provide: DSOChangeAnalyzer, useValue: {} },
|
||||||
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
{ provide: BitstreamFormatDataService, useValue: {} },
|
{ provide: BitstreamFormatDataService, useValue: {} },
|
||||||
|
{ provide: LinkService, useValue: linkService}
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
schemas: [ NO_ERRORS_SCHEMA ]
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
|
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
|
||||||
import { Collection } from '../../../../core/shared/collection.model';
|
import { Collection } from '../../../../core/shared/collection.model';
|
||||||
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
|
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { hasNoValue, hasValue } from '../../../empty.util';
|
||||||
|
import { followLink } from '../../../utils/follow-link-config.model';
|
||||||
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-search-result-grid-element',
|
selector: 'ds-collection-search-result-grid-element',
|
||||||
@@ -15,4 +19,28 @@ import { listableObjectComponent } from '../../../object-collection/shared/lista
|
|||||||
* Component representing a grid element for a collection search result
|
* Component representing a grid element for a collection search result
|
||||||
*/
|
*/
|
||||||
@listableObjectComponent(CollectionSearchResult, ViewMode.GridElement)
|
@listableObjectComponent(CollectionSearchResult, ViewMode.GridElement)
|
||||||
export class CollectionSearchResultGridElementComponent extends SearchResultGridElementComponent<CollectionSearchResult, Collection> {}
|
export class CollectionSearchResultGridElementComponent extends SearchResultGridElementComponent< CollectionSearchResult, Collection > {
|
||||||
|
private _dso: Collection;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private linkService: LinkService,
|
||||||
|
protected truncatableService: TruncatableService,
|
||||||
|
protected bitstreamDataService: BitstreamDataService
|
||||||
|
) {
|
||||||
|
super(truncatableService, bitstreamDataService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() set dso(dso: Collection) {
|
||||||
|
this._dso = dso;
|
||||||
|
if (hasValue(this._dso) && hasNoValue(this._dso.logo)) {
|
||||||
|
this.linkService.resolveLink<Collection>(
|
||||||
|
this._dso,
|
||||||
|
followLink('logo')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get dso(): Collection {
|
||||||
|
return this._dso;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/communities/', dso.id]" class="card-img-top">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/communities/', dso.id]" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.logo">
|
<ds-grid-thumbnail [thumbnail]="(dso.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top">
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.logo">
|
<ds-grid-thumbnail [thumbnail]="(dso.logo | async)?.payload">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</span>
|
</span>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@@ -19,6 +19,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service';
|
|||||||
import { TruncatePipe } from '../../../utils/truncate.pipe';
|
import { TruncatePipe } from '../../../utils/truncate.pipe';
|
||||||
import { CommunitySearchResultGridElementComponent } from './community-search-result-grid-element.component';
|
import { CommunitySearchResultGridElementComponent } from './community-search-result-grid-element.component';
|
||||||
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
import { BitstreamFormatDataService } from '../../../../core/data/bitstream-format-data.service';
|
||||||
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
|
|
||||||
let communitySearchResultGridElementComponent: CommunitySearchResultGridElementComponent;
|
let communitySearchResultGridElementComponent: CommunitySearchResultGridElementComponent;
|
||||||
let fixture: ComponentFixture<CommunitySearchResultGridElementComponent>;
|
let fixture: ComponentFixture<CommunitySearchResultGridElementComponent>;
|
||||||
@@ -52,6 +53,9 @@ mockCommunityWithoutAbstract.indexableObject = Object.assign(new Community(), {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const linkService = jasmine.createSpyObj('linkService', {
|
||||||
|
resolveLink: mockCommunityWithAbstract
|
||||||
|
});
|
||||||
|
|
||||||
describe('CommunitySearchResultGridElementComponent', () => {
|
describe('CommunitySearchResultGridElementComponent', () => {
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
@@ -72,6 +76,7 @@ describe('CommunitySearchResultGridElementComponent', () => {
|
|||||||
{ provide: DSOChangeAnalyzer, useValue: {} },
|
{ provide: DSOChangeAnalyzer, useValue: {} },
|
||||||
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
{ provide: DefaultChangeAnalyzer, useValue: {} },
|
||||||
{ provide: BitstreamFormatDataService, useValue: {} },
|
{ provide: BitstreamFormatDataService, useValue: {} },
|
||||||
|
{ provide: LinkService, useValue: linkService}
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
schemas: [ NO_ERRORS_SCHEMA ]
|
||||||
|
@@ -1,18 +1,46 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { Community } from '../../../../core/shared/community.model';
|
import { Community } from '../../../../core/shared/community.model';
|
||||||
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
|
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
|
||||||
import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
|
import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator';
|
import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator';
|
||||||
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
import { hasNoValue, hasValue } from '../../../empty.util';
|
||||||
|
import { followLink } from '../../../utils/follow-link-config.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-community-search-result-grid-element',
|
selector: 'ds-community-search-result-grid-element',
|
||||||
styleUrls: ['../search-result-grid-element.component.scss', 'community-search-result-grid-element.component.scss'],
|
styleUrls: [
|
||||||
templateUrl: 'community-search-result-grid-element.component.html'
|
'../search-result-grid-element.component.scss',
|
||||||
|
'community-search-result-grid-element.component.scss',
|
||||||
|
],
|
||||||
|
templateUrl: 'community-search-result-grid-element.component.html',
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* Component representing a grid element for a community search result
|
* Component representing a grid element for a community search result
|
||||||
*/
|
*/
|
||||||
@listableObjectComponent(CommunitySearchResult, ViewMode.GridElement)
|
@listableObjectComponent(CommunitySearchResult, ViewMode.GridElement)
|
||||||
export class CommunitySearchResultGridElementComponent extends SearchResultGridElementComponent<CommunitySearchResult, Community> {
|
export class CommunitySearchResultGridElementComponent extends SearchResultGridElementComponent<CommunitySearchResult,Community> {
|
||||||
|
private _dso: Community;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private linkService: LinkService,
|
||||||
|
protected truncatableService: TruncatableService,
|
||||||
|
protected bitstreamDataService: BitstreamDataService
|
||||||
|
) {
|
||||||
|
super(truncatableService, bitstreamDataService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input() set dso(dso: Community) {
|
||||||
|
this._dso = dso;
|
||||||
|
if (hasValue(this._dso) && hasNoValue(this._dso.logo)) {
|
||||||
|
this.linkService.resolveLink<Community>(this._dso, followLink('logo'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get dso(): Community {
|
||||||
|
return this._dso;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -6,10 +6,10 @@ import { BitstreamDataService } from '../../../core/data/bitstream-data.service'
|
|||||||
import { Bitstream } from '../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { Metadata } from '../../../core/shared/metadata.utils';
|
import { Metadata } from '../../../core/shared/metadata.utils';
|
||||||
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
|
||||||
import { hasValue } from '../../empty.util';
|
import { hasValue } from '../../empty.util';
|
||||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||||
import { TruncatableService } from '../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../truncatable/truncatable.service';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-result-grid-element',
|
selector: 'ds-search-result-grid-element',
|
||||||
|
@@ -69,3 +69,24 @@ export function createPendingRemoteDataObject<T>(object?: T): RemoteData<T> {
|
|||||||
export function createPendingRemoteDataObject$<T>(object?: T): Observable<RemoteData<T>> {
|
export function createPendingRemoteDataObject$<T>(object?: T): Observable<RemoteData<T>> {
|
||||||
return observableOf(createPendingRemoteDataObject(object));
|
return observableOf(createPendingRemoteDataObject(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to create a remote data object with no content
|
||||||
|
*/
|
||||||
|
export function createNoContentRemoteDataObject<T>(): RemoteData<T> {
|
||||||
|
return new RemoteData(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
204
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to create a remote data object that has succeeded with no content, wrapped in an observable
|
||||||
|
*/
|
||||||
|
export function createNoContentRemoteDataObject$<T>(): Observable<RemoteData<T>> {
|
||||||
|
return observableOf(createNoContentRemoteDataObject());
|
||||||
|
}
|
||||||
|
@@ -11,9 +11,10 @@ import { ItemPageConfig } from './item-page-config.interface';
|
|||||||
import { CollectionPageConfig } from './collection-page-config.interface';
|
import { CollectionPageConfig } from './collection-page-config.interface';
|
||||||
import { Theme } from './theme.inferface';
|
import { Theme } from './theme.inferface';
|
||||||
import {AuthConfig} from './auth-config.interfaces';
|
import {AuthConfig} from './auth-config.interfaces';
|
||||||
|
import { UIServerConfig } from './ui-server-config.interface';
|
||||||
|
|
||||||
export interface GlobalConfig extends Config {
|
export interface GlobalConfig extends Config {
|
||||||
ui: ServerConfig;
|
ui: UIServerConfig;
|
||||||
rest: ServerConfig;
|
rest: ServerConfig;
|
||||||
production: boolean;
|
production: boolean;
|
||||||
cache: CacheConfig;
|
cache: CacheConfig;
|
||||||
|
14
src/config/ui-server-config.interface.ts
Normal file
14
src/config/ui-server-config.interface.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { ServerConfig } from './server-config.interface';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server configuration related to the UI.
|
||||||
|
*/
|
||||||
|
export class UIServerConfig extends ServerConfig {
|
||||||
|
|
||||||
|
// rateLimiter is used to limit the amount of requests a user is allowed make in an amount of time, in order to prevent overloading the server
|
||||||
|
rateLimiter?: {
|
||||||
|
windowMs: number;
|
||||||
|
max: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -13,6 +13,11 @@ export const environment: GlobalConfig = {
|
|||||||
port: 4000,
|
port: 4000,
|
||||||
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
|
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
|
||||||
nameSpace: '/',
|
nameSpace: '/',
|
||||||
|
// The rateLimiter settings limit each IP to a "max" of 500 requests per "windowMs" (1 minute).
|
||||||
|
rateLimiter: {
|
||||||
|
windowMs: 1 * 60 * 1000, // 1 minute
|
||||||
|
max: 500 // limit each IP to 500 requests per windowMs
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// The REST API server settings.
|
// The REST API server settings.
|
||||||
// NOTE: these must be "synced" with the 'dspace.server.url' setting in your backend's local.cfg.
|
// NOTE: these must be "synced" with the 'dspace.server.url' setting in your backend's local.cfg.
|
||||||
|
@@ -19,6 +19,7 @@ export const environment: Partial<GlobalConfig> = {
|
|||||||
port: 80,
|
port: 80,
|
||||||
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
|
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
|
||||||
nameSpace: '/angular-dspace',
|
nameSpace: '/angular-dspace',
|
||||||
|
rateLimiter: undefined
|
||||||
},
|
},
|
||||||
// Caching settings
|
// Caching settings
|
||||||
cache: {
|
cache: {
|
||||||
|
@@ -4084,6 +4084,11 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
homedir-polyfill "^1.0.1"
|
homedir-polyfill "^1.0.1"
|
||||||
|
|
||||||
|
express-rate-limit@^5.1.3:
|
||||||
|
version "5.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-5.1.3.tgz#656bacce3f093034976346958a0f0199902c9174"
|
||||||
|
integrity sha512-TINcxve5510pXj4n9/1AMupkj3iWxl3JuZaWhCdYDlZeoCPqweGZrxbrlqTCFb1CT5wli7s8e2SH/Qz2c9GorA==
|
||||||
|
|
||||||
express@4.16.2:
|
express@4.16.2:
|
||||||
version "4.16.2"
|
version "4.16.2"
|
||||||
resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
|
resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
|
||||||
|
Reference in New Issue
Block a user