mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
[CST-5668] Address feedback and add test for orcid-auth.component
This commit is contained in:
@@ -12,6 +12,7 @@ import { RequestService } from '../data/request.service';
|
|||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
import { buildPaginatedList } from '../data/paginated-list.model';
|
import { buildPaginatedList } from '../data/paginated-list.model';
|
||||||
import {
|
import {
|
||||||
|
createFailedRemoteDataObject$,
|
||||||
createNoContentRemoteDataObject$,
|
createNoContentRemoteDataObject$,
|
||||||
createSuccessfulRemoteDataObject,
|
createSuccessfulRemoteDataObject,
|
||||||
createSuccessfulRemoteDataObject$
|
createSuccessfulRemoteDataObject$
|
||||||
@@ -22,12 +23,14 @@ import { ResearcherProfileService } from './researcher-profile.service';
|
|||||||
import { RouterMock } from '../../shared/mocks/router.mock';
|
import { RouterMock } from '../../shared/mocks/router.mock';
|
||||||
import { ResearcherProfile } from './model/researcher-profile.model';
|
import { ResearcherProfile } from './model/researcher-profile.model';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import { ReplaceOperation } from 'fast-json-patch';
|
import { RemoveOperation, ReplaceOperation } from 'fast-json-patch';
|
||||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
import { PostRequest } from '../data/request.models';
|
import { PostRequest } from '../data/request.models';
|
||||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { ConfigurationProperty } from '../shared/configuration-property.model';
|
import { ConfigurationProperty } from '../shared/configuration-property.model';
|
||||||
import { ConfigurationDataService } from '../data/configuration-data.service';
|
import { ConfigurationDataService } from '../data/configuration-data.service';
|
||||||
|
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
describe('ResearcherProfileService', () => {
|
describe('ResearcherProfileService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
@@ -89,6 +92,106 @@ describe('ResearcherProfileService', () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockItemUnlinkedToOrcid: Item = Object.assign(new Item(), {
|
||||||
|
id: 'mockItemUnlinkedToOrcid',
|
||||||
|
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [{
|
||||||
|
value: 'test person'
|
||||||
|
}],
|
||||||
|
'dspace.entity.type': [{
|
||||||
|
'value': 'Person'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockItemLinkedToOrcid: Item = Object.assign(new Item(), {
|
||||||
|
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [{
|
||||||
|
value: 'test person'
|
||||||
|
}],
|
||||||
|
'dspace.entity.type': [{
|
||||||
|
'value': 'Person'
|
||||||
|
}],
|
||||||
|
'dspace.object.owner': [{
|
||||||
|
'value': 'test person',
|
||||||
|
'language': null,
|
||||||
|
'authority': 'researcher-profile-id',
|
||||||
|
'confidence': 600,
|
||||||
|
'place': 0
|
||||||
|
}],
|
||||||
|
'dspace.orcid.authenticated': [{
|
||||||
|
'value': '2022-06-10T15:15:12.952872',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 0
|
||||||
|
}],
|
||||||
|
'dspace.orcid.scope': [{
|
||||||
|
'value': '/authenticate',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 0
|
||||||
|
}, {
|
||||||
|
'value': '/read-limited',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 1
|
||||||
|
}, {
|
||||||
|
'value': '/activities/update',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 2
|
||||||
|
}, {
|
||||||
|
'value': '/person/update',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 3
|
||||||
|
}],
|
||||||
|
'person.identifier.orcid': [{
|
||||||
|
'value': 'orcid-id',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 0
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const disconnectionAllowAdmin = {
|
||||||
|
uuid: 'orcid.disconnection.allowed-users',
|
||||||
|
name: 'orcid.disconnection.allowed-users',
|
||||||
|
values: ['only_admin']
|
||||||
|
} as ConfigurationProperty;
|
||||||
|
|
||||||
|
const disconnectionAllowAdminOwner = {
|
||||||
|
uuid: 'orcid.disconnection.allowed-users',
|
||||||
|
name: 'orcid.disconnection.allowed-users',
|
||||||
|
values: ['admin_and_owner']
|
||||||
|
} as ConfigurationProperty;
|
||||||
|
|
||||||
|
const authorizeUrl = {
|
||||||
|
uuid: 'orcid.authorize-url',
|
||||||
|
name: 'orcid.authorize-url',
|
||||||
|
values: ['orcid.authorize-url']
|
||||||
|
} as ConfigurationProperty;
|
||||||
|
const appClientId = {
|
||||||
|
uuid: 'orcid.application-client-id',
|
||||||
|
name: 'orcid.application-client-id',
|
||||||
|
values: ['orcid.application-client-id']
|
||||||
|
} as ConfigurationProperty;
|
||||||
|
const orcidScope = {
|
||||||
|
uuid: 'orcid.scope',
|
||||||
|
name: 'orcid.scope',
|
||||||
|
values: ['/authenticate', '/read-limited']
|
||||||
|
} as ConfigurationProperty;
|
||||||
|
|
||||||
const endpointURL = `https://rest.api/rest/api/profiles`;
|
const endpointURL = `https://rest.api/rest/api/profiles`;
|
||||||
const endpointURLWithEmbed = 'https://rest.api/rest/api/profiles?embed=item';
|
const endpointURLWithEmbed = 'https://rest.api/rest/api/profiles?embed=item';
|
||||||
const sourceUri = `https://rest.api/rest/api/external-source/profile`;
|
const sourceUri = `https://rest.api/rest/api/external-source/profile`;
|
||||||
@@ -140,12 +243,7 @@ describe('ResearcherProfileService', () => {
|
|||||||
findByHref: jasmine.createSpy('findByHref')
|
findByHref: jasmine.createSpy('findByHref')
|
||||||
});
|
});
|
||||||
configurationDataService = jasmine.createSpyObj('configurationDataService', {
|
configurationDataService = jasmine.createSpyObj('configurationDataService', {
|
||||||
findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), {
|
findByPropertyName: jasmine.createSpy('findByPropertyName')
|
||||||
name: 'test',
|
|
||||||
values: [
|
|
||||||
'org.dspace.ctask.general.ProfileFormats = test'
|
|
||||||
]
|
|
||||||
}))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
service = new ResearcherProfileService(
|
service = new ResearcherProfileService(
|
||||||
@@ -283,7 +381,7 @@ describe('ResearcherProfileService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('createFromExternalSource', () => {
|
describe('createFromExternalSource', () => {
|
||||||
let patchSpy;
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn((service as any).dataService, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
spyOn((service as any).dataService, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||||
spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||||
@@ -305,4 +403,140 @@ describe('ResearcherProfileService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isLinkedToOrcid', () => {
|
||||||
|
it('should return true when item has metadata', () => {
|
||||||
|
const result = service.isLinkedToOrcid(mockItemLinkedToOrcid);
|
||||||
|
expect(result).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when item has no metadata', () => {
|
||||||
|
const result = service.isLinkedToOrcid(mockItemUnlinkedToOrcid);
|
||||||
|
expect(result).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onlyAdminCanDisconnectProfileFromOrcid', () => {
|
||||||
|
it('should return true when property is only_admin', () => {
|
||||||
|
spyOn((service as any), 'getOrcidDisconnectionAllowedUsersConfiguration').and.returnValue(createSuccessfulRemoteDataObject$(disconnectionAllowAdmin));
|
||||||
|
const result = service.onlyAdminCanDisconnectProfileFromOrcid();
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: true
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false on faild', () => {
|
||||||
|
spyOn((service as any), 'getOrcidDisconnectionAllowedUsersConfiguration').and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
const result = service.onlyAdminCanDisconnectProfileFromOrcid();
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: false
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ownerCanDisconnectProfileFromOrcid', () => {
|
||||||
|
it('should return true when property is admin_and_owner', () => {
|
||||||
|
spyOn((service as any), 'getOrcidDisconnectionAllowedUsersConfiguration').and.returnValue(createSuccessfulRemoteDataObject$(disconnectionAllowAdminOwner));
|
||||||
|
const result = service.ownerCanDisconnectProfileFromOrcid();
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: true
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false on faild', () => {
|
||||||
|
spyOn((service as any), 'getOrcidDisconnectionAllowedUsersConfiguration').and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
const result = service.ownerCanDisconnectProfileFromOrcid();
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: false
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unlinkOrcid', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
spyOn((service as any).dataService, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||||
|
spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfile));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call patch method properly', () => {
|
||||||
|
const operations: RemoveOperation[] = [{
|
||||||
|
path: '/orcid',
|
||||||
|
op: 'remove'
|
||||||
|
}];
|
||||||
|
|
||||||
|
scheduler.schedule(() => service.unlinkOrcid(mockItemLinkedToOrcid).subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect((service as any).dataService.patch).toHaveBeenCalledWith(researcherProfile, operations);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOrcidAuthorizeUrl', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(service as any).configurationService.findByPropertyName.and.returnValues(
|
||||||
|
createSuccessfulRemoteDataObject$(authorizeUrl),
|
||||||
|
createSuccessfulRemoteDataObject$(appClientId),
|
||||||
|
createSuccessfulRemoteDataObject$(orcidScope)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build the url properly', () => {
|
||||||
|
const result = service.getOrcidAuthorizeUrl(mockItemUnlinkedToOrcid);
|
||||||
|
const redirectUri = environment.rest.baseUrl + '/api/eperson/orcid/' + mockItemUnlinkedToOrcid.id + '/?url=undefined';
|
||||||
|
const url = 'orcid.authorize-url?client_id=orcid.application-client-id&redirect_uri=' + redirectUri + '&response_type=code&scope=/authenticate /read-limited';
|
||||||
|
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: url
|
||||||
|
});
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateByOrcidOperations', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
spyOn((service as any).dataService, 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call patch method properly', () => {
|
||||||
|
scheduler.schedule(() => service.updateByOrcidOperations(researcherProfile, []).subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect((service as any).dataService.patch).toHaveBeenCalledWith(researcherProfile, []);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOrcidAuthorizationScopesByItem', () => {
|
||||||
|
it('should return list of scopes saved in the item', () => {
|
||||||
|
const orcidScopes = [
|
||||||
|
'/authenticate',
|
||||||
|
'/read-limited',
|
||||||
|
'/activities/update',
|
||||||
|
'/person/update'
|
||||||
|
];
|
||||||
|
const result = service.getOrcidAuthorizationScopesByItem(mockItemLinkedToOrcid);
|
||||||
|
expect(result).toEqual(orcidScopes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOrcidAuthorizationScopes', () => {
|
||||||
|
it('should return list of scopes by configuration', () => {
|
||||||
|
(service as any).configurationService.findByPropertyName.and.returnValue(
|
||||||
|
createSuccessfulRemoteDataObject$(orcidScope)
|
||||||
|
);
|
||||||
|
const orcidScopes = [
|
||||||
|
'/authenticate',
|
||||||
|
'/read-limited'
|
||||||
|
];
|
||||||
|
const expected = cold('(a|)', {
|
||||||
|
a: orcidScopes
|
||||||
|
});
|
||||||
|
const result = service.getOrcidAuthorizationScopes();
|
||||||
|
expect(result).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -32,7 +32,7 @@ import { ResearcherProfile } from './model/researcher-profile.model';
|
|||||||
import { RESEARCHER_PROFILE } from './model/researcher-profile.resource-type';
|
import { RESEARCHER_PROFILE } from './model/researcher-profile.resource-type';
|
||||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||||
import { PostRequest } from '../data/request.models';
|
import { PostRequest } from '../data/request.models';
|
||||||
import { hasValue, isEmpty } from '../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { CoreState } from '../core-state.model';
|
import { CoreState } from '../core-state.model';
|
||||||
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
@@ -171,7 +171,7 @@ export class ResearcherProfileService {
|
|||||||
* @param item the item to check
|
* @param item the item to check
|
||||||
* @returns the check result
|
* @returns the check result
|
||||||
*/
|
*/
|
||||||
isLinkedToOrcid(item: Item): boolean {
|
public isLinkedToOrcid(item: Item): boolean {
|
||||||
return item.hasMetadata('dspace.orcid.authenticated');
|
return item.hasMetadata('dspace.orcid.authenticated');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,9 +180,11 @@ export class ResearcherProfileService {
|
|||||||
*
|
*
|
||||||
* @returns the check result
|
* @returns the check result
|
||||||
*/
|
*/
|
||||||
onlyAdminCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
public onlyAdminCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
||||||
return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe(
|
return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe(
|
||||||
map((property) => property.values.map( (value) => value.toLowerCase()).includes('only_admin'))
|
map((propertyRD: RemoteData<ConfigurationProperty>) => {
|
||||||
|
return propertyRD.hasSucceeded && propertyRD.payload.values.map((value) => value.toLowerCase()).includes('only_admin');
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,25 +193,10 @@ export class ResearcherProfileService {
|
|||||||
*
|
*
|
||||||
* @returns the check result
|
* @returns the check result
|
||||||
*/
|
*/
|
||||||
ownerCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
public ownerCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
||||||
return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe(
|
return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe(
|
||||||
map((property) => {
|
map((propertyRD: RemoteData<ConfigurationProperty>) => {
|
||||||
const values = property.values.map( (value) => value.toLowerCase());
|
return propertyRD.hasSucceeded && propertyRD.payload.values.map( (value) => value.toLowerCase()).includes('admin_and_owner');
|
||||||
return values.includes('only_owner') || values.includes('admin_and_owner');
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the admin users can disconnect a researcher profile from ORCID.
|
|
||||||
*
|
|
||||||
* @returns the check result
|
|
||||||
*/
|
|
||||||
adminCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
|
||||||
return this.getOrcidDisconnectionAllowedUsersConfiguration().pipe(
|
|
||||||
map((property) => {
|
|
||||||
const values = property.values.map( (value) => value.toLowerCase());
|
|
||||||
return values.includes('only_admin') || values.includes('admin_and_owner');
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -217,8 +204,7 @@ export class ResearcherProfileService {
|
|||||||
/**
|
/**
|
||||||
* If the given item represents a profile unlink it from ORCID.
|
* If the given item represents a profile unlink it from ORCID.
|
||||||
*/
|
*/
|
||||||
unlinkOrcid(item: Item): Observable<RemoteData<ResearcherProfile>> {
|
public unlinkOrcid(item: Item): Observable<RemoteData<ResearcherProfile>> {
|
||||||
|
|
||||||
const operations: RemoveOperation[] = [{
|
const operations: RemoveOperation[] = [{
|
||||||
path:'/orcid',
|
path:'/orcid',
|
||||||
op:'remove'
|
op:'remove'
|
||||||
@@ -231,7 +217,12 @@ export class ResearcherProfileService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrcidAuthorizeUrl(profile: Item): Observable<string> {
|
/**
|
||||||
|
* Build and return the url to authenticate with orcid
|
||||||
|
*
|
||||||
|
* @param profile
|
||||||
|
*/
|
||||||
|
public getOrcidAuthorizeUrl(profile: Item): Observable<string> {
|
||||||
return combineLatest([
|
return combineLatest([
|
||||||
this.configurationService.findByPropertyName('orcid.authorize-url').pipe(getFirstSucceededRemoteDataPayload()),
|
this.configurationService.findByPropertyName('orcid.authorize-url').pipe(getFirstSucceededRemoteDataPayload()),
|
||||||
this.configurationService.findByPropertyName('orcid.application-client-id').pipe(getFirstSucceededRemoteDataPayload()),
|
this.configurationService.findByPropertyName('orcid.application-client-id').pipe(getFirstSucceededRemoteDataPayload()),
|
||||||
@@ -278,9 +269,28 @@ export class ResearcherProfileService {
|
|||||||
return this.dataService.patch(researcherProfile, operations);
|
return this.dataService.patch(researcherProfile, operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrcidDisconnectionAllowedUsersConfiguration(): Observable<ConfigurationProperty> {
|
/**
|
||||||
|
* Return all orcid authorization scopes saved in the given item
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
public getOrcidAuthorizationScopesByItem(item: Item): string[] {
|
||||||
|
return isNotEmpty(item) ? item.allMetadataValues('dspace.orcid.scope') : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all orcid authorization scopes available by configuration
|
||||||
|
*/
|
||||||
|
public getOrcidAuthorizationScopes(): Observable<string[]> {
|
||||||
|
return this.configurationService.findByPropertyName('orcid.scope').pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((propertyRD: RemoteData<ConfigurationProperty>) => propertyRD.hasSucceeded ? propertyRD.payload.values : [])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOrcidDisconnectionAllowedUsersConfiguration(): Observable<RemoteData<ConfigurationProperty>> {
|
||||||
return this.configurationService.findByPropertyName('orcid.disconnection.allowed-users').pipe(
|
return this.configurationService.findByPropertyName('orcid.disconnection.allowed-users').pipe(
|
||||||
getFirstSucceededRemoteDataPayload()
|
getFirstCompletedRemoteData()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,75 +3,89 @@
|
|||||||
<ngb-panel title="{{'person.orcid.registry.auth' | translate}}" id="auth">
|
<ngb-panel title="{{'person.orcid.registry.auth' | translate}}" id="auth">
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<ng-container *ngIf="isLinkedToOrcid(); then orcidLinked; else orcidNotLinked"></ng-container>
|
<ng-container *ngIf="(isLinkedToOrcid() | async); then orcidLinked; else orcidNotLinked"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ngb-panel>
|
</ngb-panel>
|
||||||
</ngb-accordion>
|
</ngb-accordion>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-template #orcidLinked>
|
<ng-template #orcidLinked>
|
||||||
<div class="row mt-3">
|
<div data-test="orcidLinked">
|
||||||
<div *ngIf="getOrcidAuthorizations().length > 0" class="card m-2 col-md p-0">
|
<div class="row mb-3">
|
||||||
<div class="card-header">{{ 'person.page.orcid.granted-authorizations'| translate }}</div>
|
<div *ngIf="(hasOrcidAuthorizations() | async)" class="col-sm-6" data-test="hasOrcidAuthorizations">
|
||||||
<div class="card-body">
|
<div class="card h-100">
|
||||||
<div class="container">
|
<div class="card-header">{{ 'person.page.orcid.granted-authorizations'| translate }}</div>
|
||||||
<ul>
|
<div class="card-body">
|
||||||
<li *ngFor="let auth of getOrcidAuthorizations()"> {{getAuthorizationDescription(auth) | translate}} </li>
|
<div class="container p-0">
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card m-2 col-md p-0">
|
|
||||||
<div class="card-header">{{ 'person.page.orcid.missing-authorizations'| translate }}</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="container">
|
|
||||||
<div *ngIf="( missingAuthorizations$ | async ).length === 0" class="p-3 mb-2 alert alert-success rounded">
|
|
||||||
{{'person.page.orcid.no-missing-authorizations-message' | translate}}
|
|
||||||
</div>
|
|
||||||
<div *ngIf="( missingAuthorizations$ | async ).length > 0" class="p-3 mb-2 alert alert-warning rounded">
|
|
||||||
{{'person.page.orcid.missing-authorizations-message' | translate}}
|
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li *ngFor="let auth of (getOrcidAuthorizations() | async)" data-test="orcidAuthorization">
|
||||||
*ngFor="let auth of ( ( missingAuthorizations$ | async ) )"> {{getAuthorizationDescription(auth) | translate }} </li>
|
{{getAuthorizationDescription(auth) | translate}}
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-header">{{ 'person.page.orcid.missing-authorizations'| translate }}</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="container">
|
||||||
|
<ds-alert *ngIf="!(hasMissingOrcidAuthorizations() | async)" [type]="'alert-success'" data-test="noMissingOrcidAuthorizations">
|
||||||
|
{{'person.page.orcid.no-missing-authorizations-message' | translate}}
|
||||||
|
</ds-alert>
|
||||||
|
<ds-alert *ngIf="(hasMissingOrcidAuthorizations() | async)" [type]="'alert-warning'" data-test="missingOrcidAuthorizations">
|
||||||
|
{{'person.page.orcid.missing-authorizations-message' | translate}}
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let auth of (getMissingOrcidAuthorizations() | async)" data-test="missingOrcidAuthorization">
|
||||||
|
{{getAuthorizationDescription(auth) | translate }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</ds-alert>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="(onlyAdminCanDisconnectProfileFromOrcid() | async)"
|
<ds-alert *ngIf="(onlyAdminCanDisconnectProfileFromOrcid() | async) && !(ownerCanDisconnectProfileFromOrcid() | async)"
|
||||||
class="row mt-3 p-3 mb-2 bg-info text-white rounded d-flex justify-content-center">
|
[type]="'alert-warning'" data-test="unlinkOnlyAdmin">
|
||||||
{{ 'person.page.orcid.remove-orcid-message' | translate}}
|
{{ 'person.page.orcid.remove-orcid-message' | translate}}
|
||||||
</div>
|
</ds-alert>
|
||||||
<div class="row" *ngIf="(ownerCanDisconnectProfileFromOrcid() | async)">
|
<div class="row" *ngIf="(ownerCanDisconnectProfileFromOrcid() | async)" data-test="unlinkOwner">
|
||||||
<div class="col-md">
|
<div class="col">
|
||||||
<button type="submit" class="btn btn-danger float-right m-2" (click)="unlinkOrcid()"
|
<button type="submit" class="btn btn-danger float-right" (click)="unlinkOrcid()"
|
||||||
[disabled]=unlinkProcessing>
|
[disabled]="(unlinkProcessing | async)">
|
||||||
<span *ngIf="!unlinkProcessing"><i
|
<span *ngIf="!(unlinkProcessing | async)"><i
|
||||||
class="fas fa-unlink"></i> {{ 'person.page.orcid.unlink' | translate }}</span>
|
class="fas fa-unlink"></i> {{ 'person.page.orcid.unlink' | translate }}</span>
|
||||||
<span *ngIf="unlinkProcessing"><i
|
<span *ngIf="(unlinkProcessing | async)"><i
|
||||||
class='fas fa-circle-notch fa-spin'></i> {{'person.page.orcid.unlink.processing' | translate}}</span>
|
class='fas fa-circle-notch fa-spin'></i> {{'person.page.orcid.unlink.processing' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="( missingAuthorizations$ | async ).length > 0" type="submit"
|
<button *ngIf="(hasMissingOrcidAuthorizations() | async)" type="submit"
|
||||||
class="btn btn-primary float-right m-2" (click)="linkOrcid()">
|
class="btn btn-primary float-right" (click)="linkOrcid()">
|
||||||
<span><i class="fas fa-check"></i> {{ 'person.page.orcid.grant-authorizations' | translate }}</span>
|
<span><i class="fas fa-check"></i> {{ 'person.page.orcid.grant-authorizations' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #orcidNotLinked>
|
<ng-template #orcidNotLinked>
|
||||||
|
<div data-test="orcidNotLinked">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-2"><img alt="orcid-logo" src="../../../../assets/images/orcid.logo.icon.svg"/></div>
|
<div class="col-2"><img alt="orcid-logo" src="../../../../assets/images/orcid.logo.icon.svg"/></div>
|
||||||
<div class="alert alert-info col">{{ getOrcidNotLinkedMessage() | async }}</div>
|
<div class="col">
|
||||||
|
<ds-alert [type]="'alert-info'">{{ getOrcidNotLinkedMessage() | async }}</ds-alert>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col p-0">
|
<div class="col">
|
||||||
<button class="btn btn-primary float-right" (click)="linkOrcid()">
|
<button class="btn btn-primary float-right" (click)="linkOrcid()">
|
||||||
<i class="fas fa-link"></i>
|
<i class="fas fa-link"></i>
|
||||||
{{'person.page.orcid.link' | translate}}
|
{{'person.page.orcid.link' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</div>
|
||||||
</div>
|
</ng-template>
|
||||||
|
|
||||||
|
@@ -0,0 +1,336 @@
|
|||||||
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { ResearcherProfileService } from '../../../core/profile/researcher-profile.service';
|
||||||
|
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
||||||
|
import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock';
|
||||||
|
import { OrcidAuthComponent } from './orcid-auth.component';
|
||||||
|
import { NativeWindowService } from '../../../core/services/window.service';
|
||||||
|
import { NativeWindowMockFactory } from '../../../shared/mocks/mock-native-window-ref';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
|
||||||
|
import { ResearcherProfile } from '../../../core/profile/model/researcher-profile.model';
|
||||||
|
|
||||||
|
describe('OrcidAuthComponent test suite', () => {
|
||||||
|
let comp: OrcidAuthComponent;
|
||||||
|
let fixture: ComponentFixture<OrcidAuthComponent>;
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
let researcherProfileService: jasmine.SpyObj<ResearcherProfileService>;
|
||||||
|
let nativeWindowRef;
|
||||||
|
let notificationsService;
|
||||||
|
|
||||||
|
const orcidScopes = [
|
||||||
|
'/authenticate',
|
||||||
|
'/read-limited',
|
||||||
|
'/activities/update',
|
||||||
|
'/person/update'
|
||||||
|
];
|
||||||
|
|
||||||
|
const partialOrcidScopes = [
|
||||||
|
'/authenticate',
|
||||||
|
'/read-limited',
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockItemUnlinkedToOrcid: Item = Object.assign(new Item(), {
|
||||||
|
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [{
|
||||||
|
value: 'test person'
|
||||||
|
}],
|
||||||
|
'dspace.entity.type': [{
|
||||||
|
'value': 'Person'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockItemLinkedToOrcid: Item = Object.assign(new Item(), {
|
||||||
|
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||||
|
metadata: {
|
||||||
|
'dc.title': [{
|
||||||
|
value: 'test person'
|
||||||
|
}],
|
||||||
|
'dspace.entity.type': [{
|
||||||
|
'value': 'Person'
|
||||||
|
}],
|
||||||
|
'dspace.object.owner': [{
|
||||||
|
'value': 'test person',
|
||||||
|
'language': null,
|
||||||
|
'authority': 'deced3e7-68e2-495d-bf98-7c44fc33b8ff',
|
||||||
|
'confidence': 600,
|
||||||
|
'place': 0
|
||||||
|
}],
|
||||||
|
'dspace.orcid.authenticated': [{
|
||||||
|
'value': '2022-06-10T15:15:12.952872',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 0
|
||||||
|
}],
|
||||||
|
'dspace.orcid.scope': [{
|
||||||
|
'value': '/authenticate',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 0
|
||||||
|
}, {
|
||||||
|
'value': '/read-limited',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 1
|
||||||
|
}, {
|
||||||
|
'value': '/activities/update',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 2
|
||||||
|
}, {
|
||||||
|
'value': '/person/update',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 3
|
||||||
|
}],
|
||||||
|
'person.identifier.orcid': [{
|
||||||
|
'value': 'orcid-id',
|
||||||
|
'language': null,
|
||||||
|
'authority': null,
|
||||||
|
'confidence': -1,
|
||||||
|
'place': 0
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
researcherProfileService = jasmine.createSpyObj('researcherProfileService', {
|
||||||
|
getOrcidAuthorizationScopes: jasmine.createSpy('getOrcidAuthorizationScopes'),
|
||||||
|
getOrcidAuthorizationScopesByItem: jasmine.createSpy('getOrcidAuthorizationScopesByItem'),
|
||||||
|
getOrcidAuthorizeUrl: jasmine.createSpy('getOrcidAuthorizeUrl'),
|
||||||
|
isLinkedToOrcid: jasmine.createSpy('isLinkedToOrcid'),
|
||||||
|
onlyAdminCanDisconnectProfileFromOrcid: jasmine.createSpy('onlyAdminCanDisconnectProfileFromOrcid'),
|
||||||
|
ownerCanDisconnectProfileFromOrcid: jasmine.createSpy('ownerCanDisconnectProfileFromOrcid'),
|
||||||
|
unlinkOrcid: jasmine.createSpy('unlinkOrcid')
|
||||||
|
});
|
||||||
|
|
||||||
|
void TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
NgbAccordionModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RouterTestingModule.withRoutes([])
|
||||||
|
],
|
||||||
|
declarations: [OrcidAuthComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory },
|
||||||
|
{ provide: NotificationsService, useClass: NotificationsServiceStub },
|
||||||
|
{ provide: ResearcherProfileService, useValue: researcherProfileService }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).overrideComponent(OrcidAuthComponent, {
|
||||||
|
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
fixture = TestBed.createComponent(OrcidAuthComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
researcherProfileService.getOrcidAuthorizationScopes.and.returnValue(of(orcidScopes));
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('when orcid profile is not linked', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemUnlinkedToOrcid;
|
||||||
|
researcherProfileService.getOrcidAuthorizationScopesByItem.and.returnValue([]);
|
||||||
|
researcherProfileService.isLinkedToOrcid.and.returnValue(false);
|
||||||
|
researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid.and.returnValue(of(false));
|
||||||
|
researcherProfileService.ownerCanDisconnectProfileFromOrcid.and.returnValue(of(true));
|
||||||
|
researcherProfileService.getOrcidAuthorizeUrl.and.returnValue(of('oarcidUrl'));
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create', fakeAsync(() => {
|
||||||
|
const orcidLinked = fixture.debugElement.query(By.css('[data-test="orcidLinked"]'));
|
||||||
|
const orcidNotLinked = fixture.debugElement.query(By.css('[data-test="orcidNotLinked"]'));
|
||||||
|
expect(orcidLinked).toBeFalsy();
|
||||||
|
expect(orcidNotLinked).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should change location on link', () => {
|
||||||
|
nativeWindowRef = (comp as any)._window;
|
||||||
|
scheduler.schedule(() => comp.linkOrcid());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(nativeWindowRef.nativeWindow.location.href).toBe('oarcidUrl');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when orcid profile is linked', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
researcherProfileService.isLinkedToOrcid.and.returnValue(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('', () => {
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
notificationsService = (comp as any).notificationsService;
|
||||||
|
researcherProfileService.getOrcidAuthorizationScopesByItem.and.returnValue([...orcidScopes]);
|
||||||
|
researcherProfileService.isLinkedToOrcid.and.returnValue(true);
|
||||||
|
researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid.and.returnValue(of(false));
|
||||||
|
researcherProfileService.ownerCanDisconnectProfileFromOrcid.and.returnValue(of(true));
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('and unlink is successfully', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
researcherProfileService.unlinkOrcid.and.returnValue(createSuccessfulRemoteDataObject$(new ResearcherProfile()));
|
||||||
|
spyOn(comp.unlink, 'emit');
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should show success notification', () => {
|
||||||
|
scheduler.schedule(() => comp.unlinkOrcid());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(notificationsService.success).toHaveBeenCalled();
|
||||||
|
expect(comp.unlink.emit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and unlink is failed', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
researcherProfileService.unlinkOrcid.and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should show success notification', () => {
|
||||||
|
scheduler.schedule(() => comp.unlinkOrcid());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and has orcid authorization scopes', () => {
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
researcherProfileService.getOrcidAuthorizationScopesByItem.and.returnValue([...orcidScopes]);
|
||||||
|
researcherProfileService.isLinkedToOrcid.and.returnValue(true);
|
||||||
|
researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid.and.returnValue(of(false));
|
||||||
|
researcherProfileService.ownerCanDisconnectProfileFromOrcid.and.returnValue(of(true));
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create', fakeAsync(() => {
|
||||||
|
const orcidLinked = fixture.debugElement.query(By.css('[data-test="orcidLinked"]'));
|
||||||
|
const orcidNotLinked = fixture.debugElement.query(By.css('[data-test="orcidNotLinked"]'));
|
||||||
|
expect(orcidLinked).toBeTruthy();
|
||||||
|
expect(orcidNotLinked).toBeFalsy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display orcid authorizations', fakeAsync(() => {
|
||||||
|
const orcidAuthorizations = fixture.debugElement.query(By.css('[data-test="hasOrcidAuthorizations"]'));
|
||||||
|
const noMissingOrcidAuthorizations = fixture.debugElement.query(By.css('[data-test="noMissingOrcidAuthorizations"]'));
|
||||||
|
const orcidAuthorizationsList = fixture.debugElement.queryAll(By.css('[data-test="orcidAuthorization"]'));
|
||||||
|
|
||||||
|
expect(orcidAuthorizations).toBeTruthy();
|
||||||
|
expect(noMissingOrcidAuthorizations).toBeTruthy();
|
||||||
|
expect(orcidAuthorizationsList.length).toBe(4);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and has missing orcid authorization scopes', () => {
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
researcherProfileService.getOrcidAuthorizationScopesByItem.and.returnValue([...partialOrcidScopes]);
|
||||||
|
researcherProfileService.isLinkedToOrcid.and.returnValue(true);
|
||||||
|
researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid.and.returnValue(of(false));
|
||||||
|
researcherProfileService.ownerCanDisconnectProfileFromOrcid.and.returnValue(of(true));
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create', fakeAsync(() => {
|
||||||
|
const orcidLinked = fixture.debugElement.query(By.css('[data-test="orcidLinked"]'));
|
||||||
|
const orcidNotLinked = fixture.debugElement.query(By.css('[data-test="orcidNotLinked"]'));
|
||||||
|
expect(orcidLinked).toBeTruthy();
|
||||||
|
expect(orcidNotLinked).toBeFalsy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display orcid authorizations', fakeAsync(() => {
|
||||||
|
const orcidAuthorizations = fixture.debugElement.query(By.css('[data-test="hasOrcidAuthorizations"]'));
|
||||||
|
const missingOrcidAuthorizations = fixture.debugElement.query(By.css('[data-test="missingOrcidAuthorizations"]'));
|
||||||
|
const orcidAuthorizationsList = fixture.debugElement.queryAll(By.css('[data-test="orcidAuthorization"]'));
|
||||||
|
const missingOrcidAuthorizationsList = fixture.debugElement.queryAll(By.css('[data-test="missingOrcidAuthorization"]'));
|
||||||
|
|
||||||
|
expect(orcidAuthorizations).toBeTruthy();
|
||||||
|
expect(missingOrcidAuthorizations).toBeTruthy();
|
||||||
|
expect(orcidAuthorizationsList.length).toBe(2);
|
||||||
|
expect(missingOrcidAuthorizationsList.length).toBe(2);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and only admin can unlink scopes', () => {
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
researcherProfileService.getOrcidAuthorizationScopesByItem.and.returnValue([...orcidScopes]);
|
||||||
|
researcherProfileService.isLinkedToOrcid.and.returnValue(true);
|
||||||
|
researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid.and.returnValue(of(true));
|
||||||
|
researcherProfileService.ownerCanDisconnectProfileFromOrcid.and.returnValue(of(false));
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display warning panel', fakeAsync(() => {
|
||||||
|
const unlinkOnlyAdmin = fixture.debugElement.query(By.css('[data-test="unlinkOnlyAdmin"]'));
|
||||||
|
const unlinkOwner = fixture.debugElement.query(By.css('[data-test="unlinkOwner"]'));
|
||||||
|
expect(unlinkOnlyAdmin).toBeTruthy();
|
||||||
|
expect(unlinkOwner).toBeFalsy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and owner can unlink scopes', () => {
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
comp.item = mockItemLinkedToOrcid;
|
||||||
|
researcherProfileService.getOrcidAuthorizationScopesByItem.and.returnValue([...orcidScopes]);
|
||||||
|
researcherProfileService.isLinkedToOrcid.and.returnValue(true);
|
||||||
|
researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid.and.returnValue(of(true));
|
||||||
|
researcherProfileService.ownerCanDisconnectProfileFromOrcid.and.returnValue(of(true));
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display warning panel', fakeAsync(() => {
|
||||||
|
const unlinkOnlyAdmin = fixture.debugElement.query(By.css('[data-test="unlinkOnlyAdmin"]'));
|
||||||
|
const unlinkOwner = fixture.debugElement.query(By.css('[data-test="unlinkOwner"]'));
|
||||||
|
expect(unlinkOnlyAdmin).toBeFalsy();
|
||||||
|
expect(unlinkOwner).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@@ -1,59 +1,126 @@
|
|||||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
import { Component, EventEmitter, Inject, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { ConfigurationDataService } from '../../../core/data/configuration-data.service';
|
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
import { ResearcherProfileService } from '../../../core/profile/researcher-profile.service';
|
import { ResearcherProfileService } from '../../../core/profile/researcher-profile.service';
|
||||||
import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service';
|
import { NativeWindowRef, NativeWindowService } from '../../../core/services/window.service';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { ResearcherProfile } from '../../../core/profile/model/researcher-profile.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-orcid-auth',
|
selector: 'ds-orcid-auth',
|
||||||
templateUrl: './orcid-auth.component.html',
|
templateUrl: './orcid-auth.component.html',
|
||||||
styleUrls: ['./orcid-auth.component.scss']
|
styleUrls: ['./orcid-auth.component.scss']
|
||||||
})
|
})
|
||||||
export class OrcidAuthComponent implements OnInit {
|
export class OrcidAuthComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The item for which showing the orcid settings
|
* The item for which showing the orcid settings
|
||||||
*/
|
*/
|
||||||
@Input() item: Item;
|
@Input() item: Item;
|
||||||
|
|
||||||
missingAuthorizations$ = new BehaviorSubject<string[]>([]);
|
/**
|
||||||
|
* The list of exposed orcid authorization scopes for the orcid profile
|
||||||
|
*/
|
||||||
|
profileAuthorizationScopes: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]);
|
||||||
|
|
||||||
unlinkProcessing = false;
|
/**
|
||||||
|
* The list of all orcid authorization scopes missing in the orcid profile
|
||||||
|
*/
|
||||||
|
missingAuthorizationScopes: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of all orcid authorization scopes available
|
||||||
|
*/
|
||||||
|
orcidAuthorizationScopes: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if unlink operation is processing
|
||||||
|
*/
|
||||||
|
unlinkProcessing: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if orcid profile is linked
|
||||||
|
*/
|
||||||
|
private isOrcidLinked$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if only admin can disconnect orcid profile
|
||||||
|
*/
|
||||||
|
private onlyAdminCanDisconnectProfileFromOrcid$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if owner can disconnect orcid profile
|
||||||
|
*/
|
||||||
|
private ownerCanDisconnectProfileFromOrcid$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event emitted when orcid profile is unliked successfully
|
||||||
|
*/
|
||||||
|
@Output() unlink: EventEmitter<void> = new EventEmitter<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private configurationService: ConfigurationDataService,
|
|
||||||
private researcherProfileService: ResearcherProfileService,
|
private researcherProfileService: ResearcherProfileService,
|
||||||
protected translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private itemService: ItemDataService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const scopes = this.getOrcidAuthorizations();
|
this.researcherProfileService.getOrcidAuthorizationScopes().subscribe((scopes: string[]) => {
|
||||||
return this.configurationService.findByPropertyName('orcid.scope')
|
this.orcidAuthorizationScopes.next(scopes);
|
||||||
.pipe(getFirstSucceededRemoteDataPayload(),
|
this.initOrcidAuthSettings();
|
||||||
map((configurationProperty) => configurationProperty.values),
|
});
|
||||||
map((allScopes) => allScopes.filter((scope) => !scopes.includes(scope))))
|
|
||||||
.subscribe((missingScopes) => this.missingAuthorizations$.next(missingScopes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrcidAuthorizations(): string[] {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
return this.item.allMetadataValues('dspace.orcid.scope');
|
if (!changes.item.isFirstChange() && changes.item.currentValue !== changes.item.previousValue) {
|
||||||
|
this.initOrcidAuthSettings();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isLinkedToOrcid(): boolean {
|
/**
|
||||||
return this.researcherProfileService.isLinkedToOrcid(this.item);
|
* Check if the list of exposed orcid authorization scopes for the orcid profile has values
|
||||||
|
*/
|
||||||
|
hasOrcidAuthorizations(): Observable<boolean> {
|
||||||
|
return this.profileAuthorizationScopes.asObservable().pipe(
|
||||||
|
map((scopes: string[]) => scopes.length > 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of exposed orcid authorization scopes for the orcid profile
|
||||||
|
*/
|
||||||
|
getOrcidAuthorizations(): Observable<string[]> {
|
||||||
|
return this.profileAuthorizationScopes.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the list of exposed orcid authorization scopes for the orcid profile has values
|
||||||
|
*/
|
||||||
|
hasMissingOrcidAuthorizations(): Observable<boolean> {
|
||||||
|
return this.missingAuthorizationScopes.asObservable().pipe(
|
||||||
|
map((scopes: string[]) => scopes.length > 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of exposed orcid authorization scopes for the orcid profile
|
||||||
|
*/
|
||||||
|
getMissingOrcidAuthorizations(): Observable<string[]> {
|
||||||
|
return this.profileAuthorizationScopes.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a boolean representing if orcid profile is linked
|
||||||
|
*/
|
||||||
|
isLinkedToOrcid(): Observable<boolean> {
|
||||||
|
return this.isOrcidLinked$.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrcidNotLinkedMessage(): Observable<string> {
|
getOrcidNotLinkedMessage(): Observable<string> {
|
||||||
@@ -65,34 +132,85 @@ export class OrcidAuthComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get label for a given orcid authorization scope
|
||||||
|
*
|
||||||
|
* @param scope
|
||||||
|
*/
|
||||||
getAuthorizationDescription(scope: string) {
|
getAuthorizationDescription(scope: string) {
|
||||||
return 'person.page.orcid.scope.' + scope.substring(1).replace('/', '-');
|
return 'person.page.orcid.scope.' + scope.substring(1).replace('/', '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a boolean representing if only admin can disconnect orcid profile
|
||||||
|
*/
|
||||||
onlyAdminCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
onlyAdminCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
||||||
return this.researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid();
|
return this.onlyAdminCanDisconnectProfileFromOrcid$.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a boolean representing if owner can disconnect orcid profile
|
||||||
|
*/
|
||||||
ownerCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
ownerCanDisconnectProfileFromOrcid(): Observable<boolean> {
|
||||||
return this.researcherProfileService.ownerCanDisconnectProfileFromOrcid();
|
return this.ownerCanDisconnectProfileFromOrcid$.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link existing person profile with orcid
|
||||||
|
*/
|
||||||
linkOrcid(): void {
|
linkOrcid(): void {
|
||||||
this.researcherProfileService.getOrcidAuthorizeUrl(this.item).subscribe((authorizeUrl) => {
|
this.researcherProfileService.getOrcidAuthorizeUrl(this.item).subscribe((authorizeUrl) => {
|
||||||
this._window.nativeWindow.location.href = authorizeUrl;
|
this._window.nativeWindow.location.href = authorizeUrl;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink existing person profile from orcid
|
||||||
|
*/
|
||||||
unlinkOrcid(): void {
|
unlinkOrcid(): void {
|
||||||
this.unlinkProcessing = true;
|
this.unlinkProcessing.next(true);
|
||||||
this.researcherProfileService.unlinkOrcid(this.item).subscribe((remoteData) => {
|
this.researcherProfileService.unlinkOrcid(this.item).subscribe((remoteData: RemoteData<ResearcherProfile>) => {
|
||||||
this.unlinkProcessing = false;
|
this.unlinkProcessing.next(false);
|
||||||
if (remoteData.isSuccess) {
|
if (remoteData.isSuccess) {
|
||||||
this.notificationsService.success(this.translateService.get('person.page.orcid.unlink.success'));
|
this.notificationsService.success(this.translateService.get('person.page.orcid.unlink.success'));
|
||||||
|
this.unlink.emit();
|
||||||
} else {
|
} else {
|
||||||
this.notificationsService.error(this.translateService.get('person.page.orcid.unlink.error'));
|
this.notificationsService.error(this.translateService.get('person.page.orcid.unlink.error'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize all Orcid authentication settings
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private initOrcidAuthSettings(): void {
|
||||||
|
|
||||||
|
this.setOrcidAuthorizationsFromItem();
|
||||||
|
|
||||||
|
this.setMissingOrcidAuthorizations();
|
||||||
|
|
||||||
|
this.researcherProfileService.onlyAdminCanDisconnectProfileFromOrcid().subscribe((result) => {
|
||||||
|
this.onlyAdminCanDisconnectProfileFromOrcid$.next(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.researcherProfileService.ownerCanDisconnectProfileFromOrcid().subscribe((result) => {
|
||||||
|
this.ownerCanDisconnectProfileFromOrcid$.next(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.isOrcidLinked$.next(this.researcherProfileService.isLinkedToOrcid(this.item));
|
||||||
|
}
|
||||||
|
|
||||||
|
private setMissingOrcidAuthorizations(): void {
|
||||||
|
const profileScopes = this.researcherProfileService.getOrcidAuthorizationScopesByItem(this.item);
|
||||||
|
const orcidScopes = this.orcidAuthorizationScopes.value;
|
||||||
|
const missingScopes = orcidScopes.filter((scope) => !profileScopes.includes(scope));
|
||||||
|
|
||||||
|
this.missingAuthorizationScopes.next(missingScopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setOrcidAuthorizationsFromItem(): void {
|
||||||
|
this.profileAuthorizationScopes.next(this.researcherProfileService.getOrcidAuthorizationScopesByItem(this.item));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div *ngIf="item" class="container">
|
<div *ngIf="(item | async)" class="container">
|
||||||
<div class="button-row bottom mb-3">
|
<div class="button-row bottom mb-3">
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a [routerLink]="getItemPage()" role="button" class="btn btn-outline-secondary" data-test="back-button">
|
<a [routerLink]="getItemPage()" role="button" class="btn btn-outline-secondary" data-test="back-button">
|
||||||
@@ -8,5 +8,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ds-orcid-auth *ngIf="item" [item]="item"></ds-orcid-auth>
|
<ds-orcid-auth *ngIf="(item | async)" [item]="(item | async)" (unlink)="updateItem()"></ds-orcid-auth>
|
||||||
<ds-orcid-setting *ngIf="item && isLinkedToOrcid()" [item]="item"></ds-orcid-setting>
|
<ds-orcid-setting *ngIf="(item | async) && isLinkedToOrcid()" [item]="(item | async)"></ds-orcid-setting>
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
@@ -15,14 +17,16 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } f
|
|||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||||
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
||||||
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
|
|
||||||
fdescribe('OrcidPageComponent test suite', () => {
|
describe('OrcidPageComponent test suite', () => {
|
||||||
let comp: OrcidPageComponent;
|
let comp: OrcidPageComponent;
|
||||||
let fixture: ComponentFixture<OrcidPageComponent>;
|
let fixture: ComponentFixture<OrcidPageComponent>;
|
||||||
|
let scheduler: TestScheduler;
|
||||||
let authService: jasmine.SpyObj<AuthService>;
|
let authService: jasmine.SpyObj<AuthService>;
|
||||||
let routeStub: jasmine.SpyObj<ActivatedRouteStub>;
|
let routeStub: jasmine.SpyObj<ActivatedRouteStub>;
|
||||||
let routeData: any;
|
let routeData: any;
|
||||||
|
let itemDataService: jasmine.SpyObj<ItemDataService>;
|
||||||
let researcherProfileService: jasmine.SpyObj<ResearcherProfileService>;
|
let researcherProfileService: jasmine.SpyObj<ResearcherProfileService>;
|
||||||
|
|
||||||
const mockItem: Item = Object.assign(new Item(), {
|
const mockItem: Item = Object.assign(new Item(), {
|
||||||
@@ -70,6 +74,10 @@ fdescribe('OrcidPageComponent test suite', () => {
|
|||||||
isLinkedToOrcid: jasmine.createSpy('isLinkedToOrcid')
|
isLinkedToOrcid: jasmine.createSpy('isLinkedToOrcid')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itemDataService = jasmine.createSpyObj('ItemDataService', {
|
||||||
|
findById: jasmine.createSpy('findById')
|
||||||
|
});
|
||||||
|
|
||||||
void TestBed.configureTestingModule({
|
void TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
@@ -85,6 +93,7 @@ fdescribe('OrcidPageComponent test suite', () => {
|
|||||||
{ provide: ActivatedRoute, useValue: routeStub },
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
{ provide: ResearcherProfileService, useValue: researcherProfileService },
|
{ provide: ResearcherProfileService, useValue: researcherProfileService },
|
||||||
{ provide: AuthService, useValue: authService },
|
{ provide: AuthService, useValue: authService },
|
||||||
|
{ provide: ItemDataService, useValue: itemDataService },
|
||||||
],
|
],
|
||||||
|
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
@@ -92,6 +101,7 @@ fdescribe('OrcidPageComponent test suite', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
fixture = TestBed.createComponent(OrcidPageComponent);
|
fixture = TestBed.createComponent(OrcidPageComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
authService.isAuthenticated.and.returnValue(observableOf(true));
|
authService.isAuthenticated.and.returnValue(observableOf(true));
|
||||||
@@ -107,7 +117,15 @@ fdescribe('OrcidPageComponent test suite', () => {
|
|||||||
it('should call isLinkedToOrcid', () => {
|
it('should call isLinkedToOrcid', () => {
|
||||||
comp.isLinkedToOrcid();
|
comp.isLinkedToOrcid();
|
||||||
|
|
||||||
expect(researcherProfileService.isLinkedToOrcid).toHaveBeenCalledWith(comp.item);
|
expect(researcherProfileService.isLinkedToOrcid).toHaveBeenCalledWith(comp.item.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update item', fakeAsync(() => {
|
||||||
|
itemDataService.findById.and.returnValue(createSuccessfulRemoteDataObject$(mockItemLinkedToOrcid));
|
||||||
|
scheduler.schedule(() => comp.updateItem());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(comp.item.value).toEqual(mockItemLinkedToOrcid);
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,15 +1,17 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { ResearcherProfileService } from '../../core/profile/researcher-profile.service';
|
import { ResearcherProfileService } from '../../core/profile/researcher-profile.service';
|
||||||
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { getItemPageRoute } from '../item-page-routing-paths';
|
import { getItemPageRoute } from '../item-page-routing-paths';
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
import { redirectOn4xx } from '../../core/shared/authorized.operators';
|
import { redirectOn4xx } from '../../core/shared/authorized.operators';
|
||||||
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component that represents the orcid settings page
|
* A component that represents the orcid settings page
|
||||||
@@ -24,10 +26,11 @@ export class OrcidPageComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The item for which showing the orcid settings
|
* The item for which showing the orcid settings
|
||||||
*/
|
*/
|
||||||
item: Item;
|
item: BehaviorSubject<Item> = new BehaviorSubject<Item>(null);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
|
private itemService: ItemDataService,
|
||||||
private researcherProfileService: ResearcherProfileService,
|
private researcherProfileService: ResearcherProfileService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router
|
private router: Router
|
||||||
@@ -43,7 +46,7 @@ export class OrcidPageComponent implements OnInit {
|
|||||||
redirectOn4xx(this.router, this.authService),
|
redirectOn4xx(this.router, this.authService),
|
||||||
getFirstSucceededRemoteDataPayload()
|
getFirstSucceededRemoteDataPayload()
|
||||||
).subscribe((item) => {
|
).subscribe((item) => {
|
||||||
this.item = item;
|
this.item.next(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,14 +56,27 @@ export class OrcidPageComponent implements OnInit {
|
|||||||
* @returns the check result
|
* @returns the check result
|
||||||
*/
|
*/
|
||||||
isLinkedToOrcid(): boolean {
|
isLinkedToOrcid(): boolean {
|
||||||
return this.researcherProfileService.isLinkedToOrcid(this.item);
|
return this.researcherProfileService.isLinkedToOrcid(this.item.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the route to an item's page
|
* Get the route to an item's page
|
||||||
*/
|
*/
|
||||||
getItemPage(): string {
|
getItemPage(): string {
|
||||||
return getItemPageRoute(this.item);
|
return getItemPageRoute(this.item.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the updated profile item
|
||||||
|
*/
|
||||||
|
updateItem(): void {
|
||||||
|
this.itemService.findById(this.item.value.id, false).pipe(
|
||||||
|
getFirstCompletedRemoteData()
|
||||||
|
).subscribe((itemRD: RemoteData<Item>) => {
|
||||||
|
if (itemRD.hasSucceeded) {
|
||||||
|
this.item.next(itemRD.payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user