mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
71429: Feature and Authentication Models and Services
This commit is contained in:
@@ -145,6 +145,10 @@ import { Version } from './shared/version.model';
|
|||||||
import { VersionHistory } from './shared/version-history.model';
|
import { VersionHistory } from './shared/version-history.model';
|
||||||
import { WorkflowActionDataService } from './data/workflow-action-data.service';
|
import { WorkflowActionDataService } from './data/workflow-action-data.service';
|
||||||
import { WorkflowAction } from './tasks/models/workflow-action-object.model';
|
import { WorkflowAction } from './tasks/models/workflow-action-object.model';
|
||||||
|
import { Feature } from './shared/feature.model';
|
||||||
|
import { Authorization } from './shared/authorization.model';
|
||||||
|
import { FeatureDataService } from './data/feature-authorization/feature-data.service';
|
||||||
|
import { AuthorizationDataService } from './data/feature-authorization/authorization-data.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When not in production, endpoint responses can be mocked for testing purposes
|
* When not in production, endpoint responses can be mocked for testing purposes
|
||||||
@@ -264,6 +268,8 @@ const PROVIDERS = [
|
|||||||
LicenseDataService,
|
LicenseDataService,
|
||||||
ItemTypeDataService,
|
ItemTypeDataService,
|
||||||
WorkflowActionDataService,
|
WorkflowActionDataService,
|
||||||
|
FeatureDataService,
|
||||||
|
AuthorizationDataService,
|
||||||
// register AuthInterceptor as HttpInterceptor
|
// register AuthInterceptor as HttpInterceptor
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
@@ -314,7 +320,9 @@ export const models =
|
|||||||
ExternalSourceEntry,
|
ExternalSourceEntry,
|
||||||
Version,
|
Version,
|
||||||
VersionHistory,
|
VersionHistory,
|
||||||
WorkflowAction
|
WorkflowAction,
|
||||||
|
Feature,
|
||||||
|
Authorization
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -0,0 +1,126 @@
|
|||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { AUTHORIZATION } from '../../shared/authorization.resource-type';
|
||||||
|
import { dataService } from '../../cache/builders/build-decorators';
|
||||||
|
import { DataService } from '../data.service';
|
||||||
|
import { Authorization } from '../../shared/authorization.model';
|
||||||
|
import { RequestService } from '../request.service';
|
||||||
|
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { CoreState } from '../../core.reducers';
|
||||||
|
import { ObjectCacheService } from '../../cache/object-cache.service';
|
||||||
|
import { HALEndpointService } from '../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { DSOChangeAnalyzer } from '../dso-change-analyzer.service';
|
||||||
|
import { AuthService } from '../../auth/auth.service';
|
||||||
|
import { SiteDataService } from '../site-data.service';
|
||||||
|
import { FindListOptions, FindListRequest } from '../request.models';
|
||||||
|
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { RemoteData } from '../remote-data';
|
||||||
|
import { PaginatedList } from '../paginated-list';
|
||||||
|
import { find, skipWhile, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||||
|
import { RequestParam } from '../../cache/models/request-param.model';
|
||||||
|
import { AuthorizationSearchParams } from './authorization-search-params';
|
||||||
|
import { addAuthenticatedUserUuidIfEmpty, addSiteObjectUrlIfEmpty } from './authorization-utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service to retrieve {@link Authorization}s from the REST API
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(AUTHORIZATION)
|
||||||
|
export class AuthorizationDataService extends DataService<Authorization> {
|
||||||
|
protected linkPath = 'authorizations';
|
||||||
|
protected searchByObjectPath = 'object';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DSOChangeAnalyzer<Authorization>,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected siteService: SiteDataService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for a list of {@link Authorization}s using the "object" search endpoint and providing optional object url,
|
||||||
|
* {@link EPerson} uuid and/or {@link Feature} id
|
||||||
|
* @param objectUrl URL to the object to search {@link Authorization}s for.
|
||||||
|
* If not provided, the repository's {@link Site} will be used.
|
||||||
|
* @param ePersonUuid UUID of the {@link EPerson} to search {@link Authorization}s for.
|
||||||
|
* If not provided, the UUID of the currently authenticated {@link EPerson} will be used.
|
||||||
|
* @param featureId ID of the {@link Feature} to search {@link Authorization}s for
|
||||||
|
* @param options {@link FindListOptions} to provide pagination and/or additional arguments
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
|
*/
|
||||||
|
searchByObject(objectUrl?: string, ePersonUuid?: string, featureId?: string, options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<Authorization>>): Observable<RemoteData<PaginatedList<Authorization>>> {
|
||||||
|
return observableOf(new AuthorizationSearchParams(objectUrl, ePersonUuid, featureId)).pipe(
|
||||||
|
addSiteObjectUrlIfEmpty(this.siteService),
|
||||||
|
addAuthenticatedUserUuidIfEmpty(this.authService),
|
||||||
|
switchMap((params: AuthorizationSearchParams) => {
|
||||||
|
return this.searchBy(this.searchByObjectPath, this.createSearchOptions(params.objectUrl, options, params.ePersonUuid, params.featureId), ...linksToFollow);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a new FindListRequest with given search method
|
||||||
|
*
|
||||||
|
* @param searchMethod The search method for the object
|
||||||
|
* @param options The [[FindListOptions]] object
|
||||||
|
* @param linksToFollow The array of [[FollowLinkConfig]]
|
||||||
|
* @return {Observable<RemoteData<PaginatedList<Authorization>>}
|
||||||
|
* Return an observable that emits response from the server
|
||||||
|
*/
|
||||||
|
searchBy(searchMethod: string, options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<Authorization>>): Observable<RemoteData<PaginatedList<Authorization>>> {
|
||||||
|
const hrefObs = this.getSearchByHref(searchMethod, options, ...linksToFollow);
|
||||||
|
|
||||||
|
return hrefObs.pipe(
|
||||||
|
find((href: string) => hasValue(href)),
|
||||||
|
tap((href: string) => {
|
||||||
|
this.requestService.removeByHrefSubstring(href);
|
||||||
|
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
||||||
|
|
||||||
|
this.requestService.configure(request);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
switchMap((href) => this.requestService.getByHref(href)),
|
||||||
|
skipWhile((requestEntry) => hasValue(requestEntry) && requestEntry.completed),
|
||||||
|
switchMap((href) =>
|
||||||
|
this.rdbService.buildList<Authorization>(hrefObs, ...linksToFollow) as Observable<RemoteData<PaginatedList<Authorization>>>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create {@link FindListOptions} with {@link RequestParam}s containing a "uri", "feature" and/or "eperson" parameter
|
||||||
|
* @param objectUrl Required parameter value to add to {@link RequestParam} "uri"
|
||||||
|
* @param options Optional initial {@link FindListOptions} to add parameters to
|
||||||
|
* @param ePersonUuid Optional parameter value to add to {@link RequestParam} "eperson"
|
||||||
|
* @param featureId Optional parameter value to add to {@link RequestParam} "feature"
|
||||||
|
*/
|
||||||
|
private createSearchOptions(objectUrl: string, options: FindListOptions = {}, ePersonUuid?: string, featureId?: string): FindListOptions {
|
||||||
|
let params = [];
|
||||||
|
if (isNotEmpty(options.searchParams)) {
|
||||||
|
params = [...options.searchParams];
|
||||||
|
}
|
||||||
|
params.push(new RequestParam('uri', objectUrl))
|
||||||
|
if (hasValue(featureId)) {
|
||||||
|
params.push(new RequestParam('feature', featureId));
|
||||||
|
}
|
||||||
|
if (hasValue(ePersonUuid)) {
|
||||||
|
params.push(new RequestParam('eperson', ePersonUuid));
|
||||||
|
}
|
||||||
|
return Object.assign(new FindListOptions(), options, {
|
||||||
|
searchParams: [...params]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
export class AuthorizationSearchParams {
|
||||||
|
objectUrl: string;
|
||||||
|
ePersonUuid: string;
|
||||||
|
featureId: string;
|
||||||
|
|
||||||
|
constructor(objectUrl?: string, ePersonUuid?: string, featureId?: string) {
|
||||||
|
this.objectUrl = objectUrl;
|
||||||
|
this.ePersonUuid = ePersonUuid;
|
||||||
|
this.featureId = featureId;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,53 @@
|
|||||||
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { AuthorizationSearchParams } from './authorization-search-params';
|
||||||
|
import { SiteDataService } from '../site-data.service';
|
||||||
|
import { hasNoValue } from '../../../shared/empty.util';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { AuthService } from '../../auth/auth.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator accepting {@link AuthorizationSearchParams} and adding the current {@link Site}'s selflink to the parameter's
|
||||||
|
* objectUrl property, if this property is empty
|
||||||
|
* @param siteService The {@link SiteDataService} used for retrieving the repository's {@link Site}
|
||||||
|
*/
|
||||||
|
export const addSiteObjectUrlIfEmpty = (siteService: SiteDataService) =>
|
||||||
|
(source: Observable<AuthorizationSearchParams>): Observable<AuthorizationSearchParams> =>
|
||||||
|
source.pipe(
|
||||||
|
switchMap((params: AuthorizationSearchParams) => {
|
||||||
|
if (hasNoValue(params.objectUrl)) {
|
||||||
|
return siteService.find().pipe(
|
||||||
|
map((site) => Object.assign({}, params, { objectUrl: site.self }))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return observableOf(params);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operator accepting {@link AuthorizationSearchParams} and adding the authenticated user's uuid to the parameter's
|
||||||
|
* ePersonUuid property, if this property is empty and an {@link EPerson} is currently authenticated
|
||||||
|
* @param authService The {@link AuthService} used for retrieving the currently authenticated {@link EPerson}
|
||||||
|
*/
|
||||||
|
export const addAuthenticatedUserUuidIfEmpty = (authService: AuthService) =>
|
||||||
|
(source: Observable<AuthorizationSearchParams>): Observable<AuthorizationSearchParams> =>
|
||||||
|
source.pipe(
|
||||||
|
switchMap((params: AuthorizationSearchParams) => {
|
||||||
|
if (hasNoValue(params.ePersonUuid)) {
|
||||||
|
return authService.isAuthenticated().pipe(
|
||||||
|
switchMap((authenticated) => {
|
||||||
|
if (authenticated) {
|
||||||
|
return authService.getAuthenticatedUserFromStore().pipe(
|
||||||
|
map((ePerson) => Object.assign({}, params, { ePersonUuid: ePerson.uuid }))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
observableOf(params)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return observableOf(params);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
@@ -0,0 +1,72 @@
|
|||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { FEATURE } from '../../shared/feature.resource-type';
|
||||||
|
import { dataService } from '../../cache/builders/build-decorators';
|
||||||
|
import { DataService } from '../data.service';
|
||||||
|
import { Feature } from '../../shared/feature.model';
|
||||||
|
import { RequestService } from '../request.service';
|
||||||
|
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { CoreState } from '../../core.reducers';
|
||||||
|
import { ObjectCacheService } from '../../cache/object-cache.service';
|
||||||
|
import { HALEndpointService } from '../../shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { DSOChangeAnalyzer } from '../dso-change-analyzer.service';
|
||||||
|
import { FindListOptions, FindListRequest } from '../request.models';
|
||||||
|
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||||
|
import { RemoteData } from '../remote-data';
|
||||||
|
import { PaginatedList } from '../paginated-list';
|
||||||
|
import { find, skipWhile, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service to retrieve {@link Feature}s from the REST API
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@dataService(FEATURE)
|
||||||
|
export class FeatureDataService extends DataService<Feature> {
|
||||||
|
protected linkPath = 'features';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DSOChangeAnalyzer<Feature>
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a new FindListRequest with given search method
|
||||||
|
*
|
||||||
|
* @param searchMethod The search method for the object
|
||||||
|
* @param options The [[FindListOptions]] object
|
||||||
|
* @param linksToFollow The array of [[FollowLinkConfig]]
|
||||||
|
* @return {Observable<RemoteData<PaginatedList<Feature>>}
|
||||||
|
* Return an observable that emits response from the server
|
||||||
|
*/
|
||||||
|
searchBy(searchMethod: string, options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<Feature>>): Observable<RemoteData<PaginatedList<Feature>>> {
|
||||||
|
const hrefObs = this.getSearchByHref(searchMethod, options, ...linksToFollow);
|
||||||
|
|
||||||
|
return hrefObs.pipe(
|
||||||
|
find((href: string) => hasValue(href)),
|
||||||
|
tap((href: string) => {
|
||||||
|
this.requestService.removeByHrefSubstring(href);
|
||||||
|
const request = new FindListRequest(this.requestService.generateRequestId(), href, options);
|
||||||
|
|
||||||
|
this.requestService.configure(request);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
switchMap((href) => this.requestService.getByHref(href)),
|
||||||
|
skipWhile((requestEntry) => hasValue(requestEntry) && requestEntry.completed),
|
||||||
|
switchMap((href) =>
|
||||||
|
this.rdbService.buildList<Feature>(hrefObs, ...linksToFollow) as Observable<RemoteData<PaginatedList<Feature>>>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
54
src/app/core/shared/authorization.model.ts
Normal file
54
src/app/core/shared/authorization.model.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { link, typedObject } from '../cache/builders/build-decorators';
|
||||||
|
import { AUTHORIZATION } from './authorization.resource-type';
|
||||||
|
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
|
||||||
|
import { HALLink } from './hal-link.model';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { EPerson } from '../eperson/models/eperson.model';
|
||||||
|
import { EPERSON } from '../eperson/models/eperson.resource-type';
|
||||||
|
import { FEATURE } from './feature.resource-type';
|
||||||
|
import { DSpaceObject } from './dspace-object.model';
|
||||||
|
import { Feature } from './feature.model';
|
||||||
|
import { ITEM } from './item.resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a DSpace Authorization
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
@inheritSerialization(DSpaceObject)
|
||||||
|
export class Authorization extends DSpaceObject {
|
||||||
|
static type = AUTHORIZATION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifier for this authorization
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink;
|
||||||
|
eperson: HALLink;
|
||||||
|
feature: HALLink;
|
||||||
|
object: HALLink;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The EPerson this Authorization belongs to
|
||||||
|
* Null if the authorization grants access to anonymous users
|
||||||
|
*/
|
||||||
|
@link(EPERSON)
|
||||||
|
eperson?: Observable<RemoteData<EPerson>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Feature enabled by this Authorization
|
||||||
|
*/
|
||||||
|
@link(FEATURE)
|
||||||
|
feature?: Observable<RemoteData<Feature>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Object this authorization applies to
|
||||||
|
*/
|
||||||
|
@link(ITEM)
|
||||||
|
object?: Observable<RemoteData<DSpaceObject>>;
|
||||||
|
}
|
9
src/app/core/shared/authorization.resource-type.ts
Normal file
9
src/app/core/shared/authorization.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from './resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for Authorization
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const AUTHORIZATION = new ResourceType('authorization');
|
31
src/app/core/shared/feature.model.ts
Normal file
31
src/app/core/shared/feature.model.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { typedObject } from '../cache/builders/build-decorators';
|
||||||
|
import { autoserialize, inheritSerialization } from 'cerialize';
|
||||||
|
import { FEATURE } from './feature.resource-type';
|
||||||
|
import { DSpaceObject } from './dspace-object.model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a DSpace Feature
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
@inheritSerialization(DSpaceObject)
|
||||||
|
export class Feature extends DSpaceObject {
|
||||||
|
static type = FEATURE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifier for this feature
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A human readable description of the feature's purpose
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of resource types this feature applies to
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
resourcetypes: string[];
|
||||||
|
}
|
9
src/app/core/shared/feature.resource-type.ts
Normal file
9
src/app/core/shared/feature.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from './resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for Feature
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const FEATURE = new ResourceType('feature');
|
Reference in New Issue
Block a user