Browse Source

Merge pull request #21286 from abpframework/auto-merge/rel-9-0/3170

Merge branch dev with rel-9.0
pull/21305/head
maliming 2 years ago
committed by GitHub
parent
commit
f2ef2914d8
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts
  2. 10
      npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts
  3. 10
      npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts
  4. 17
      npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts
  5. 6
      npm/ng-packs/packages/components/extensible/src/tests/form-props.util.spec.ts
  6. 199
      npm/ng-packs/packages/components/extensible/src/tests/props.util.spec.ts
  7. 26
      npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts
  8. 11
      npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts
  9. 10
      npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts
  10. 11
      npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts
  11. 10
      npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts

11
npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts

@ -1,7 +1,7 @@
import { Injectable, inject } from '@angular/core';
import { Injectable, Injector, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { tap, map } from 'rxjs/operators';
import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core';
import { IAbpGuard } from '@abp/ng.core';
import {
ExtensionsService,
getObjectExtensionEntitiesFromStore,
@ -20,8 +20,7 @@ import { eAccountComponents } from '../enums/components';
*/
@Injectable()
export class AccountExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly permmission = inject(PermissionService);
protected readonly injector = inject(Injector);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
@ -29,11 +28,11 @@ export class AccountExtensionsGuard implements IAbpGuard {
const editFormContributors = inject(ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
return getObjectExtensionEntitiesFromStore(this.configState, 'Identity').pipe(
return getObjectExtensionEntitiesFromStore(this.injector, 'Identity').pipe(
map(entities => ({
[eAccountComponents.PersonalSettings]: entities.User,
})),
mapEntitiesToContributors(this.configState, this.permmission, 'AbpIdentity'),
mapEntitiesToContributors(this.injector, 'AbpIdentity'),
tap(objectExtensionContributors => {
mergeWithDefaultProps(
this.extensions.editFormProps,

10
npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts

@ -1,7 +1,6 @@
import { inject } from '@angular/core';
import { inject, Injector } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { map, tap } from 'rxjs';
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import {
ExtensionsService,
getObjectExtensionEntitiesFromStore,
@ -12,19 +11,18 @@ import { eAccountComponents } from '../enums';
import { ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, DEFAULT_ACCOUNT_FORM_PROPS } from '../tokens';
export const accountExtensionsResolver: ResolveFn<any> = () => {
const configState = inject(ConfigStateService);
const permission = inject(PermissionService);
const injector = inject(Injector);
const extensions = inject(ExtensionsService);
const config = { optional: true };
const editFormContributors = inject(ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
return getObjectExtensionEntitiesFromStore(configState, 'Identity').pipe(
return getObjectExtensionEntitiesFromStore(injector, 'Identity').pipe(
map(entities => ({
[eAccountComponents.PersonalSettings]: entities.User,
})),
mapEntitiesToContributors(configState, permission, 'AbpIdentity'),
mapEntitiesToContributors(injector, 'AbpIdentity'),
tap(objectExtensionContributors => {
mergeWithDefaultProps(
extensions.editFormProps,

10
npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts

@ -1,4 +1,5 @@
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import { Injector } from '@angular/core';
import { Observable, of } from 'rxjs';
import { EXTRA_PROPERTIES_KEY } from '../constants/extra-properties';
import {
@ -52,20 +53,21 @@ export function mergeWithDefaultProps<F extends PropsFactory<any>>(
}
export function checkPolicies(
injector: Injector,
properties: ObjectExtensions.EntityExtensionProperties,
configState: ConfigStateService,
permissionService: PermissionService,
) {
const configState = injector.get(ConfigStateService);
const permission = injector.get(PermissionService);
const props = Object.entries(properties);
const checkPolicy = (policy: Policy): boolean => {
const { permissions, globalFeatures, features } = policy;
const { permissions, globalFeatures, features } = policy || {};
const checks = [
{
items: permissions?.permissionNames,
requiresAll: permissions?.requiresAll,
check: (item: string) => permissionService.getGrantedPolicy(item),
check: (item: string) => permission.getGrantedPolicy(item),
},
{
items: globalFeatures?.features,

17
npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts

@ -5,8 +5,8 @@ import {
ExtensionEnumDto,
ExtensionPropertyUiLookupDto,
ObjectExtensionsDto,
PermissionService,
} from '@abp/ng.core';
import { Injector } from '@angular/core';
import { Observable, pipe, zip } from 'rxjs';
import { filter, map, switchMap, take } from 'rxjs/operators';
import { ePropType } from '../enums/props.enum';
@ -56,10 +56,8 @@ function selectEnums(
);
}
export function getObjectExtensionEntitiesFromStore(
configState: ConfigStateService,
moduleKey: string,
) {
export function getObjectExtensionEntitiesFromStore(injector: Injector, moduleKey: string) {
const configState = injector.get(ConfigStateService);
return selectObjectExtensions(configState).pipe(
map(extensions => {
if (!extensions) return null;
@ -73,11 +71,8 @@ export function getObjectExtensionEntitiesFromStore(
);
}
export function mapEntitiesToContributors<T = any>(
configState: ConfigStateService,
permissionService: PermissionService,
resource: string,
) {
export function mapEntitiesToContributors<T = any>(injector: Injector, resource: string) {
const configState = injector.get(ConfigStateService);
return pipe(
switchMap((entities: any) =>
zip(selectLocalization(configState), selectEnums(configState)).pipe(
@ -100,7 +95,7 @@ export function mapEntitiesToContributors<T = any>(
return acc;
}
checkPolicies(properties, configState, permissionService);
checkPolicies(injector, properties);
const mapPropertiesToContributors = createPropertiesToContributorsMapper<T>(
generateDisplayName,

6
npm/ng-packs/packages/components/extensible/src/tests/form-props.util.spec.ts

@ -107,7 +107,7 @@ describe('Form Prop Utils', () => {
extraProperties: {
bool: true,
date: '03/30/2002',
dateTime: '2002-03-30 13:30:59Z',
dateTime: '2002-03-30 13:30:59',
time: '13:30:59',
},
});
@ -117,8 +117,8 @@ describe('Form Prop Utils', () => {
expect(extraPropertiesGroup.value).toEqual({
bool: true,
date: '2002-03-30',
dateTime: '2002-03-30T13:30:59.000Z',
time: '13:30',
dateTime: '2002-03-30T13:30:59.000',
time: '13:30:59',
});
});
});

199
npm/ng-packs/packages/components/extensible/src/tests/props.util.spec.ts

@ -1,3 +1,11 @@
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import { Injector } from '@angular/core';
import {
checkPolicies,
createExtraPropertyValueResolver,
mergeWithDefaultProps,
} from '../lib/utils/props.util';
import { ObjectExtensions } from '../lib/models/object-extensions';
import {
EntityProp,
EntityPropContributorCallbacks,
@ -5,7 +13,6 @@ import {
EntityPropsFactory,
} from '../lib/models/entity-props';
import { PropData } from '../lib/models/props';
import { createExtraPropertyValueResolver, mergeWithDefaultProps } from '../lib/utils/props.util';
class MockPropData<R = any> extends PropData<R> {
getInjected: PropData<R>['getInjected'];
@ -69,4 +76,194 @@ describe('Entity Prop Utils', () => {
expect(entityProps.get('y').props.toString()).toBe('3 <-> 2');
});
});
describe('#checkPolicies', () => {
let injector: Injector;
let configState: ConfigStateService;
let permissionService: PermissionService;
beforeEach(() => {
jest.clearAllMocks();
configState = {
getGlobalFeatureIsEnabled: jest.fn().mockReturnValue(false),
getFeatureIsEnabled: jest.fn().mockReturnValue(false),
} as any;
permissionService = {
getGrantedPolicy: jest.fn().mockReturnValue(false),
} as any;
injector = {
get: jest.fn().mockImplementation(service => {
if (service === ConfigStateService) {
return configState;
}
if (service === PermissionService) {
return permissionService;
}
return null;
}),
} as any;
});
it('should keep property when all permissions and features are satisfied', () => {
(permissionService.getGrantedPolicy as jest.Mock).mockReturnValue(true);
(configState.getGlobalFeatureIsEnabled as jest.Mock).mockReturnValue(true);
(configState.getFeatureIsEnabled as jest.Mock).mockReturnValue(true);
const properties: ObjectExtensions.EntityExtensionProperties = {
property1: {
policy: {
permissions: { permissionNames: ['Permission1', 'Permission2'], requiresAll: true },
globalFeatures: { features: ['GlobalFeature1'], requiresAll: true },
features: { features: ['Feature1'], requiresAll: true },
},
displayName: undefined,
api: undefined,
ui: undefined,
attributes: [],
configuration: undefined,
defaultValue: undefined,
},
};
checkPolicies(injector, properties);
expect(properties.property1).toBeDefined();
expect(permissionService.getGrantedPolicy).toHaveBeenCalledWith('Permission1');
expect(permissionService.getGrantedPolicy).toHaveBeenCalledWith('Permission2');
expect(configState.getGlobalFeatureIsEnabled).toHaveBeenCalledWith('GlobalFeature1');
expect(configState.getFeatureIsEnabled).toHaveBeenCalledWith('Feature1');
});
it('should keep property when no permissions and features are satisfied', () => {
(permissionService.getGrantedPolicy as jest.Mock).mockReturnValue(false);
(configState.getGlobalFeatureIsEnabled as jest.Mock).mockReturnValue(false);
(configState.getFeatureIsEnabled as jest.Mock).mockReturnValue(false);
const properties: ObjectExtensions.EntityExtensionProperties = {
property1: {
policy: {
permissions: { permissionNames: ['Permission1', 'Permission2'], requiresAll: true },
globalFeatures: { features: ['GlobalFeature1'], requiresAll: true },
features: { features: ['Feature1'], requiresAll: true },
},
displayName: undefined,
api: undefined,
ui: undefined,
attributes: [],
configuration: undefined,
defaultValue: undefined,
},
};
checkPolicies(injector, properties);
expect(properties.property1).toBeUndefined();
expect(permissionService.getGrantedPolicy).toHaveBeenCalledWith('Permission1');
expect(permissionService.getGrantedPolicy).not.toHaveBeenCalledWith('Permission2');
expect(configState.getGlobalFeatureIsEnabled).not.toHaveBeenCalledWith('GlobalFeature1');
expect(configState.getFeatureIsEnabled).not.toHaveBeenCalledWith('Feature1');
});
it('should delete property when only some permissions are granted', () => {
(permissionService.getGrantedPolicy as jest.Mock).mockImplementation(
permission => permission === 'Permission1',
);
(configState.getGlobalFeatureIsEnabled as jest.Mock).mockReturnValue(true);
(configState.getFeatureIsEnabled as jest.Mock).mockReturnValue(true);
const properties: ObjectExtensions.EntityExtensionProperties = {
property1: {
policy: {
permissions: { permissionNames: ['Permission1', 'Permission2'], requiresAll: true },
globalFeatures: { features: ['GlobalFeature1'], requiresAll: true },
features: { features: ['Feature1'], requiresAll: true },
},
displayName: undefined,
api: undefined,
ui: undefined,
attributes: [],
configuration: undefined,
defaultValue: undefined,
},
};
checkPolicies(injector, properties);
expect(properties.property1).toBeUndefined();
expect(permissionService.getGrantedPolicy).toHaveBeenCalledWith('Permission1');
expect(permissionService.getGrantedPolicy).toHaveBeenCalledWith('Permission2');
});
it('should delete property when some global features are disabled', () => {
(permissionService.getGrantedPolicy as jest.Mock).mockReturnValue(true);
(configState.getGlobalFeatureIsEnabled as jest.Mock).mockImplementation(feature =>
feature === 'GlobalFeature2' ? false : true,
);
(configState.getFeatureIsEnabled as jest.Mock).mockReturnValue(true);
const properties: ObjectExtensions.EntityExtensionProperties = {
property1: {
policy: {
permissions: { permissionNames: ['Permission1'], requiresAll: true },
globalFeatures: { features: ['GlobalFeature1', 'GlobalFeature2'], requiresAll: true },
features: { features: ['Feature1'], requiresAll: true },
},
displayName: undefined,
api: undefined,
ui: undefined,
attributes: [],
configuration: undefined,
defaultValue: undefined,
},
};
checkPolicies(injector, properties);
expect(properties.property1).toBeUndefined();
expect(configState.getGlobalFeatureIsEnabled).toHaveBeenCalledWith('GlobalFeature1');
expect(configState.getGlobalFeatureIsEnabled).toHaveBeenCalledWith('GlobalFeature2');
});
it('should keep property when all permissions are granted but only some features are required', () => {
(permissionService.getGrantedPolicy as jest.Mock).mockImplementation(
permission => permission === 'Permission1' || permission === 'Permission2',
);
(configState.getFeatureIsEnabled as jest.Mock).mockImplementation(
feature => feature === 'Feature1',
);
(configState.getGlobalFeatureIsEnabled as jest.Mock).mockReturnValue(true);
const properties: ObjectExtensions.EntityExtensionProperties = {
property1: {
policy: {
permissions: { permissionNames: ['Permission1', 'Permission2'], requiresAll: false },
features: {
features: ['Feature1', 'Feature2', 'Feature3'],
requiresAll: false,
},
globalFeatures: { features: ['GlobalFeature1'], requiresAll: true },
},
displayName: undefined,
api: undefined,
ui: undefined,
attributes: [],
configuration: undefined,
defaultValue: undefined,
},
};
checkPolicies(injector, properties);
expect(properties.property1).toBeDefined();
expect(configState.getFeatureIsEnabled).toHaveBeenCalledWith('Feature1');
expect(configState.getFeatureIsEnabled).not.toHaveBeenCalledWith('Feature2');
expect(configState.getFeatureIsEnabled).not.toHaveBeenCalledWith('Feature3');
expect(configState.getGlobalFeatureIsEnabled).toHaveBeenCalledWith('GlobalFeature1');
});
});
});

26
npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts

@ -1,4 +1,4 @@
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import { ConfigStateService } from '@abp/ng.core';
import { firstValueFrom, lastValueFrom, of } from 'rxjs';
import { take } from 'rxjs/operators';
import { ePropType } from '../lib/enums/props.enum';
@ -9,18 +9,25 @@ import {
getObjectExtensionEntitiesFromStore,
mapEntitiesToContributors,
} from '../lib/utils/state.util';
import { Injector } from '@angular/core';
const fakeAppConfigService = { get: () => of(createMockState()) } as any;
const fakeLocalizationService = { get: () => of(createMockState()) } as any;
const configState = new ConfigStateService(fakeAppConfigService, fakeLocalizationService, false);
configState.refreshAppState();
const permissionService = new PermissionService(configState);
describe('State Utils', () => {
let injector: Injector;
beforeEach(() => {
injector = {
get: jest.fn().mockReturnValue(configState),
};
});
describe('#getObjectExtensionEntitiesFromStore', () => {
it('should return observable entities of an existing module', async () => {
const objectExtensionEntitiesFromStore$ = getObjectExtensionEntitiesFromStore(
configState,
injector,
'Identity',
);
@ -29,7 +36,7 @@ describe('State Utils', () => {
});
it('should return observable empty object if module does not exist', async () => {
const entities = await getObjectExtensionEntitiesFromStore(configState, 'Saas').toPromise();
const entities = await getObjectExtensionEntitiesFromStore(injector, 'Saas').toPromise();
expect(entities).toEqual({});
});
@ -37,7 +44,11 @@ describe('State Utils', () => {
const emptyConfigState = new ConfigStateService(null, null, false);
const emit = jest.fn();
getObjectExtensionEntitiesFromStore(emptyConfigState, 'Identity').subscribe(emit);
injector = {
get: jest.fn().mockReturnValue(emptyConfigState),
};
getObjectExtensionEntitiesFromStore(injector, 'Identity').subscribe(emit);
setTimeout(() => {
expect(emit).not.toHaveBeenCalled();
@ -49,10 +60,7 @@ describe('State Utils', () => {
describe('#mapEntitiesToContributors', () => {
it('should return contributors from given entities', async () => {
const contributors = await lastValueFrom(
of(createMockEntities()).pipe(
mapEntitiesToContributors(configState, permissionService, 'AbpIdentity'),
take(1),
),
of(createMockEntities()).pipe(mapEntitiesToContributors(injector, 'AbpIdentity'), take(1)),
);
const propList = new EntityPropList();

11
npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts

@ -5,8 +5,8 @@ import {
mergeWithDefaultActions,
mergeWithDefaultProps,
} from '@abp/ng.components/extensible';
import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core';
import { Injectable, inject } from '@angular/core';
import { IAbpGuard } from '@abp/ng.core';
import { Injectable, Injector, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
@ -30,8 +30,7 @@ import {
*/
@Injectable()
export class IdentityExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly permission = inject(PermissionService);
protected readonly injector = inject(Injector);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
@ -43,12 +42,12 @@ export class IdentityExtensionsGuard implements IAbpGuard {
const createFormContributors = inject(IDENTITY_CREATE_FORM_PROP_CONTRIBUTORS, config) || {};
const editFormContributors = inject(IDENTITY_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
return getObjectExtensionEntitiesFromStore(this.configState, 'Identity').pipe(
return getObjectExtensionEntitiesFromStore(this.injector, 'Identity').pipe(
map(entities => ({
[eIdentityComponents.Roles]: entities.Role,
[eIdentityComponents.Users]: entities.User,
})),
mapEntitiesToContributors(this.configState, this.permission, 'AbpIdentity'),
mapEntitiesToContributors(this.injector, 'AbpIdentity'),
tap(objectExtensionContributors => {
mergeWithDefaultActions(
this.extensions.entityActions,

10
npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts

@ -5,8 +5,7 @@ import {
mergeWithDefaultActions,
mergeWithDefaultProps,
} from '@abp/ng.components/extensible';
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import { inject } from '@angular/core';
import { inject, Injector } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { map, tap } from 'rxjs';
import { eIdentityComponents } from '../enums';
@ -24,8 +23,6 @@ import {
} from '../tokens';
export const identityExtensionsResolver: ResolveFn<any> = () => {
const configState = inject(ConfigStateService);
const permission = inject(PermissionService);
const extensions = inject(ExtensionsService);
const config = { optional: true };
@ -36,12 +33,13 @@ export const identityExtensionsResolver: ResolveFn<any> = () => {
const createFormContributors = inject(IDENTITY_CREATE_FORM_PROP_CONTRIBUTORS, config) || {};
const editFormContributors = inject(IDENTITY_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
return getObjectExtensionEntitiesFromStore(configState, 'Identity').pipe(
const injector = inject(Injector);
return getObjectExtensionEntitiesFromStore(injector, 'Identity').pipe(
map(entities => ({
[eIdentityComponents.Roles]: entities.Role,
[eIdentityComponents.Users]: entities.User,
})),
mapEntitiesToContributors(configState, permission, 'AbpIdentity'),
mapEntitiesToContributors(injector, 'AbpIdentity'),
tap(objectExtensionContributors => {
mergeWithDefaultActions(
extensions.entityActions,

11
npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts

@ -5,8 +5,8 @@ import {
mergeWithDefaultActions,
mergeWithDefaultProps,
} from '@abp/ng.components/extensible';
import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core';
import { Injectable, inject } from '@angular/core';
import { IAbpGuard } from '@abp/ng.core';
import { Injectable, Injector, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
@ -30,8 +30,7 @@ import {
*/
@Injectable()
export class TenantManagementExtensionsGuard implements IAbpGuard {
protected readonly configState = inject(ConfigStateService);
protected readonly permission = inject(PermissionService);
protected readonly injector = inject(Injector);
protected readonly extensions = inject(ExtensionsService);
canActivate(): Observable<boolean> {
@ -45,11 +44,11 @@ export class TenantManagementExtensionsGuard implements IAbpGuard {
const editFormContributors =
inject(TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
return getObjectExtensionEntitiesFromStore(this.configState, 'TenantManagement').pipe(
return getObjectExtensionEntitiesFromStore(this.injector, 'TenantManagement').pipe(
map(entities => ({
[eTenantManagementComponents.Tenants]: entities.Tenant,
})),
mapEntitiesToContributors(this.configState, this.permission, 'TenantManagement'),
mapEntitiesToContributors(this.injector, 'TenantManagement'),
tap(objectExtensionContributors => {
mergeWithDefaultActions(
this.extensions.entityActions,

10
npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts

@ -5,8 +5,7 @@ import {
mergeWithDefaultActions,
mergeWithDefaultProps,
} from '@abp/ng.components/extensible';
import { ConfigStateService, PermissionService } from '@abp/ng.core';
import { inject } from '@angular/core';
import { inject, Injector } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { map, tap } from 'rxjs';
import { eTenantManagementComponents } from '../enums';
@ -24,8 +23,7 @@ import {
} from '../tokens';
export const tenantManagementExtensionsResolver: ResolveFn<any> = () => {
const configState = inject(ConfigStateService);
const permission = inject(PermissionService);
const injector = inject(Injector);
const extensions = inject(ExtensionsService);
const config = { optional: true };
@ -37,11 +35,11 @@ export const tenantManagementExtensionsResolver: ResolveFn<any> = () => {
inject(TENANT_MANAGEMENT_CREATE_FORM_PROP_CONTRIBUTORS, config) || {};
const editFormContributors = inject(TENANT_MANAGEMENT_EDIT_FORM_PROP_CONTRIBUTORS, config) || {};
return getObjectExtensionEntitiesFromStore(configState, 'TenantManagement').pipe(
return getObjectExtensionEntitiesFromStore(injector, 'TenantManagement').pipe(
map(entities => ({
[eTenantManagementComponents.Tenants]: entities.Tenant,
})),
mapEntitiesToContributors(configState, permission, 'TenantManagement'),
mapEntitiesToContributors(injector, 'TenantManagement'),
tap(objectExtensionContributors => {
mergeWithDefaultActions(
extensions.entityActions,

Loading…
Cancel
Save