From f34ba86dcf04d4666399d0b0e689879a4f826151 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Mon, 28 Oct 2024 08:05:46 +0300 Subject: [PATCH 1/3] update: guards and resolvers with the injection cycle --- .../account/src/lib/guards/extensions.guard.ts | 11 +++++------ .../account/src/lib/resolvers/extensions.resolver.ts | 10 ++++------ .../identity/src/lib/guards/extensions.guard.ts | 11 +++++------ .../identity/src/lib/resolvers/extensions.resolver.ts | 10 ++++------ .../src/lib/guards/extensions.guard.ts | 11 +++++------ .../src/lib/resolvers/extensions.resolver.ts | 10 ++++------ 6 files changed, 27 insertions(+), 36 deletions(-) diff --git a/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts index a67db6c7b94..7e0254d7b4e 100644 --- a/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts +++ b/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 { @@ -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, diff --git a/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts index e744e98442a..623de9f49c9 100644 --- a/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts +++ b/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 = () => { - 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, diff --git a/npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts index 5d78bba9fbb..5b18b126d53 100644 --- a/npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts +++ b/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 { @@ -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, diff --git a/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts index 812a63d3f76..7dac7979cb2 100644 --- a/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts +++ b/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 = () => { - const configState = inject(ConfigStateService); - const permission = inject(PermissionService); const extensions = inject(ExtensionsService); const config = { optional: true }; @@ -36,12 +33,13 @@ export const identityExtensionsResolver: ResolveFn = () => { 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, diff --git a/npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts index 0b140f8de80..5d61aa01ae0 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts +++ b/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 { @@ -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, diff --git a/npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts index 98e3f34aa6c..0f4fc190a34 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts +++ b/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 = () => { - 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 = () => { 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, From 358a586e2899525266f38c58411633bf1b14635e Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Mon, 28 Oct 2024 08:11:05 +0300 Subject: [PATCH 2/3] update: form prop util functions to use the injection cycle --- .../extensible/src/lib/utils/props.util.ts | 10 ++++++---- .../extensible/src/lib/utils/state.util.ts | 17 ++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts index 7f06a9b1deb..084f33db906 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts +++ b/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>( } 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, diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts index 68a97e844ca..cb72dabd18f 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts +++ b/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( - configState: ConfigStateService, - permissionService: PermissionService, - resource: string, -) { +export function mapEntitiesToContributors(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( return acc; } - checkPolicies(properties, configState, permissionService); + checkPolicies(injector, properties); const mapPropertiesToContributors = createPropertiesToContributorsMapper( generateDisplayName, From 9a47611bc55d0fb9064f790ebb101aa7fe9ad0dc Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Mon, 28 Oct 2024 08:11:35 +0300 Subject: [PATCH 3/3] update: form prop util tests where the injection cycle is used --- .../src/tests/form-props.util.spec.ts | 6 +- .../extensible/src/tests/props.util.spec.ts | 199 +++++++++++++++++- .../extensible/src/tests/state.util.spec.ts | 26 ++- 3 files changed, 218 insertions(+), 13 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/tests/form-props.util.spec.ts b/npm/ng-packs/packages/components/extensible/src/tests/form-props.util.spec.ts index 123912cb9ad..ae3698757da 100644 --- a/npm/ng-packs/packages/components/extensible/src/tests/form-props.util.spec.ts +++ b/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', }); }); }); diff --git a/npm/ng-packs/packages/components/extensible/src/tests/props.util.spec.ts b/npm/ng-packs/packages/components/extensible/src/tests/props.util.spec.ts index fc2b9a3282b..ce9a439133a 100644 --- a/npm/ng-packs/packages/components/extensible/src/tests/props.util.spec.ts +++ b/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 extends PropData { getInjected: PropData['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'); + }); + }); }); diff --git a/npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts b/npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts index 006ea1101e9..de6e5d73c6f 100644 --- a/npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts +++ b/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();