From 76b755957598b4a21e1f270538c6137ae1c537ae Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 16:44:05 +0300 Subject: [PATCH 1/3] feat(core): add condition check to getGrantedPolicy --- .../core/src/lib/states/config.state.ts | 99 +++++++++++++++---- 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index e18693ccb7..d984a8d9b3 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -1,8 +1,18 @@ -import { Action, createSelector, Selector, State, StateContext, Store } from '@ngxs/store'; +import { + Action, + createSelector, + Selector, + State, + StateContext, + Store, +} from '@ngxs/store'; import { of } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; import snq from 'snq'; -import { GetAppConfiguration, PatchRouteByName } from '../actions/config.actions'; +import { + GetAppConfiguration, + PatchRouteByName, +} from '../actions/config.actions'; import { SetLanguage } from '../actions/session.actions'; import { ABP } from '../models/common'; import { Config } from '../models/config'; @@ -73,9 +83,12 @@ export class ConfigState { } static getApiUrl(key?: string) { - const selector = createSelector([ConfigState], (state: Config.State): string => { - return state.environment.apis[key || 'default'].url; - }); + const selector = createSelector( + [ConfigState], + (state: Config.State): string => { + return state.environment.apis[key || 'default'].url; + }, + ); return selector; } @@ -90,10 +103,19 @@ export class ConfigState { static getSettings(keyword?: string) { const selector = createSelector([ConfigState], (state: Config.State) => { if (keyword) { - const keys = snq(() => Object.keys(state.setting.values).filter(key => key.indexOf(keyword) > -1), []); + const keys = snq( + () => + Object.keys(state.setting.values).filter( + key => key.indexOf(keyword) > -1, + ), + [], + ); if (keys.length) { - return keys.reduce((acc, key) => ({ ...acc, [key]: state.setting.values[key] }), {}); + return keys.reduce( + (acc, key) => ({ ...acc, [key]: state.setting.values[key] }), + {}, + ); } } @@ -103,15 +125,40 @@ export class ConfigState { } static getGrantedPolicy(key: string) { - const selector = createSelector([ConfigState], (state: Config.State): boolean => { - if (!key) return true; - return snq(() => state.auth.grantedPolicies[key], false); - }); + const selector = createSelector( + [ConfigState], + (state: Config.State): boolean => { + if (!key) return true; + const getPolicy = k => snq(() => state.auth.grantedPolicies[k], false); + + const orRegexp = /\|\|/g; + const andRegexp = /&&/g; + + if (orRegexp.test(key)) { + const keys = key.split('||').filter(k => !!k); + + if (keys.length !== 2) return false; + + return getPolicy(keys[0].trim()) || getPolicy(keys[1].trim()); + } else if (andRegexp.test(key)) { + const keys = key.split('&&').filter(k => !!k); + + if (keys.length !== 2) return false; + + return getPolicy(keys[0].trim()) && getPolicy(keys[1].trim()); + } + + return getPolicy(key); + }, + ); return selector; } - static getLocalization(key: string | Config.LocalizationWithDefault, ...interpolateParams: string[]) { + static getLocalization( + key: string | Config.LocalizationWithDefault, + ...interpolateParams: string[] + ) { let defaultValue: string; if (typeof key !== 'string') { @@ -164,7 +211,10 @@ export class ConfigState { return selector; } - constructor(private appConfigurationService: ApplicationConfigurationService, private store: Store) {} + constructor( + private appConfigurationService: ApplicationConfigurationService, + private store: Store, + ) {} @Action(GetAppConfiguration) addData({ patchState, dispatch }: StateContext) { @@ -175,19 +225,25 @@ export class ConfigState { }), ), switchMap(configuration => { - let defaultLang: string = configuration.setting.values['Abp.Localization.DefaultLanguage']; + let defaultLang: string = + configuration.setting.values['Abp.Localization.DefaultLanguage']; if (defaultLang.includes(';')) { defaultLang = defaultLang.split(';')[0]; } - return this.store.selectSnapshot(SessionState.getLanguage) ? of(null) : dispatch(new SetLanguage(defaultLang)); + return this.store.selectSnapshot(SessionState.getLanguage) + ? of(null) + : dispatch(new SetLanguage(defaultLang)); }), ); } @Action(PatchRouteByName) - patchRoute({ patchState, getState }: StateContext, { name, newValue }: PatchRouteByName) { + patchRoute( + { patchState, getState }: StateContext, + { name, newValue }: PatchRouteByName, + ) { let routes: ABP.FullRoute[] = getState().routes; const index = routes.findIndex(route => route.name === name); @@ -208,7 +264,9 @@ function patchRouteDeep( ): ABP.FullRoute[] { routes = routes.map(route => { if (route.name === name) { - newValue.url = `${parentUrl}/${(!newValue.path && newValue.path === '' ? route.path : newValue.path) || ''}`; + newValue.url = `${parentUrl}/${(!newValue.path && newValue.path === '' + ? route.path + : newValue.path) || ''}`; if (newValue.children && newValue.children.length) { newValue.children = newValue.children.map(child => ({ @@ -219,7 +277,12 @@ function patchRouteDeep( return { ...route, ...newValue }; } else if (route.children && route.children.length) { - route.children = patchRouteDeep(route.children, name, newValue, (parentUrl || '/') + route.path); + route.children = patchRouteDeep( + route.children, + name, + newValue, + (parentUrl || '/') + route.path, + ); } return route; From 4764750a8c4c38c670d05858e88407b91c5dbe60 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 16:44:37 +0300 Subject: [PATCH 2/3] fix(core): sort pipe null data problem --- .../packages/core/src/lib/pipes/sort.pipe.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts index ea23da1efc..984279f1a4 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts @@ -5,7 +5,11 @@ export type SortOrder = 'asc' | 'desc'; name: 'abpSort', }) export class SortPipe implements PipeTransform { - transform(value: any[], sortOrder: SortOrder | string = 'asc', sortKey?: string): any { + transform( + value: any[], + sortOrder: SortOrder | string = 'asc', + sortKey?: string, + ): any { sortOrder = sortOrder && (sortOrder.toLowerCase() as any); if (!value || (sortOrder !== 'asc' && sortOrder !== 'desc')) return value; @@ -17,7 +21,9 @@ export class SortPipe implements PipeTransform { numberArray = value.filter(item => typeof item === 'number').sort(); stringArray = value.filter(item => typeof item === 'string').sort(); } else { - numberArray = value.filter(item => typeof item[sortKey] === 'number').sort((a, b) => a[sortKey] - b[sortKey]); + numberArray = value + .filter(item => typeof item[sortKey] === 'number') + .sort((a, b) => a[sortKey] - b[sortKey]); stringArray = value .filter(item => typeof item[sortKey] === 'string') .sort((a, b) => { @@ -26,7 +32,15 @@ export class SortPipe implements PipeTransform { else return 0; }); } - const sorted = numberArray.concat(stringArray); + const sorted = [ + ...numberArray, + ...stringArray, + ...value.filter( + item => + typeof item[sortKey] !== 'number' && + typeof item[sortKey] !== 'string', + ), + ]; return sortOrder === 'asc' ? sorted : sorted.reverse(); } } From 2f534545bac1d45d8f5e5fd99fee982765db6a4d Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 27 Nov 2019 17:00:08 +0300 Subject: [PATCH 3/3] fix(core): sort pipe key error --- .../packages/core/src/lib/pipes/sort.pipe.ts | 4 +- .../core/src/lib/tests/config.state.spec.ts | 138 ++++++++++++++---- .../core/src/lib/tests/sort.pipe.spec.ts | 24 ++- 3 files changed, 130 insertions(+), 36 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts index 984279f1a4..4d634f33c7 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts @@ -37,8 +37,8 @@ export class SortPipe implements PipeTransform { ...stringArray, ...value.filter( item => - typeof item[sortKey] !== 'number' && - typeof item[sortKey] !== 'string', + typeof (sortKey ? item[sortKey] : item) !== 'number' && + typeof (sortKey ? item[sortKey] : item) !== 'string', ), ]; return sortOrder === 'asc' ? sorted : sorted.reverse(); diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts index a7bae167b1..1fd2e45b35 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts @@ -1,8 +1,15 @@ -import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; +import { + createServiceFactory, + SpectatorService, + SpyObject, +} from '@ngneat/spectator/jest'; import { Store } from '@ngxs/store'; import { ReplaySubject, timer, Subject, of } from 'rxjs'; import { Config } from '../models/config'; -import { ApplicationConfigurationService, ConfigStateService } from '../services'; +import { + ApplicationConfigurationService, + ConfigStateService, +} from '../services'; import { ConfigState } from '../states'; import { SetLanguage, PatchRouteByName } from '../actions'; @@ -91,6 +98,7 @@ export const CONFIG_STATE_DATA = { }, grantedPolicies: { 'Abp.Identity': false, + 'Abp.Account': true, }, }, setting: { @@ -126,7 +134,10 @@ describe('ConfigState', () => { store = spectator.get(Store); service = spectator.service; appConfigService = spectator.get(ApplicationConfigurationService); - state = new ConfigState(spectator.get(ApplicationConfigurationService), store); + state = new ConfigState( + spectator.get(ApplicationConfigurationService), + store, + ); }); describe('#getAll', () => { @@ -137,24 +148,34 @@ describe('ConfigState', () => { describe('#getApplicationInfo', () => { it('should return application property', () => { - expect(ConfigState.getApplicationInfo(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA.environment.application); + expect(ConfigState.getApplicationInfo(CONFIG_STATE_DATA)).toEqual( + CONFIG_STATE_DATA.environment.application, + ); }); }); describe('#getOne', () => { it('should return one property', () => { - expect(ConfigState.getOne('environment')(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA.environment); + expect(ConfigState.getOne('environment')(CONFIG_STATE_DATA)).toEqual( + CONFIG_STATE_DATA.environment, + ); }); }); describe('#getDeep', () => { it('should return deeper', () => { - expect(ConfigState.getDeep('environment.localization.defaultResourceName')(CONFIG_STATE_DATA)).toEqual( - CONFIG_STATE_DATA.environment.localization.defaultResourceName, - ); - expect(ConfigState.getDeep(['environment', 'localization', 'defaultResourceName'])(CONFIG_STATE_DATA)).toEqual( - CONFIG_STATE_DATA.environment.localization.defaultResourceName, - ); + expect( + ConfigState.getDeep('environment.localization.defaultResourceName')( + CONFIG_STATE_DATA, + ), + ).toEqual(CONFIG_STATE_DATA.environment.localization.defaultResourceName); + expect( + ConfigState.getDeep([ + 'environment', + 'localization', + 'defaultResourceName', + ])(CONFIG_STATE_DATA), + ).toEqual(CONFIG_STATE_DATA.environment.localization.defaultResourceName); expect(ConfigState.getDeep('test')(null)).toBeFalsy(); }); @@ -162,21 +183,33 @@ describe('ConfigState', () => { describe('#getRoute', () => { it('should return route', () => { - expect(ConfigState.getRoute(null, '::Menu:Home')(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA.flattedRoutes[0]); - expect(ConfigState.getRoute('identity')(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA.flattedRoutes[1]); + expect( + ConfigState.getRoute(null, '::Menu:Home')(CONFIG_STATE_DATA), + ).toEqual(CONFIG_STATE_DATA.flattedRoutes[0]); + expect(ConfigState.getRoute('identity')(CONFIG_STATE_DATA)).toEqual( + CONFIG_STATE_DATA.flattedRoutes[1], + ); }); }); describe('#getApiUrl', () => { it('should return api url', () => { - expect(ConfigState.getApiUrl('other')(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA.environment.apis.other.url); - expect(ConfigState.getApiUrl()(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA.environment.apis.default.url); + expect(ConfigState.getApiUrl('other')(CONFIG_STATE_DATA)).toEqual( + CONFIG_STATE_DATA.environment.apis.other.url, + ); + expect(ConfigState.getApiUrl()(CONFIG_STATE_DATA)).toEqual( + CONFIG_STATE_DATA.environment.apis.default.url, + ); }); }); describe('#getSetting', () => { it('should return a setting', () => { - expect(ConfigState.getSetting('Abp.Localization.DefaultLanguage')(CONFIG_STATE_DATA)).toEqual( + expect( + ConfigState.getSetting('Abp.Localization.DefaultLanguage')( + CONFIG_STATE_DATA, + ), + ).toEqual( CONFIG_STATE_DATA.setting.values['Abp.Localization.DefaultLanguage'], ); }); @@ -184,41 +217,82 @@ describe('ConfigState', () => { describe('#getSettings', () => { it('should return settings', () => { - expect(ConfigState.getSettings('Localization')(CONFIG_STATE_DATA)).toEqual({ + expect( + ConfigState.getSettings('Localization')(CONFIG_STATE_DATA), + ).toEqual({ 'Abp.Localization.DefaultLanguage': 'en', }); - expect(ConfigState.getSettings('AllSettings')(CONFIG_STATE_DATA)).toEqual(CONFIG_STATE_DATA.setting.values); + expect(ConfigState.getSettings('AllSettings')(CONFIG_STATE_DATA)).toEqual( + CONFIG_STATE_DATA.setting.values, + ); }); }); describe('#getGrantedPolicy', () => { it('should return a granted policy', () => { - expect(ConfigState.getGrantedPolicy('Abp.Identity')(CONFIG_STATE_DATA)).toBe(false); + expect( + ConfigState.getGrantedPolicy('Abp.Identity')(CONFIG_STATE_DATA), + ).toBe(false); + expect( + ConfigState.getGrantedPolicy('Abp.Identity || Abp.Account')( + CONFIG_STATE_DATA, + ), + ).toBe(true); + expect( + ConfigState.getGrantedPolicy('Abp.Account && Abp.Identity')( + CONFIG_STATE_DATA, + ), + ).toBe(false); + expect( + ConfigState.getGrantedPolicy('Abp.Account &&')(CONFIG_STATE_DATA), + ).toBe(false); + expect( + ConfigState.getGrantedPolicy('|| Abp.Account')(CONFIG_STATE_DATA), + ).toBe(false); expect(ConfigState.getGrantedPolicy('')(CONFIG_STATE_DATA)).toBe(true); }); }); describe('#getLocalization', () => { it('should return a localization', () => { - expect(ConfigState.getLocalization('AbpIdentity::Identity')(CONFIG_STATE_DATA)).toBe('identity'); - - expect(ConfigState.getLocalization('AbpIdentity::NoIdentity')(CONFIG_STATE_DATA)).toBe('AbpIdentity::NoIdentity'); - - expect(ConfigState.getLocalization({ key: '', defaultValue: 'default' })(CONFIG_STATE_DATA)).toBe('default'); - - expect(ConfigState.getLocalization("::'{0}' and '{1}' do not match.", 'first', 'second')(CONFIG_STATE_DATA)).toBe( - 'first and second do not match.', - ); + expect( + ConfigState.getLocalization('AbpIdentity::Identity')(CONFIG_STATE_DATA), + ).toBe('identity'); + + expect( + ConfigState.getLocalization('AbpIdentity::NoIdentity')( + CONFIG_STATE_DATA, + ), + ).toBe('AbpIdentity::NoIdentity'); + + expect( + ConfigState.getLocalization({ key: '', defaultValue: 'default' })( + CONFIG_STATE_DATA, + ), + ).toBe('default'); + + expect( + ConfigState.getLocalization( + "::'{0}' and '{1}' do not match.", + 'first', + 'second', + )(CONFIG_STATE_DATA), + ).toBe('first and second do not match.'); try { ConfigState.getLocalization('::Test')({ ...CONFIG_STATE_DATA, - environment: { ...CONFIG_STATE_DATA.environment, localization: {} as any }, + environment: { + ...CONFIG_STATE_DATA.environment, + localization: {} as any, + }, }); expect(false).toBeTruthy(); // fail } catch (error) { - expect((error as Error).message).toContain('Please check your environment'); + expect((error as Error).message).toContain( + 'Please check your environment', + ); } }); }); @@ -273,7 +347,9 @@ describe('ConfigState', () => { name: 'Home', path: 'home', url: '/home', - children: [{ path: 'dashboard', name: 'Dashboard', url: '/home/dashboard' }], + children: [ + { path: 'dashboard', name: 'Dashboard', url: '/home/dashboard' }, + ], }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/sort.pipe.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/sort.pipe.spec.ts index 8428515118..b69d139091 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/sort.pipe.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/sort.pipe.spec.ts @@ -17,10 +17,28 @@ describe('SortPipe', () => { }); test('should sort object array in given order with given key', () => { - const array = [{ key: 5 }, { key: 'b' }, { key: 1 }, { key: 'a' }]; + const array = [ + { key: 5 }, + { key: 'b' }, + { key: 1 }, + { key: 'a' }, + { key: null }, + ]; - expect(pipe.transform(array, 'asc', 'key')).toEqual([{ key: 1 }, { key: 5 }, { key: 'a' }, { key: 'b' }]); - expect(pipe.transform(array, 'desc', 'key')).toEqual([{ key: 'b' }, { key: 'a' }, { key: 5 }, { key: 1 }]); + expect(pipe.transform(array, 'asc', 'key')).toEqual([ + { key: 1 }, + { key: 5 }, + { key: 'a' }, + { key: 'b' }, + { key: null }, + ]); + expect(pipe.transform(array, 'desc', 'key')).toEqual([ + { key: null }, + { key: 'b' }, + { key: 'a' }, + { key: 5 }, + { key: 1 }, + ]); }); test('should require an array as value', () => {