Skip to content

Commit

Permalink
data: implement local fields
Browse files Browse the repository at this point in the history
* Adds local fields on document, item and holdings (serial type).
* Adds holdings informations on the detail screen.

Co-Authored-by: Bertrand Zuchuat <bertrand.zuchuat@rero.ch>
  • Loading branch information
Garfield-fr committed Dec 9, 2020
1 parent 34dcedb commit b7b55ba
Show file tree
Hide file tree
Showing 21 changed files with 875 additions and 137 deletions.
81 changes: 81 additions & 0 deletions projects/admin/src/app/api/local-field-api.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* RERO ILS UI
* Copyright (C) 2020 RERO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { RecordService, RecordUiService } from '@rero/ng-core';
import { of } from 'rxjs';
import { LocalFieldApiService } from './local-field-api.service';

describe('Service: LocalFieldApi', () => {

let localFieldApiService: LocalFieldApiService;

const emptyRecords = {
aggregations: {},
hits: {
total: {
relation: 'eq',
value: 0
},
hits: []
},
links: {}
};

const recordServiceSpy = jasmine.createSpyObj('RecordService', ['getRecords', 'totalHits']);
recordServiceSpy.getRecords.and.returnValue(of(emptyRecords));
recordServiceSpy.totalHits.and.returnValue(0);

const recordUiServiceSpy = jasmine.createSpyObj('RecordUiService', ['deleteRecord']);
recordUiServiceSpy.deleteRecord.and.returnValue(of(true));

beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
RouterTestingModule
],
providers: [
LocalFieldApiService,
{ provide: RecordService, useValue: recordServiceSpy },
{ provide: RecordUiService, useValue: recordUiServiceSpy },
]
});
localFieldApiService = TestBed.inject(LocalFieldApiService);
});

it('should create a service', () => {
expect(localFieldApiService).toBeTruthy();
});

it('should return an empty object', () => {
localFieldApiService
.getByResourceTypeAndResourcePidAndOrganisationId('doc', '1', '1')
.subscribe(result => {
expect('metadata' in result).toBeFalsy();
});
});

it('should return true on the deletion of the record', () => {
localFieldApiService
.delete('1').subscribe((success: boolean) => {
expect(success).toBeTruthy();
});
});
});
71 changes: 71 additions & 0 deletions projects/admin/src/app/api/local-field-api.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* RERO ILS UI
* Copyright (C) 2020 RERO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { Injectable } from '@angular/core';
import { RecordService, RecordUiService } from '@rero/ng-core';
import { map } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
})
export class LocalFieldApiService {

/**
* Constructor
* @param _recordService - RecordService
*/
constructor(
private _recordService: RecordService,
private _recordUiService: RecordUiService
) { }

/**
* Get Local field for current resource and organisation user
* @param resourceType - string, parent type of resource
* @param resourcePid - string, parent pid of resource
* @param organisationPid - string, pid of organisation
* @return Observable
*/
getByResourceTypeAndResourcePidAndOrganisationId(
resourceType: string,
resourcePid: string,
organisationPid: string
) {
const query = [
`parent.type:${resourceType}`,
`parent.pid:${resourcePid}`,
`organisation.pid:${organisationPid}`,
].join(' AND ');

return this._recordService.getRecords('local_fields', query, 1, 1).pipe(
map((result: any) => {
return this._recordService.totalHits(result.hits.total) === 1
? result.hits.hits[0]
: {};
})
);
}

/**
* Delete local fields by resource pid
* @param resourcePid - string, pid of resource
* @return Observable
*/
delete(resourcePid: string) {
return this._recordUiService.deleteRecord('local_fields', resourcePid);
}
}
4 changes: 3 additions & 1 deletion projects/admin/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ import { UiRemoteTypeaheadService } from './service/ui-remote-typeahead.service'
import { CustomShortcutHelpComponent } from './widgets/custom-shortcut-help/custom-shortcut-help.component';
import { FrontpageBoardComponent } from './widgets/frontpage/frontpage-board/frontpage-board.component';
import { FrontpageComponent } from './widgets/frontpage/frontpage.component';
import { LocalFieldComponent } from './record/detail-view/local-field/local-field.component';

/** Init application factory */
export function appInitFactory(appInitService: AppInitService) {
Expand Down Expand Up @@ -228,7 +229,8 @@ export function appInitFactory(appInitService: AppInitService) {
IllRequestDetailViewComponent,
CustomShortcutHelpComponent,
HoldingItemNoteComponent,
MenuSwitchLibraryComponent
MenuSwitchLibraryComponent,
LocalFieldComponent
],
imports: [
AppRoutingModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export class ErrorPageComponent implements OnInit {
* - level: the boostrap alert look-and-feel level to use for the error. 'danger' by default.
*/
messages = {
400: {
title: _('Bad Request'),
description: [_('Malformed request syntax.')],
level: 'warning'
},
401: {
title: _('Unauthorized'),
description: [_('Access denied due to invalid credentials.')],
Expand Down
75 changes: 75 additions & 0 deletions projects/admin/src/app/guard/can-add-local-fields.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* RERO ILS UI
* Copyright (C) 2020 RERO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { LocalStorageService } from '@rero/ng-core';
import { of } from 'rxjs';
import { LocalFieldApiService } from '../api/local-field-api.service';
import { CanAddLocalFieldsGuard } from './can-add-local-fields.guard';


describe('CanAddLocalFieldsGuard', () => {

let canAddLocalFieldsGuard: CanAddLocalFieldsGuard;

const localFieldsApiServiceSpy = jasmine.createSpyObj(
'LocalFieldApiService', [
'getByResourceTypeAndResourcePidAndOrganisationId'
]
);
localFieldsApiServiceSpy
.getByResourceTypeAndResourcePidAndOrganisationId
.and.returnValue(of({}));

const localStorageServiceSpy = jasmine.createSpyObj('LocalStorageService', ['get']);
localStorageServiceSpy.get.and.returnValue({
currentLibrary: 1,
currentOrganisation: 1
});

const activatedRouteSnapshotSpy = jasmine.createSpyObj('ActivatedRouteSnapshot', ['']);
activatedRouteSnapshotSpy.queryParams = {type: 'documents', ref: '240'};

const routerStateSnapshotSpy = jasmine.createSpyObj('RouterStateSnapshot', ['']);

beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
RouterTestingModule
],
providers: [
CanAddLocalFieldsGuard,
{ provide: LocalFieldApiService, useValue: localFieldsApiServiceSpy },
{ provide: LocalStorageService, useValue: localStorageServiceSpy },
]
});
canAddLocalFieldsGuard = TestBed.inject(CanAddLocalFieldsGuard);
});

it('should create a service', () => {
expect(canAddLocalFieldsGuard).toBeTruthy();
});

it('should return true if the url parameters are right', () => {
expect(canAddLocalFieldsGuard.canActivate(
activatedRouteSnapshotSpy, routerStateSnapshotSpy
)).toBeTruthy();
});
});
101 changes: 101 additions & 0 deletions projects/admin/src/app/guard/can-add-local-fields.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* RERO ILS UI
* Copyright (C) 2020 RERO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { LocalStorageService } from '@rero/ng-core';
import { User } from '@rero/shared';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { LocalFieldApiService } from '../api/local-field-api.service';

@Injectable({
providedIn: 'root'
})
export class CanAddLocalFieldsGuard implements CanActivate {
/**
* This guard allows to control the existence of the resource
* for the current organization. If a record exists, it blocks
* the addition of a new resource.
*/

/** Available type of document to add local fields */
private _types = {
documents: 'doc',
holdings: 'hold',
items: 'item'
};

/**
* Constructor
* @param _localeStorageService - LocalStorageService
* @param _localFieldsApiService - LocalFieldApiService
* @param _router - Router
*/
constructor(
private _localeStorageService: LocalStorageService,
private _localFieldsApiService: LocalFieldApiService,
private _router: Router
) {}

/**
* Can activate
* @param next - ActivatedRouteSnapshot
* @param state - RouterStateSnapshot
*/
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

const params = next.queryParams;
if (params.type && params.ref) {
/*
* Use of User local storage, because the loading of the routing
* is done before the load of the application.
*/
const userLocale: User = this._localeStorageService.get(User.STORAGE_KEY);
if (userLocale) {
const organisationPid = userLocale.currentOrganisation;
return this._localFieldsApiService.getByResourceTypeAndResourcePidAndOrganisationId(
this._translateType(params.type),
params.ref,
organisationPid
).pipe(map(record => {
return record.metadata ? false : true;
}));
}
this._router.navigate(['/errors/401'], { skipLocationChange: true });
} else {
this._router.navigate(['/errors/400'], { skipLocationChange: true });
}

return true;
}

/**
* Translate type with a symbol
* @param type - string, resource type
* @return string - translated type
* @throws redirect to error 400
*/
private _translateType(type: string) {
if (type in this._types) {
return this._types[type];
}
this._router.navigate(['/errors/400'], { skipLocationChange: true });
}
}
Loading

0 comments on commit b7b55ba

Please sign in to comment.