Intermediate commit

This commit is contained in:
Giuseppe Digilio
2020-04-03 20:25:58 +02:00
parent 1ac52edbf7
commit 6314e17f27
19 changed files with 443 additions and 106 deletions

View File

@@ -1675,9 +1675,21 @@
"resource-policies.add.for.item": "Add a new Item policy", "resource-policies.add.for.item": "Add a new Item policy",
"resource-policies.create.modal.head": "Create new resource policy", "resource-policies.create.page.heading": "Create new resource policy for ",
"resource-policies.edit.modal.head": "Edit resource policy", "resource-policies.create.page.failure.content": "An error occurred while creating the resource policy.",
"resource-policies.create.page.success.content": "Operation successful",
"resource-policies.create.page.title": "Create new resource policy",
"resource-policies.edit.page.heading": "Edit resource policy ",
"resource-policies.edit.page.failure.content": "An error occurred while editing the resource policy.",
"resource-policies.edit.page.success.content": "Operation successful",
"resource-policies.edit.page.title": "Edit resource policy",
"resource-policies.form.action-type.label": "Select the action type", "resource-policies.form.action-type.label": "Select the action type",
@@ -1721,9 +1733,11 @@
"resource-policies.table.headers.group": "Group", "resource-policies.table.headers.group": "Group",
"resource-policies.table.headers.id": "ID",
"resource-policies.table.headers.name": "Name", "resource-policies.table.headers.name": "Name",
"resource-policies.table.headers.id": "ID", "resource-policies.table.headers.policyType": "type",
"resource-policies.table.headers.title.for.bitstream": "Policies for Bitstream", "resource-policies.table.headers.title.for.bitstream": "Policies for Bitstream",

View File

@@ -24,6 +24,8 @@ import { ItemMoveComponent } from './item-move/item-move.component';
import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.component'; import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.component';
import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component';
import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component'; import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component';
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
/** /**
* Module that contains all components related to the Edit Item page administrator functionality * Module that contains all components related to the Edit Item page administrator functionality
@@ -56,7 +58,9 @@ import { ItemAuthorizationsComponent } from './item-authorizations/item-authoriz
ItemCollectionMapperComponent, ItemCollectionMapperComponent,
ItemMoveComponent, ItemMoveComponent,
VirtualMetadataComponent, VirtualMetadataComponent,
ItemAuthorizationsComponent ItemAuthorizationsComponent,
ResourcePolicyEditComponent,
ResourcePolicyCreateComponent,
] ]
}) })
export class EditItemPageModule { export class EditItemPageModule {

View File

@@ -15,8 +15,10 @@ import { ItemRelationshipsComponent } from './item-relationships/item-relationsh
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component';
import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component'; import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component';
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component'; import { ResourcePolicyTargetResolver } from '../../shared/resource-policies/resolvers/resource-policy-target.resolver';
import { ResourcePolicyResolver } from '../../shared/resource-policies/resolvers/resource-policy.resolver';
import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component'; import { ResourcePolicyCreateComponent } from '../../shared/resource-policies/create/resource-policy-create.component';
import { ResourcePolicyEditComponent } from '../../shared/resource-policies/edit/resource-policy-edit.component';
export const ITEM_EDIT_WITHDRAW_PATH = 'withdraw'; export const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
export const ITEM_EDIT_REINSTATE_PATH = 'reinstate'; export const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
@@ -118,19 +120,27 @@ export const ITEM_EDIT_AUTHORIZATIONS_PATH = 'authorizations';
}, },
{ {
path: ITEM_EDIT_AUTHORIZATIONS_PATH, path: ITEM_EDIT_AUTHORIZATIONS_PATH,
data: { title: 'item.edit.authorizations.title' },
children: [ children: [
{ {
path: ':dso/create', path: 'create',
resolve: {
resourcePolicyTarget: ResourcePolicyTargetResolver
},
component: ResourcePolicyCreateComponent, component: ResourcePolicyCreateComponent,
data: { title: 'resource-policies.create.page.title' }
}, },
{ {
path: ':dso/:policy/edit', path: 'edit',
resolve: {
resourcePolicy: ResourcePolicyResolver
},
component: ResourcePolicyEditComponent, component: ResourcePolicyEditComponent,
data: { title: 'resource-policies.edit.page.title' }
}, },
{ {
path: '', path: '',
component: ItemAuthorizationsComponent component: ItemAuthorizationsComponent,
data: { title: 'item.edit.authorizations.title' }
} }
] ]
} }
@@ -138,7 +148,10 @@ export const ITEM_EDIT_AUTHORIZATIONS_PATH = 'authorizations';
} }
]) ])
], ],
providers: [] providers: [
ResourcePolicyResolver,
ResourcePolicyTargetResolver
]
}) })
export class EditItemPageRoutingModule { export class EditItemPageRoutingModule {

View File

@@ -1,11 +1,11 @@
<div class="container"> <div class="container">
<ds-alert [type]="'alert-info'" [content]="'item.edit.authorizations.heading'"></ds-alert> <ds-alert [type]="'alert-info'" [content]="'item.edit.authorizations.heading'"></ds-alert>
<ds-resource-policies [resourceKey]="'item'" [resourceUUID]="(getItemUUID() | async)"></ds-resource-policies> <ds-resource-policies [resourceType]="'item'" [resourceUUID]="(getItemUUID() | async)"></ds-resource-policies>
<ng-container *ngFor="let bundle of (getItemBundles() | async)?.page; trackById"> <ng-container *ngFor="let bundle of (getItemBundles() | async)?.page; trackById">
<ds-resource-policies [resourceKey]="'bundle'" <ds-resource-policies [resourceType]="'bundle'"
[resourceUUID]="bundle.id"></ds-resource-policies> [resourceUUID]="bundle.id"></ds-resource-policies>
<ng-container *ngFor="let bitstream of (bundleBitstreamsMap.get(bundle.id) | async)?.page; trackById"> <ng-container *ngFor="let bitstream of (bundleBitstreamsMap.get(bundle.id) | async)?.page; trackById">
<ds-resource-policies [resourceKey]="'bitstream'" <ds-resource-policies [resourceType]="'bitstream'"
[resourceUUID]="bitstream.id"></ds-resource-policies> [resourceUUID]="bitstream.id"></ds-resource-policies>
</ng-container> </ng-container>
</ng-container> </ng-container>

View File

@@ -170,18 +170,6 @@ describe('ResourcePolicyService', () => {
}); });
describe('delete', () => { describe('delete', () => {
beforeEach(async(() => {
scheduler = getTestScheduler();
responseCacheEntry.completed = true;
requestService = jasmine.createSpyObj('requestService', {
configure: {},
getByHref: observableOf(responseCacheEntry),
getByUUID: hot('a', { a: responseCacheEntry }),
generateRequestId: 'request-id',
removeByHrefSubstring: {}
});
}));
it('should proxy the call to dataservice.create', () => { it('should proxy the call to dataservice.create', () => {
scheduler.schedule(() => service.delete(resourcePolicyId)); scheduler.schedule(() => service.delete(resourcePolicyId));
scheduler.flush(); scheduler.flush();
@@ -190,6 +178,15 @@ describe('ResourcePolicyService', () => {
}); });
}); });
describe('update', () => {
it('should proxy the call to dataservice.update', () => {
scheduler.schedule(() => service.update(resourcePolicy));
scheduler.flush();
expect((service as any).dataService.update).toHaveBeenCalledWith(resourcePolicy);
});
});
describe('findById', () => { describe('findById', () => {
it('should proxy the call to dataservice.findById', () => { it('should proxy the call to dataservice.findById', () => {
scheduler.schedule(() => service.findById(resourcePolicyId)); scheduler.schedule(() => service.findById(resourcePolicyId));

View File

@@ -103,6 +103,15 @@ export class ResourcePolicyService {
return this.dataService.delete(resourcePolicyID); return this.dataService.delete(resourcePolicyID);
} }
/**
* Add a new patch to the object cache
* The patch is derived from the differences between the given object and its version in the object cache
* @param {ResourcePolicy} object The given object
*/
update(object: ResourcePolicy): Observable<RemoteData<ResourcePolicy>> {
return this.dataService.update(object);
}
/** /**
* Returns an observable of {@link RemoteData} of a {@link ResourcePolicy}, based on an href, with a list of {@link FollowLinkConfig}, * Returns an observable of {@link RemoteData} of a {@link ResourcePolicy}, based on an href, with a list of {@link FollowLinkConfig},
* to automatically resolve {@link HALLink}s of the {@link ResourcePolicy} * to automatically resolve {@link HALLink}s of the {@link ResourcePolicy}

View File

@@ -67,6 +67,10 @@ export const getSucceededRemoteData = () =>
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> => <T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded)); source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded));
export const getSucceededRemoteWithNotEmptyData = () =>
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded && isNotEmpty(rd.payload)));
/** /**
* Get the first successful remotely retrieved object * Get the first successful remotely retrieved object
* *
@@ -84,6 +88,23 @@ export const getFirstSucceededRemoteDataPayload = () =>
getRemoteDataPayload() getRemoteDataPayload()
); );
/**
* Get the first successful remotely retrieved object with not empty payload
*
* You usually don't want to use this, it is a code smell.
* Work with the RemoteData object instead, that way you can
* handle loading and errors correctly.
*
* These operators were created as a first step in refactoring
* out all the instances where this is used incorrectly.
*/
export const getFirstSucceededRemoteDataWithNotEmptyPayload = () =>
<T>(source: Observable<RemoteData<T>>): Observable<T> =>
source.pipe(
getSucceededRemoteWithNotEmptyData(),
getRemoteDataPayload()
);
/** /**
* Get the all successful remotely retrieved objects * Get the all successful remotely retrieved objects
* *

View File

@@ -1,6 +1,6 @@
<div class="container"> <div class="container">
<h4 class="mb-3">{{'resource-policies.create.modal.head' | translate}}</h4> <h4 class="mb-3">{{'resource-policies.edit.page.heading' | translate}} {{targetResourceName}}</h4>
<ds-resource-policy-form (reset)="redirectToPreviousPage()" <ds-resource-policy-form (reset)="redirectToAuthorizationsPage()"
(submit)="createResourcePolicy($event)"></ds-resource-policy-form> (submit)="createResourcePolicy($event)"></ds-resource-policy-form>
</div> </div>

View File

@@ -1,32 +1,69 @@
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { take } from 'rxjs/operators'; import { first, map, take } from 'rxjs/operators';
import { ResourcePolicyEvent } from '../form/resource-policy-form'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { RouteService } from '../../../core/services/route.service';
import { ResourcePolicyService } from '../../../core/resource-policy/resource-policy.service'; import { ResourcePolicyService } from '../../../core/resource-policy/resource-policy.service';
import { NotificationsService } from '../../notifications/notifications.service';
import { RemoteData } from '../../../core/data/remote-data';
import { ResourcePolicy } from '../../../core/resource-policy/models/resource-policy.model';
import { ResourcePolicyEvent } from '../form/resource-policy-form';
import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../+item-page/edit-item-page/edit-item-page.routing.module';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-resource-policy-create', selector: 'ds-resource-policy-create',
templateUrl: './resource-policy-create.component.html' templateUrl: './resource-policy-create.component.html'
}) })
export class ResourcePolicyCreateComponent { export class ResourcePolicyCreateComponent implements OnInit {
/**
* The uuid of the resource target of the policy
*/
private targetResourceUUID: string;
public targetResourceName: string;
constructor( constructor(
protected resourcePolicy: ResourcePolicyService, private dsoNameService: DSONameService,
protected router: Router, private notificationsService: NotificationsService,
protected routeService: RouteService) { private resourcePolicyService: ResourcePolicyService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit(): void {
this.route.data.pipe(
map((data) => data),
take(1)
).subscribe((data: any) => {
this.targetResourceUUID = (data.resourcePolicyTarget as RemoteData<DSpaceObject>).payload.id;
this.targetResourceName = this.dsoNameService.getName((data.resourcePolicyTarget as RemoteData<DSpaceObject>).payload);
});
}
redirectToAuthorizationsPage() {
this.router.navigate([`../../${ITEM_EDIT_AUTHORIZATIONS_PATH}`], { relativeTo: this.route });
} }
createResourcePolicy(event: ResourcePolicyEvent) { createResourcePolicy(event: ResourcePolicyEvent) {
let response$;
if (event.target.type === 'eperson') {
response$ = this.resourcePolicyService.create(event.object, this.targetResourceUUID, event.target.uuid);
} else {
response$ = this.resourcePolicyService.create(event.object, this.targetResourceUUID, null, event.target.uuid);
}
response$.pipe(
first((response: RemoteData<ResourcePolicy>) => !response.isResponsePending)
).subscribe((responseRD: RemoteData<ResourcePolicy>) => {
if (responseRD.hasSucceeded) {
this.notificationsService.success(null, 'resource-policies.create.page.success.content');
this.redirectToAuthorizationsPage();
} else {
this.notificationsService.error(null, 'resource-policies.create.page.failure.content');
} }
redirectToPreviousPage() {
this.routeService.getPreviousUrl().pipe(take(1))
.subscribe((url) => {
this.router.navigateByUrl(url);
}) })
} }
} }

View File

@@ -1,5 +1,7 @@
<div class="container"> <div class="container">
<h4 class="mb-3">{{'resource-policies.edit.modal.head' | translate}}</h4> <h4 class="mb-3">{{'resource-policies.edit.page.heading' | translate}} {{resourcePolicy.id}}</h4>
<ds-resource-policy-form></ds-resource-policy-form> <ds-resource-policy-form [resourcePolicy]="resourcePolicy"
(reset)="redirectToAuthorizationsPage()"
(submit)="updateResourcePolicy($event)"></ds-resource-policy-form>
</div> </div>

View File

@@ -1,9 +1,60 @@
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { first, map, take } from 'rxjs/operators';
import { ResourcePolicyService } from '../../../core/resource-policy/resource-policy.service';
import { NotificationsService } from '../../notifications/notifications.service';
import { RemoteData } from '../../../core/data/remote-data';
import { ResourcePolicy } from '../../../core/resource-policy/models/resource-policy.model';
import { ResourcePolicyEvent } from '../form/resource-policy-form';
import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../+item-page/edit-item-page/edit-item-page.routing.module';
@Component({ @Component({
selector: 'ds-resource-policy-edit', selector: 'ds-resource-policy-edit',
templateUrl: './resource-policy-edit.component.html' templateUrl: './resource-policy-edit.component.html'
}) })
export class ResourcePolicyEditComponent { export class ResourcePolicyEditComponent implements OnInit {
/**
* The resource policy object to edit
*/
public resourcePolicy: ResourcePolicy;
constructor(
private notificationsService: NotificationsService,
private resourcePolicyService: ResourcePolicyService,
private route: ActivatedRoute,
private router: Router) {
}
ngOnInit(): void {
this.route.data.pipe(
map((data) => data),
take(1)
).subscribe((data: any) => {
this.resourcePolicy = (data.resourcePolicy as RemoteData<ResourcePolicy>).payload;
console.log(data)
});
}
redirectToAuthorizationsPage() {
this.router.navigate([`../../${ITEM_EDIT_AUTHORIZATIONS_PATH}`], { relativeTo: this.route });
}
updateResourcePolicy(event: ResourcePolicyEvent) {
const updatedObject = Object.assign({}, event.object, {
_links: this.resourcePolicy._links
});
this.resourcePolicyService.update(updatedObject).pipe(
first((response: RemoteData<ResourcePolicy>) => !response.isResponsePending)
).subscribe((responseRD: RemoteData<ResourcePolicy>) => {
if (responseRD.hasSucceeded) {
this.notificationsService.success(null, 'resource-policies.edit.page.success.content');
this.redirectToAuthorizationsPage();
} else {
this.notificationsService.error(null, 'resource-policies.edit.page.failure.content');
}
})
}
} }

View File

@@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { map, take } from 'rxjs/operators'; import { map, take } from 'rxjs/operators';
@@ -7,13 +7,15 @@ import { uniqueId } from 'lodash'
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list'; import { PaginatedList } from '../../../../core/data/paginated-list';
import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model';
import { DataService } from '../../../../core/data/data.service'; import { DataService } from '../../../../core/data/data.service';
import { hasValue, isNotEmpty } from '../../../empty.util'; import { hasValue, isNotEmpty } from '../../../empty.util';
import { FindListOptions } from '../../../../core/data/request.models'; import { FindListOptions } from '../../../../core/data/request.models';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { getDataServiceFor } from '../../../../core/cache/builders/build-decorators';
import { EPERSON } from '../../../../core/eperson/models/eperson.resource-type';
import { GROUP } from '../../../../core/eperson/models/group.resource-type';
import { ResourceType } from '../../../../core/shared/resource-type';
@Component({ @Component({
selector: 'ds-eperson-group-list', selector: 'ds-eperson-group-list',
@@ -69,9 +71,13 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
*/ */
private subs: Subscription[] = []; private subs: Subscription[] = [];
constructor(public dsoNameService: DSONameService, constructor(public dsoNameService: DSONameService, private parentInjector: Injector) {
private epersonService: EPersonDataService, const resourceType: ResourceType = (this.isListOfEPerson) ? EPERSON : GROUP;
private groupsService: GroupDataService) { const provider = getDataServiceFor(resourceType);
this.dataService = Injector.create({
providers: [],
parent: this.parentInjector
}).get(provider);
} }
/** /**
@@ -80,7 +86,6 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.paginationOptions.id = uniqueId('eperson-group-list-pagination'); this.paginationOptions.id = uniqueId('eperson-group-list-pagination');
this.paginationOptions.pageSize = 5; this.paginationOptions.pageSize = 5;
this.dataService = (this.isListOfEPerson) ? this.epersonService : this.groupsService;
if (this.initSelected) { if (this.initSelected) {
this.entrySelectedId.next(this.initSelected); this.entrySelectedId.next(this.initSelected);

View File

@@ -7,7 +7,7 @@
<div class="container-fluid"> <div class="container-fluid">
<label for="ResourcePolicyObject">{{'resource-policies.form.eperson-group-list.label' | translate}}</label> <label for="ResourcePolicyObject">{{'resource-policies.form.eperson-group-list.label' | translate}}</label>
<input id="ResourcePolicyObject" class="form-control mb-3" type="text" readonly [value]="getResourcePolicyTargetName()"> <input id="ResourcePolicyObject" class="form-control mb-3" type="text" readonly [value]="getResourcePolicyTargetName()">
<ngb-tabset type="pills"> <ngb-tabset *ngIf="canSetGrant()" type="pills">
<ngb-tab [title]="'resource-policies.form.eperson-group-list.tab.eperson' | translate"> <ngb-tab [title]="'resource-policies.form.eperson-group-list.tab.eperson' | translate">
<ng-template ngbTabContent> <ng-template ngbTabContent>
<ds-eperson-group-list (select)="updateObjectSelected($event, true)"></ds-eperson-group-list> <ds-eperson-group-list (select)="updateObjectSelected($event, true)"></ds-eperson-group-list>

View File

@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { DynamicFormControlModel, DynamicFormGroupModel, DynamicSelectModel } from '@ng-dynamic-forms/core'; import { DynamicFormControlModel, DynamicFormGroupModel, DynamicSelectModel } from '@ng-dynamic-forms/core';
@@ -22,8 +22,11 @@ import { DsDynamicTextAreaModel } from '../../form/builder/ds-dynamic-form-ui/mo
import { DynamicDsDatePickerModel } from '../../form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.model'; import { DynamicDsDatePickerModel } from '../../form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.model';
import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { hasValue, isNotEmpty } from '../../empty.util'; import { hasValue, isEmpty, isNotEmpty } from '../../empty.util';
import { FormService } from '../../form/form.service'; import { FormService } from '../../form/form.service';
import { RESOURCE_POLICY } from '../../../core/resource-policy/models/resource-policy.resource-type';
import { RemoteData } from '../../../core/data/remote-data';
import { Subscription } from 'rxjs/internal/Subscription';
export interface ResourcePolicyEvent { export interface ResourcePolicyEvent {
object: ResourcePolicy, object: ResourcePolicy,
@@ -40,10 +43,10 @@ export interface ResourcePolicyEvent {
/** /**
* Component that show form for adding/editing a resource policy * Component that show form for adding/editing a resource policy
*/ */
export class ResourcePolicyFormComponent implements OnInit { export class ResourcePolicyFormComponent implements OnInit, OnDestroy {
/** /**
* The resource policy to edit * If given contains the resource policy to edit
* @type {ResourcePolicy} * @type {ResourcePolicy}
*/ */
@Input() resourcePolicy: ResourcePolicy; @Input() resourcePolicy: ResourcePolicy;
@@ -76,13 +79,19 @@ export class ResourcePolicyFormComponent implements OnInit {
* The eperson or group that will be grant of the permission * The eperson or group that will be grant of the permission
* @type {DSpaceObject} * @type {DSpaceObject}
*/ */
public resourcePolicyTarget: DSpaceObject; public resourcePolicyGrant: DSpaceObject;
/** /**
* The type of the object that will be grant of the permission. It could be 'eperson' or 'group' * The type of the object that will be grant of the permission. It could be 'eperson' or 'group'
* @type {string} * @type {string}
*/ */
public resourcePolicyTargetType: string; public resourcePolicyGrantType: string;
/**
* Array to track all subscriptions and unsubscribe them onDestroy
* @type {Array}
*/
private subs: Subscription[] = [];
/** /**
* Initialize instance variables * Initialize instance variables
@@ -102,6 +111,14 @@ export class ResourcePolicyFormComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.formId = this.formService.getUniqueId('resource-policy-form'); this.formId = this.formService.getUniqueId('resource-policy-form');
this.formModel = this.buildResourcePolicyForm(); this.formModel = this.buildResourcePolicyForm();
if (!this.canSetGrant()) {
this.subs.push(observableCombineLatest([this.resourcePolicy.eperson, this.resourcePolicy.group])
.subscribe(([epersonRD, groupRD]: [RemoteData<DSpaceObject>, RemoteData<DSpaceObject>]) => {
this.resourcePolicyGrant = epersonRD.payload || groupRD.payload;
})
)
}
} }
/** /**
@@ -111,7 +128,7 @@ export class ResourcePolicyFormComponent implements OnInit {
*/ */
isFormValid(): Observable<boolean> { isFormValid(): Observable<boolean> {
return this.formService.isValid(this.formId).pipe( return this.formService.isValid(this.formId).pipe(
map((isValid: boolean) => isValid && isNotEmpty(this.resourcePolicyTarget)) map((isValid: boolean) => isValid && isNotEmpty(this.resourcePolicyGrant))
) )
} }
@@ -171,21 +188,31 @@ export class ResourcePolicyFormComponent implements OnInit {
return formModel; return formModel;
} }
/**
* Return a boolean representing If is possible to set policy grant
*
* @return true if is possible, false otherwise
*/
canSetGrant(): boolean {
return isEmpty(this.resourcePolicy);
}
/** /**
* Return the name of the eperson or group that will be grant of the permission * Return the name of the eperson or group that will be grant of the permission
* *
* @return the object name * @return the object name
*/ */
getResourcePolicyTargetName(): string { getResourcePolicyTargetName(): string {
return isNotEmpty(this.resourcePolicyTarget) ? this.dsoNameService.getName(this.resourcePolicyTarget) : ''; console.log(this.resourcePolicy);
return isNotEmpty(this.resourcePolicyGrant) ? this.dsoNameService.getName(this.resourcePolicyGrant) : '';
} }
/** /**
* Update reference to the eperson or group that will be grant of the permission * Update reference to the eperson or group that will be grant of the permission
*/ */
updateObjectSelected(object: DSpaceObject, isEPerson: boolean): void { updateObjectSelected(object: DSpaceObject, isEPerson: boolean): void {
this.resourcePolicyTarget = object; this.resourcePolicyGrant = object;
this.resourcePolicyTargetType = isEPerson ? 'eperson' : 'group'; this.resourcePolicyGrantType = isEPerson ? 'eperson' : 'group';
} }
/** /**
@@ -204,17 +231,40 @@ export class ResourcePolicyFormComponent implements OnInit {
this.formService.getFormData(this.formId) this.formService.getFormData(this.formId)
.subscribe((data) => { .subscribe((data) => {
const eventPayload: ResourcePolicyEvent = Object.create({}); const eventPayload: ResourcePolicyEvent = Object.create({});
const resourcePolicy = new ResourcePolicy(); eventPayload.object = this.createResourcePolicyByFormData(data);
resourcePolicy.name = data.name; console.log('resourcePolicyTarget', this.resourcePolicyGrant.type.value);
resourcePolicy.description = data.description; eventPayload.target = {
resourcePolicy.policyType = data.policyType; type: this.resourcePolicyGrantType,
resourcePolicy.action = data.action; uuid: this.resourcePolicyGrant.id
resourcePolicy.startDate = (data.date) ? data.date.start : undefined; };
resourcePolicy.endDate = (data.date) ? data.date.end : undefined;
eventPayload.object = resourcePolicy;
eventPayload.target.type = this.resourcePolicyTargetType;
eventPayload.target.uuid = this.resourcePolicyTarget.id;
this.submit.emit(eventPayload); this.submit.emit(eventPayload);
}) })
} }
/**
* Create e new ResourcePolicy by form data
*
* @return the new ResourcePolicy object
*/
createResourcePolicyByFormData(data): ResourcePolicy {
const resourcePolicy = new ResourcePolicy();
resourcePolicy.name = (data.name) ? data.name[0].value : null;
resourcePolicy.description = (data.description) ? data.description[0].value : null;
resourcePolicy.policyType = (data.policyType) ? data.policyType[0].value : null;
resourcePolicy.action = (data.action) ? data.action[0].value : null;
resourcePolicy.startDate = (data.date && data.date.start) ? data.date.start[0].value : null;
resourcePolicy.endDate = (data.date && data.date.end) ? data.date.end[0].value : null;
resourcePolicy.type = RESOURCE_POLICY;
return resourcePolicy;
}
/**
* Unsubscribe from all subscriptions
*/
ngOnDestroy(): void {
this.subs
.filter((subscription) => hasValue(subscription))
.forEach((subscription) => subscription.unsubscribe())
}
} }

View File

@@ -0,0 +1,53 @@
import { Injectable, Injector } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { find } from 'rxjs/operators';
import { getDataServiceFor } from '../../../core/cache/builders/build-decorators';
import { ResourceType } from '../../../core/shared/resource-type';
import { DataService } from '../../../core/data/data.service';
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { hasValue, isEmpty } from '../../empty.util';
import { RemoteData } from '../../../core/data/remote-data';
/**
* This class represents a resolver that requests a specific item before the route is activated
*/
@Injectable()
export class ResourcePolicyTargetResolver implements Resolve<RemoteData<DSpaceObject>> {
/**
* The data service used to make request.
*/
private dataService: DataService<DSpaceObject>;
constructor(private parentInjector: Injector, private router: Router) {
}
/**
* Method for resolving an item based on the parameters in the current route
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
* or an error if something went wrong
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<DSpaceObject>> {
const targetType = route.queryParamMap.get('targetType');
const policyTargetId = route.queryParamMap.get('policyTargetId');
if (isEmpty(targetType) || isEmpty(policyTargetId)) {
this.router.navigateByUrl('/404', { skipLocationChange: true });
}
const provider = getDataServiceFor(new ResourceType(targetType));
this.dataService = Injector.create({
providers: [],
parent: this.parentInjector
}).get(provider);
return this.dataService.findById(policyTargetId).pipe(
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
);
}
}

View File

@@ -0,0 +1,40 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { find } from 'rxjs/operators';
import { hasValue, isEmpty } from '../../empty.util';
import { RemoteData } from '../../../core/data/remote-data';
import { ResourcePolicy } from '../../../core/resource-policy/models/resource-policy.model';
import { ResourcePolicyService } from '../../../core/resource-policy/resource-policy.service';
import { followLink } from '../../utils/follow-link-config.model';
/**
* This class represents a resolver that requests a specific item before the route is activated
*/
@Injectable()
export class ResourcePolicyResolver implements Resolve<RemoteData<ResourcePolicy>> {
constructor(private resourcePolicyService: ResourcePolicyService, private router: Router) {
}
/**
* Method for resolving an item based on the parameters in the current route
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
* or an error if something went wrong
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<ResourcePolicy>> {
const policyId = route.queryParamMap.get('policyId');
if (isEmpty(policyId)) {
this.router.navigateByUrl('/404', { skipLocationChange: true });
}
return this.resourcePolicyService.findById(policyId, followLink('eperson'), followLink('group')).pipe(
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
);
}
}

View File

@@ -4,9 +4,9 @@
<tr> <tr>
<th colspan="7"> <th colspan="7">
<p class="d-flex justify-content-between align-items-center m-0"> <p class="d-flex justify-content-between align-items-center m-0">
{{ 'resource-policies.table.headers.title.for.' + resourceKey | translate }} {{resourceUUID}} {{ 'resource-policies.table.headers.title.for.' + resourceType | translate }} {{resourceUUID}}
<button class="btn btn-outline-primary float-right" (click)="createResourcePolicy()"> <button class="btn btn-outline-primary float-right" (click)="createResourcePolicy()">
{{'resource-policies.add.for.' + resourceKey | translate}} {{'resource-policies.add.for.' + resourceType | translate}}
</button> </button>
</p> </p>
</th> </th>
@@ -14,6 +14,7 @@
<tr class="text-center"> <tr class="text-center">
<th>{{'resource-policies.table.headers.id' | translate}}</th> <th>{{'resource-policies.table.headers.id' | translate}}</th>
<th>{{'resource-policies.table.headers.name' | translate}}</th> <th>{{'resource-policies.table.headers.name' | translate}}</th>
<th>{{'resource-policies.table.headers.policyType' | translate}}</th>
<th>{{'resource-policies.table.headers.action' | translate}}</th> <th>{{'resource-policies.table.headers.action' | translate}}</th>
<th>{{'resource-policies.table.headers.eperson' | translate}}</th> <th>{{'resource-policies.table.headers.eperson' | translate}}</th>
<th>{{'resource-policies.table.headers.group' | translate}}</th> <th>{{'resource-policies.table.headers.group' | translate}}</th>
@@ -30,6 +31,7 @@
</button> </button>
</th> </th>
<td>{{policy.name}}</td> <td>{{policy.name}}</td>
<td>{{policy.policyType}}</td>
<td>{{policy.action}}</td> <td>{{policy.action}}</td>
<td *ngIf="(hasEPerson(policy) | async)"> <td *ngIf="(hasEPerson(policy) | async)">
{{getEPersonName(policy) | async}} {{getEPersonName(policy) | async}}

View File

@@ -1,12 +1,16 @@
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators'; import { filter, map, startWith, take } from 'rxjs/operators';
import { ResourcePolicyService } from '../../core/resource-policy/resource-policy.service'; import { ResourcePolicyService } from '../../core/resource-policy/resource-policy.service';
import { PaginatedList } from '../../core/data/paginated-list'; import { PaginatedList } from '../../core/data/paginated-list';
import { getFirstSucceededRemoteDataPayload, getSucceededRemoteData } from '../../core/shared/operators'; import {
getFirstSucceededRemoteDataPayload,
getFirstSucceededRemoteDataWithNotEmptyPayload,
getSucceededRemoteData
} from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { ResourcePolicy } from '../../core/resource-policy/models/resource-policy.model'; import { ResourcePolicy } from '../../core/resource-policy/models/resource-policy.model';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@@ -15,6 +19,8 @@ import { GroupDataService } from '../../core/eperson/group-data.service';
import { hasValue, isNotEmpty } from '../empty.util'; import { hasValue, isNotEmpty } from '../empty.util';
import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPerson } from '../../core/eperson/models/eperson.model';
import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { followLink } from '../utils/follow-link-config.model';
import { RequestService } from '../../core/data/request.service';
@Component({ @Component({
selector: 'ds-resource-policies', selector: 'ds-resource-policies',
@@ -36,13 +42,20 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* The resource type (e.g. 'item', 'bundle' etc) used as key to build automatically translation label * The resource type (e.g. 'item', 'bundle' etc) used as key to build automatically translation label
* @type {string} * @type {string}
*/ */
@Input() public resourceKey: string; @Input() public resourceType: string;
/**
* A boolean representing if component is active
* @type {boolean}
*/
private isActive: boolean;
/** /**
* The list of policies for given resource * The list of policies for given resource
* @type {Observable<RemoteData<PaginatedList<ResourcePolicy>>>} * @type {Observable<RemoteData<PaginatedList<ResourcePolicy>>>}
*/ */
private resourcePolicies$: Observable<RemoteData<PaginatedList<ResourcePolicy>>>; private resourcePolicies$: BehaviorSubject<RemoteData<PaginatedList<ResourcePolicy>>> =
new BehaviorSubject<RemoteData<PaginatedList<ResourcePolicy>>>({} as any);
/** /**
* Array to track all subscriptions and unsubscribe them onDestroy * Array to track all subscriptions and unsubscribe them onDestroy
@@ -57,6 +70,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* @param {DSONameService} dsoNameService * @param {DSONameService} dsoNameService
* @param {EPersonDataService} ePersonService * @param {EPersonDataService} ePersonService
* @param {GroupDataService} groupService * @param {GroupDataService} groupService
* @param {RequestService} requestService
* @param {ResourcePolicyService} resourcePolicyService * @param {ResourcePolicyService} resourcePolicyService
* @param {ActivatedRoute} route * @param {ActivatedRoute} route
* @param {Router} router * @param {Router} router
@@ -66,6 +80,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
private dsoNameService: DSONameService, private dsoNameService: DSONameService,
private ePersonService: EPersonDataService, private ePersonService: EPersonDataService,
private groupService: GroupDataService, private groupService: GroupDataService,
private requestService: RequestService,
private resourcePolicyService: ResourcePolicyService, private resourcePolicyService: ResourcePolicyService,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router private router: Router
@@ -76,9 +91,16 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* Initialize the component, setting up the resource's policies * Initialize the component, setting up the resource's policies
*/ */
ngOnInit(): void { ngOnInit(): void {
this.resourcePolicies$ = this.resourcePolicyService.searchByResource(this.resourceUUID).pipe( this.isActive = true;
getSucceededRemoteData() this.requestService.removeByHrefSubstring(this.resourceUUID);
); this.resourcePolicyService.searchByResource(this.resourceUUID, null,
followLink('eperson'), followLink('group')).pipe(
filter(() => this.isActive),
getSucceededRemoteData(),
take(1)
).subscribe((result) => {
this.resourcePolicies$.next(result);
});
} }
@@ -86,7 +108,13 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* Redirect to resource policy creation page * Redirect to resource policy creation page
*/ */
createResourcePolicy(): void { createResourcePolicy(): void {
this.router.navigate([`../${this.resourceUUID}/create`], { relativeTo: this.route }) this.router.navigate([`../create`], {
relativeTo: this.route,
queryParams: {
policyTargetId: this.resourceUUID,
targetType: this.resourceType
}
})
} }
/** /**
@@ -95,7 +123,12 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* @param policy The resource policy * @param policy The resource policy
*/ */
editResourcePolicy(policy: ResourcePolicy): void { editResourcePolicy(policy: ResourcePolicy): void {
this.router.navigate([`../${this.resourceUUID}/${policy.id}/edit`], { relativeTo: this.route }) this.router.navigate([`../edit`], {
relativeTo: this.route,
queryParams: {
policyId: policy.id
}
})
} }
/** /**
@@ -104,9 +137,11 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* @param policy The resource policy * @param policy The resource policy
*/ */
getEPersonName(policy: ResourcePolicy): Observable<string> { getEPersonName(policy: ResourcePolicy): Observable<string> {
return this.ePersonService.findByHref(policy._links.eperson.href).pipe( return policy.eperson.pipe(
getFirstSucceededRemoteDataPayload(), filter(() => this.isActive),
map((eperson: EPerson) => this.dsoNameService.getName(eperson)) getFirstSucceededRemoteDataWithNotEmptyPayload(),
map((eperson: EPerson) => this.dsoNameService.getName(eperson)),
startWith('')
) )
} }
@@ -116,9 +151,11 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* @param policy The resource policy * @param policy The resource policy
*/ */
getGroupName(policy: ResourcePolicy): Observable<string> { getGroupName(policy: ResourcePolicy): Observable<string> {
return this.groupService.findByHref(policy._links.group.href).pipe( return policy.group.pipe(
getFirstSucceededRemoteDataPayload(), filter(() => this.isActive),
map((group: Group) => this.dsoNameService.getName(group)) getFirstSucceededRemoteDataWithNotEmptyPayload(),
map((group: Group) => this.dsoNameService.getName(group)),
startWith('')
) )
} }
@@ -128,7 +165,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* @return an observable that emits all resource's policies * @return an observable that emits all resource's policies
*/ */
getResourcePolicies(): Observable<RemoteData<PaginatedList<ResourcePolicy>>> { getResourcePolicies(): Observable<RemoteData<PaginatedList<ResourcePolicy>>> {
return this.resourcePolicies$; return this.resourcePolicies$.asObservable();
} }
/** /**
@@ -138,7 +175,8 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* @return an observable that emits true when the policy is linked to a ePerson, false otherwise * @return an observable that emits true when the policy is linked to a ePerson, false otherwise
*/ */
hasEPerson(policy): Observable<boolean> { hasEPerson(policy): Observable<boolean> {
return this.ePersonService.findByHref(policy._links.eperson.href).pipe( return policy.eperson.pipe(
filter(() => this.isActive),
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
map((eperson: EPerson) => isNotEmpty(eperson)) map((eperson: EPerson) => isNotEmpty(eperson))
) )
@@ -151,7 +189,8 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* @return an observable that emits true when the policy is linked to a group, false otherwise * @return an observable that emits true when the policy is linked to a group, false otherwise
*/ */
hasGroup(policy): Observable<boolean> { hasGroup(policy): Observable<boolean> {
return this.groupService.findByHref(policy._links.group.href).pipe( return policy.group.pipe(
filter(() => this.isActive),
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
map((group: Group) => isNotEmpty(group)) map((group: Group) => isNotEmpty(group))
) )
@@ -164,7 +203,8 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
*/ */
redirectToGroupEditPage(policy: ResourcePolicy): void { redirectToGroupEditPage(policy: ResourcePolicy): void {
this.subs.push( this.subs.push(
this.groupService.findByHref(policy._links.group.href).pipe( policy.group.pipe(
filter(() => this.isActive),
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
map((group: Group) => group.id) map((group: Group) => group.id)
).subscribe((groupUUID) => this.router.navigate(['groups', groupUUID, 'edit'])) ).subscribe((groupUUID) => this.router.navigate(['groups', groupUUID, 'edit']))
@@ -175,6 +215,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
* Unsubscribe from all subscriptions * Unsubscribe from all subscriptions
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.isActive = false;
this.subs this.subs
.filter((subscription) => hasValue(subscription)) .filter((subscription) => hasValue(subscription))
.forEach((subscription) => subscription.unsubscribe()) .forEach((subscription) => subscription.unsubscribe())

View File

@@ -189,9 +189,9 @@ import { ItemVersionsNoticeComponent } from './item/item-versions/notice/item-ve
import { ResourcePoliciesComponent } from './resource-policies/resource-policies.component'; import { ResourcePoliciesComponent } from './resource-policies/resource-policies.component';
import { NgForTrackByIdDirective } from './ng-for-track-by-id.directive'; import { NgForTrackByIdDirective } from './ng-for-track-by-id.directive';
import { ResourcePolicyFormComponent } from './resource-policies/form/resource-policy-form'; import { ResourcePolicyFormComponent } from './resource-policies/form/resource-policy-form';
import { ResourcePolicyCreateComponent } from './resource-policies/create/resource-policy-create.component';
import { ResourcePolicyEditComponent } from './resource-policies/edit/resource-policy-edit.component';
import { EpersonGroupListComponent } from './resource-policies/form/eperson-group-list/eperson-group-list.component'; import { EpersonGroupListComponent } from './resource-policies/form/eperson-group-list/eperson-group-list.component';
import { ResourcePolicyTargetResolver } from './resource-policies/resolvers/resource-policy-target.resolver';
import { ResourcePolicyResolver } from './resource-policies/resolvers/resource-policy.resolver';
const MODULES = [ const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -366,8 +366,6 @@ const COMPONENTS = [
ItemVersionsNoticeComponent, ItemVersionsNoticeComponent,
ResourcePoliciesComponent, ResourcePoliciesComponent,
ResourcePolicyFormComponent, ResourcePolicyFormComponent,
ResourcePolicyCreateComponent,
ResourcePolicyEditComponent,
EpersonGroupListComponent EpersonGroupListComponent
]; ];
@@ -435,9 +433,7 @@ const ENTRY_COMPONENTS = [
LogInPasswordComponent, LogInPasswordComponent,
LogInShibbolethComponent, LogInShibbolethComponent,
ItemVersionsComponent, ItemVersionsComponent,
ItemVersionsNoticeComponent, ItemVersionsNoticeComponent
ResourcePolicyCreateComponent,
ResourcePolicyEditComponent
]; ];
const SHARED_ITEM_PAGE_COMPONENTS = [ const SHARED_ITEM_PAGE_COMPONENTS = [
@@ -452,7 +448,9 @@ const PROVIDERS = [
{ {
provide: DYNAMIC_FORM_CONTROL_MAP_FN, provide: DYNAMIC_FORM_CONTROL_MAP_FN,
useValue: dsDynamicFormControlMapFn useValue: dsDynamicFormControlMapFn
} },
ResourcePolicyResolver,
ResourcePolicyTargetResolver
]; ];
const DIRECTIVES = [ const DIRECTIVES = [