Browse Source

Merge pull request #2425 from abpframework/feat/add-route-action

feat/add-route-action
pull/2440/head
Mehmet Erim 7 years ago
committed by GitHub
parent
commit
3dbb938e5e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      npm/ng-packs/packages/core/src/lib/actions/config.actions.ts
  2. 19
      npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts
  3. 158
      npm/ng-packs/packages/core/src/lib/states/config.state.ts
  4. 13
      npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts

8
npm/ng-packs/packages/core/src/lib/actions/config.actions.ts

@ -8,3 +8,11 @@ export class PatchRouteByName {
export class GetAppConfiguration { export class GetAppConfiguration {
static readonly type = '[Config] Get App Configuration'; static readonly type = '[Config] Get App Configuration';
} }
/**
* @see usage: https://github.com/abpframework/abp/pull/2425#issue-355018812
*/
export class AddRoute {
static readonly type = '[Config] Add Route';
constructor(public payload: Omit<ABP.Route, 'children'>) {}
}

19
npm/ng-packs/packages/core/src/lib/plugins/config.plugin.ts

@ -1,6 +1,13 @@
import { Inject, Injectable, InjectionToken } from '@angular/core'; import { Inject, Injectable, InjectionToken } from '@angular/core';
import { Router, Routes } from '@angular/router'; import { Router, Routes } from '@angular/router';
import { actionMatcher, InitState, NgxsNextPluginFn, NgxsPlugin, setValue, UpdateState } from '@ngxs/store'; import {
actionMatcher,
InitState,
NgxsNextPluginFn,
NgxsPlugin,
setValue,
UpdateState,
} from '@ngxs/store';
import snq from 'snq'; import snq from 'snq';
import { ABP } from '../models'; import { ABP } from '../models';
import { organizeRoutes, getAbpRoutes } from '../utils/route-utils'; import { organizeRoutes, getAbpRoutes } from '../utils/route-utils';
@ -12,7 +19,10 @@ export const NGXS_CONFIG_PLUGIN_OPTIONS = new InjectionToken('NGXS_CONFIG_PLUGIN
export class ConfigPlugin implements NgxsPlugin { export class ConfigPlugin implements NgxsPlugin {
private initialized = false; private initialized = false;
constructor(@Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root, private router: Router) {} constructor(
@Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root,
private router: Router,
) {}
handle(state: any, event: any, next: NgxsNextPluginFn) { handle(state: any, event: any, next: NgxsNextPluginFn) {
const matches = actionMatcher(event); const matches = actionMatcher(event);
@ -54,7 +64,9 @@ function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): a
routes routes
.filter(route => route.component || route.loadChildren) .filter(route => route.component || route.loadChildren)
.forEach(route => { .forEach(route => {
const abpPackage = abpRoutes.find(abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper); const abpPackage = abpRoutes.find(
abp => abp.path.toLowerCase() === route.path.toLowerCase() && !abp.wrapper,
);
const { length } = transformed; const { length } = transformed;
@ -104,6 +116,7 @@ function flatRoutes(routes: ABP.FullRoute[]): ABP.FullRoute[] {
return r.reduce((acc, val) => { return r.reduce((acc, val) => {
let value: ABP.FullRoute[] = [val]; let value: ABP.FullRoute[] = [val];
if (val.children) { if (val.children) {
val.children = val.children.map(child => ({ ...child, parentName: val.name }));
value = [val, ...flat(val.children)]; value = [val, ...flat(val.children)];
} }

158
npm/ng-packs/packages/core/src/lib/states/config.state.ts

@ -1,18 +1,8 @@
import { import { Action, createSelector, Selector, State, StateContext, Store } from '@ngxs/store';
Action,
createSelector,
Selector,
State,
StateContext,
Store,
} from '@ngxs/store';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators'; import { switchMap, tap } from 'rxjs/operators';
import snq from 'snq'; import snq from 'snq';
import { import { GetAppConfiguration, PatchRouteByName, AddRoute } from '../actions/config.actions';
GetAppConfiguration,
PatchRouteByName,
} from '../actions/config.actions';
import { SetLanguage } from '../actions/session.actions'; import { SetLanguage } from '../actions/session.actions';
import { ABP } from '../models/common'; import { ABP } from '../models/common';
import { Config } from '../models/config'; import { Config } from '../models/config';
@ -83,12 +73,9 @@ export class ConfigState {
} }
static getApiUrl(key?: string) { static getApiUrl(key?: string) {
const selector = createSelector( const selector = createSelector([ConfigState], (state: Config.State): string => {
[ConfigState], return state.environment.apis[key || 'default'].url;
(state: Config.State): string => { });
return state.environment.apis[key || 'default'].url;
},
);
return selector; return selector;
} }
@ -104,18 +91,12 @@ export class ConfigState {
const selector = createSelector([ConfigState], (state: Config.State) => { const selector = createSelector([ConfigState], (state: Config.State) => {
if (keyword) { if (keyword) {
const keys = snq( const keys = snq(
() => () => Object.keys(state.setting.values).filter(key => key.indexOf(keyword) > -1),
Object.keys(state.setting.values).filter(
key => key.indexOf(keyword) > -1,
),
[], [],
); );
if (keys.length) { if (keys.length) {
return keys.reduce( return keys.reduce((acc, key) => ({ ...acc, [key]: state.setting.values[key] }), {});
(acc, key) => ({ ...acc, [key]: state.setting.values[key] }),
{},
);
} }
} }
@ -125,32 +106,29 @@ export class ConfigState {
} }
static getGrantedPolicy(key: string) { static getGrantedPolicy(key: string) {
const selector = createSelector( const selector = createSelector([ConfigState], (state: Config.State): boolean => {
[ConfigState], if (!key) return true;
(state: Config.State): boolean => { const getPolicy = k => snq(() => state.auth.grantedPolicies[k], false);
if (!key) return true;
const getPolicy = k => snq(() => state.auth.grantedPolicies[k], false);
const orRegexp = /\|\|/g; const orRegexp = /\|\|/g;
const andRegexp = /&&/g; const andRegexp = /&&/g;
if (orRegexp.test(key)) { if (orRegexp.test(key)) {
const keys = key.split('||').filter(k => !!k); const keys = key.split('||').filter(k => !!k);
if (keys.length !== 2) return false; if (keys.length !== 2) return false;
return getPolicy(keys[0].trim()) || getPolicy(keys[1].trim()); return getPolicy(keys[0].trim()) || getPolicy(keys[1].trim());
} else if (andRegexp.test(key)) { } else if (andRegexp.test(key)) {
const keys = key.split('&&').filter(k => !!k); const keys = key.split('&&').filter(k => !!k);
if (keys.length !== 2) return false; if (keys.length !== 2) return false;
return getPolicy(keys[0].trim()) && getPolicy(keys[1].trim()); return getPolicy(keys[0].trim()) && getPolicy(keys[1].trim());
} }
return getPolicy(key); return getPolicy(key);
}, });
);
return selector; return selector;
} }
@ -225,8 +203,7 @@ export class ConfigState {
}), }),
), ),
switchMap(configuration => { switchMap(configuration => {
let defaultLang: string = let defaultLang: string = configuration.setting.values['Abp.Localization.DefaultLanguage'];
configuration.setting.values['Abp.Localization.DefaultLanguage'];
if (defaultLang.includes(';')) { if (defaultLang.includes(';')) {
defaultLang = defaultLang.split(';')[0]; defaultLang = defaultLang.split(';')[0];
@ -246,12 +223,73 @@ export class ConfigState {
) { ) {
let routes: ABP.FullRoute[] = getState().routes; let routes: ABP.FullRoute[] = getState().routes;
const index = routes.findIndex(route => route.name === name);
routes = patchRouteDeep(routes, name, newValue); routes = patchRouteDeep(routes, name, newValue);
const flattedRoutes = getState().flattedRoutes;
const index = flattedRoutes.findIndex(route => route.name === name);
if (index > -1) {
flattedRoutes[index] = newValue as ABP.FullRoute;
}
return patchState({
routes,
flattedRoutes,
});
}
@Action(AddRoute)
addRoute({ patchState, getState }: StateContext<Config.State>, { payload }: AddRoute) {
let routes: ABP.FullRoute[] = getState().routes;
const flattedRoutes = getState().flattedRoutes;
const route: ABP.FullRoute = { ...payload };
if (route.parentName) {
const index = flattedRoutes.findIndex(r => r.name === route.parentName);
if (index < 0) return;
const parent = flattedRoutes[index];
if (parent.url.replace('/', '')) {
route.url = `${parent.url}/${route.path}`;
} else {
route.url = `/${route.path}`;
}
route.order = route.order || route.order === 0 ? route.order : parent.children.length;
parent.children = [...(parent.children || []), route].sort((a, b) => a.order - b.order);
flattedRoutes[index] = parent;
flattedRoutes.push(route);
let parentName = parent.name;
const parentNameArr = [parentName];
while (parentName) {
parentName = snq(() => flattedRoutes.find(r => r.name === parentName).parentName);
if (parentName) {
parentNameArr.unshift(parentName);
}
}
routes = updateRouteDeep(routes, parentNameArr, parent);
} else {
route.url = `/${route.path}`;
if (route.order || route.order === 0) {
routes = [...routes, route].sort((a, b) => a.order - b.order);
} else {
route.order = routes.length;
routes = [...routes, route];
}
flattedRoutes.push(route);
}
return patchState({ return patchState({
routes, routes,
flattedRoutes,
}); });
} }
} }
@ -295,3 +333,25 @@ function patchRouteDeep(
return organizeRoutes(routes); return organizeRoutes(routes);
} }
function updateRouteDeep(
routes: ABP.FullRoute[],
parentNameArr: string[],
newValue: ABP.FullRoute,
parentIndex = 0,
) {
const index = routes.findIndex(route => route.name === parentNameArr[parentIndex]);
if (parentIndex === parentNameArr.length - 1) {
routes[index] = newValue;
} else {
routes[index].children = updateRouteDeep(
routes[index].children,
parentNameArr,
newValue,
parentIndex + 1,
);
}
return routes;
}

13
npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts

@ -170,6 +170,7 @@ const expectedState = {
path: 'users', path: 'users',
name: 'AbpIdentity::Users', name: 'AbpIdentity::Users',
order: 1, order: 1,
parentName: 'AbpIdentity::Menu:IdentityManagement',
requiredPolicy: 'AbpIdentity.Users', requiredPolicy: 'AbpIdentity.Users',
url: '/identity/users', url: '/identity/users',
}, },
@ -177,6 +178,7 @@ const expectedState = {
path: 'roles', path: 'roles',
name: 'AbpIdentity::Roles', name: 'AbpIdentity::Roles',
order: 2, order: 2,
parentName: 'AbpIdentity::Menu:IdentityManagement',
requiredPolicy: 'AbpIdentity.Roles', requiredPolicy: 'AbpIdentity.Roles',
url: '/identity/roles', url: '/identity/roles',
}, },
@ -194,6 +196,7 @@ const expectedState = {
path: 'tenants', path: 'tenants',
name: 'AbpTenantManagement::Tenants', name: 'AbpTenantManagement::Tenants',
order: 1, order: 1,
parentName: 'AbpTenantManagement::Menu:TenantManagement',
requiredPolicy: 'AbpTenantManagement.Tenants', requiredPolicy: 'AbpTenantManagement.Tenants',
url: '/tenant-management/tenants', url: '/tenant-management/tenants',
}, },
@ -215,6 +218,7 @@ const expectedState = {
path: 'users', path: 'users',
name: 'AbpIdentity::Users', name: 'AbpIdentity::Users',
order: 1, order: 1,
parentName: 'AbpIdentity::Menu:IdentityManagement',
requiredPolicy: 'AbpIdentity.Users', requiredPolicy: 'AbpIdentity.Users',
url: '/identity/users', url: '/identity/users',
}, },
@ -222,6 +226,7 @@ const expectedState = {
path: 'roles', path: 'roles',
name: 'AbpIdentity::Roles', name: 'AbpIdentity::Roles',
order: 2, order: 2,
parentName: 'AbpIdentity::Menu:IdentityManagement',
requiredPolicy: 'AbpIdentity.Roles', requiredPolicy: 'AbpIdentity.Roles',
url: '/identity/roles', url: '/identity/roles',
}, },
@ -232,6 +237,7 @@ const expectedState = {
path: 'users', path: 'users',
name: 'AbpIdentity::Users', name: 'AbpIdentity::Users',
order: 1, order: 1,
parentName: 'AbpIdentity::Menu:IdentityManagement',
requiredPolicy: 'AbpIdentity.Users', requiredPolicy: 'AbpIdentity.Users',
url: '/identity/users', url: '/identity/users',
}, },
@ -239,6 +245,7 @@ const expectedState = {
path: 'roles', path: 'roles',
name: 'AbpIdentity::Roles', name: 'AbpIdentity::Roles',
order: 2, order: 2,
parentName: 'AbpIdentity::Menu:IdentityManagement',
requiredPolicy: 'AbpIdentity.Roles', requiredPolicy: 'AbpIdentity.Roles',
url: '/identity/roles', url: '/identity/roles',
}, },
@ -253,6 +260,7 @@ const expectedState = {
path: 'tenants', path: 'tenants',
name: 'AbpTenantManagement::Tenants', name: 'AbpTenantManagement::Tenants',
order: 1, order: 1,
parentName: 'AbpTenantManagement::Menu:TenantManagement',
requiredPolicy: 'AbpTenantManagement.Tenants', requiredPolicy: 'AbpTenantManagement.Tenants',
url: '/tenant-management/tenants', url: '/tenant-management/tenants',
}, },
@ -264,6 +272,7 @@ const expectedState = {
path: 'tenants', path: 'tenants',
name: 'AbpTenantManagement::Tenants', name: 'AbpTenantManagement::Tenants',
order: 1, order: 1,
parentName: 'AbpTenantManagement::Menu:TenantManagement',
requiredPolicy: 'AbpTenantManagement.Tenants', requiredPolicy: 'AbpTenantManagement.Tenants',
url: '/tenant-management/tenants', url: '/tenant-management/tenants',
}, },
@ -277,12 +286,14 @@ const expectedState = {
path: 'login', path: 'login',
name: 'AbpAccount::Login', name: 'AbpAccount::Login',
order: 1, order: 1,
parentName: 'AbpAccount::Menu:Account',
url: '/account/login', url: '/account/login',
}, },
{ {
path: 'register', path: 'register',
name: 'AbpAccount::Register', name: 'AbpAccount::Register',
order: 2, order: 2,
parentName: 'AbpAccount::Menu:Account',
url: '/account/register', url: '/account/register',
}, },
], ],
@ -293,12 +304,14 @@ const expectedState = {
path: 'login', path: 'login',
name: 'AbpAccount::Login', name: 'AbpAccount::Login',
order: 1, order: 1,
parentName: 'AbpAccount::Menu:Account',
url: '/account/login', url: '/account/login',
}, },
{ {
path: 'register', path: 'register',
name: 'AbpAccount::Register', name: 'AbpAccount::Register',
order: 2, order: 2,
parentName: 'AbpAccount::Menu:Account',
url: '/account/register', url: '/account/register',
}, },
], ],

Loading…
Cancel
Save