mirror of https://github.com/abpframework/abp.git
33 changed files with 5099 additions and 1 deletions
@ -0,0 +1,36 @@ |
|||
{ |
|||
"extends": ["../../.eslintrc.json"], |
|||
"ignorePatterns": ["!**/*"], |
|||
"overrides": [ |
|||
{ |
|||
"files": ["*.ts"], |
|||
"extends": [ |
|||
"plugin:@nrwl/nx/angular", |
|||
"plugin:@angular-eslint/template/process-inline-templates" |
|||
], |
|||
"rules": { |
|||
"@angular-eslint/directive-selector": [ |
|||
"error", |
|||
{ |
|||
"type": "attribute", |
|||
"prefix": "abp", |
|||
"style": "camelCase" |
|||
} |
|||
], |
|||
"@angular-eslint/component-selector": [ |
|||
"error", |
|||
{ |
|||
"type": "element", |
|||
"prefix": "abp", |
|||
"style": "kebab-case" |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"files": ["*.html"], |
|||
"extends": ["plugin:@nrwl/nx/angular-template"], |
|||
"rules": {} |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<h1> @abp/ng.permission-management </h1> |
|||
|
|||
[docs.abp.io](https://docs.abp.io) |
|||
@ -0,0 +1,20 @@ |
|||
module.exports = { |
|||
displayName: 'permission-management', |
|||
preset: '../../jest.preset.js', |
|||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], |
|||
globals: { |
|||
'ts-jest': { |
|||
tsconfig: '<rootDir>/tsconfig.spec.json', |
|||
stringifyContentPathRegex: '\\.(html|svg)$', |
|||
}, |
|||
}, |
|||
coverageDirectory: '../../coverage/packages/permission-management', |
|||
transform: { |
|||
'^.+\\.(ts|js|html)$': 'jest-preset-angular', |
|||
}, |
|||
snapshotSerializers: [ |
|||
'jest-preset-angular/build/serializers/no-ng-attributes', |
|||
'jest-preset-angular/build/serializers/ng-snapshot', |
|||
'jest-preset-angular/build/serializers/html-comment', |
|||
], |
|||
}; |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../dist/packages/permission-management", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
}, |
|||
"allowedNonPeerDependencies": ["@abp/ng.theme.shared"] |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
{ |
|||
"name": "@abp/ng.permission-management", |
|||
"version": "4.4.0", |
|||
"homepage": "https://abp.io", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/abpframework/abp.git" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/ng.theme.shared": "~4.4.0", |
|||
"tslib": "^2.0.0" |
|||
}, |
|||
"publishConfig": { |
|||
"access": "public" |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './permission-management.actions'; |
|||
@ -0,0 +1,11 @@ |
|||
import { ProviderInfoDto, UpdatePermissionsDto } from '../proxy/models'; |
|||
|
|||
export class GetPermissions { |
|||
static readonly type = '[PermissionManagement] Get Permissions'; |
|||
constructor(public payload: ProviderInfoDto) {} |
|||
} |
|||
|
|||
export class UpdatePermissions { |
|||
static readonly type = '[PermissionManagement] Update Permissions'; |
|||
constructor(public payload: ProviderInfoDto & UpdatePermissionsDto) {} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './permission-management.component'; |
|||
@ -0,0 +1,105 @@ |
|||
<abp-modal [(visible)]="visible" (init)="initModal()" [busy]="modalBusy"> |
|||
<ng-container *ngIf="{ entityName: entityName$ | async } as data"> |
|||
<ng-template #abpHeader> |
|||
<h4> |
|||
{{ 'AbpPermissionManagement::Permissions' | abpLocalization }} - {{ data.entityName }} |
|||
</h4> |
|||
</ng-template> |
|||
<ng-template #abpBody> |
|||
<div class="custom-checkbox custom-control mb-2"> |
|||
<input |
|||
type="checkbox" |
|||
id="select-all-in-all-tabs" |
|||
name="select-all-in-all-tabs" |
|||
class="custom-control-input" |
|||
[(ngModel)]="selectAllTab" |
|||
(click)="onClickSelectAll()" |
|||
/> |
|||
<label class="custom-control-label" for="select-all-in-all-tabs">{{ |
|||
'AbpPermissionManagement::SelectAllInAllTabs' | abpLocalization |
|||
}}</label> |
|||
</div> |
|||
|
|||
<hr class="mt-2 mb-2" /> |
|||
<div class="row"> |
|||
<div class="overflow-scroll col-md-4"> |
|||
<ul class="nav nav-pills flex-column"> |
|||
<li *ngFor="let group of groups$ | async; trackBy: trackByFn" class="nav-item"> |
|||
<a |
|||
*ngIf="{ assignedCount: getAssignedCount(group.name) } as data" |
|||
class="nav-link pointer" |
|||
[class.active]="selectedGroup?.name === group?.name" |
|||
(click)="onChangeGroup(group)" |
|||
> |
|||
<div [class.font-weight-bold]="data.assignedCount"> |
|||
{{ group?.displayName }} |
|||
<span>({{ data.assignedCount }})</span> |
|||
</div> |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="col-md-8 overflow-scroll"> |
|||
<h4>{{ selectedGroup?.displayName }}</h4> |
|||
<hr class="mt-2 mb-3" /> |
|||
<div class="pl-1 pt-1"> |
|||
<div class="custom-checkbox custom-control mb-2"> |
|||
<input |
|||
type="checkbox" |
|||
id="select-all-in-this-tabs" |
|||
name="select-all-in-this-tabs" |
|||
class="custom-control-input" |
|||
[(ngModel)]="selectThisTab" |
|||
(click)="onClickSelectThisTab()" |
|||
/> |
|||
<label class="custom-control-label" for="select-all-in-this-tabs">{{ |
|||
'AbpPermissionManagement::SelectAllInThisTab' | abpLocalization |
|||
}}</label> |
|||
</div> |
|||
<hr class="mb-3" /> |
|||
<div |
|||
*ngFor=" |
|||
let permission of selectedGroupPermissions$ | async; |
|||
let i = index; |
|||
trackBy: trackByFn |
|||
" |
|||
[ngStyle]="permission.style" |
|||
class="custom-checkbox custom-control mb-2" |
|||
> |
|||
<input |
|||
#permissionCheckbox |
|||
type="checkbox" |
|||
[checked]="getChecked(permission.name)" |
|||
[value]="getChecked(permission.name)" |
|||
[attr.id]="permission.name" |
|||
class="custom-control-input" |
|||
[disabled]="isGrantedByOtherProviderName(permission.grantedProviders)" |
|||
/> |
|||
<label |
|||
class="custom-control-label" |
|||
[attr.for]="permission.name" |
|||
(click)="onClickCheckbox(permission, permissionCheckbox.value)" |
|||
>{{ permission.displayName }} |
|||
<ng-container *ngIf="!hideBadges"> |
|||
<span |
|||
*ngFor="let provider of permission.grantedProviders" |
|||
class="badge badge-light" |
|||
>{{ provider.providerName }}: {{ provider.providerKey }}</span |
|||
> |
|||
</ng-container> |
|||
</label> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</ng-template> |
|||
<ng-template #abpFooter> |
|||
<button type="button" class="btn btn-secondary" abpClose> |
|||
{{ 'AbpIdentity::Cancel' | abpLocalization }} |
|||
</button> |
|||
<abp-button iconClass="fa fa-check" (click)="submit()">{{ |
|||
'AbpIdentity::Save' | abpLocalization |
|||
}}</abp-button> |
|||
</ng-template> |
|||
</ng-container> |
|||
</abp-modal> |
|||
@ -0,0 +1,324 @@ |
|||
import { |
|||
AbpApplicationConfigurationService, |
|||
ConfigStateService, |
|||
CurrentUserDto, |
|||
} from '@abp/ng.core'; |
|||
import { LocaleDirection } from '@abp/ng.theme.shared'; |
|||
import { Component, EventEmitter, Input, Output, TrackByFunction } from '@angular/core'; |
|||
import { Select, Store } from '@ngxs/store'; |
|||
import { Observable, of } from 'rxjs'; |
|||
import { finalize, map, pluck, switchMap, take, tap } from 'rxjs/operators'; |
|||
import { GetPermissions, UpdatePermissions } from '../actions/permission-management.actions'; |
|||
import { PermissionManagement } from '../models/permission-management'; |
|||
import { |
|||
GetPermissionListResultDto, |
|||
PermissionGrantInfoDto, |
|||
PermissionGroupDto, |
|||
ProviderInfoDto, |
|||
UpdatePermissionDto, |
|||
} from '../proxy/models'; |
|||
import { PermissionManagementState } from '../states/permission-management.state'; |
|||
|
|||
type PermissionWithStyle = PermissionGrantInfoDto & { |
|||
style: string; |
|||
}; |
|||
|
|||
@Component({ |
|||
selector: 'abp-permission-management', |
|||
templateUrl: './permission-management.component.html', |
|||
exportAs: 'abpPermissionManagement', |
|||
styles: [ |
|||
` |
|||
.overflow-scroll { |
|||
max-height: 70vh; |
|||
overflow-y: scroll; |
|||
} |
|||
`,
|
|||
], |
|||
}) |
|||
export class PermissionManagementComponent |
|||
implements |
|||
PermissionManagement.PermissionManagementComponentInputs, |
|||
PermissionManagement.PermissionManagementComponentOutputs { |
|||
@Input() |
|||
readonly providerName: string; |
|||
|
|||
@Input() |
|||
readonly providerKey: string; |
|||
|
|||
@Input() |
|||
readonly hideBadges = false; |
|||
|
|||
protected _visible = false; |
|||
|
|||
@Input() |
|||
get visible(): boolean { |
|||
return this._visible; |
|||
} |
|||
|
|||
set visible(value: boolean) { |
|||
if (value === this._visible) return; |
|||
|
|||
if (value) { |
|||
this.openModal().subscribe(() => { |
|||
this._visible = true; |
|||
this.visibleChange.emit(true); |
|||
}); |
|||
} else { |
|||
this.selectedGroup = null; |
|||
this._visible = false; |
|||
this.visibleChange.emit(false); |
|||
} |
|||
} |
|||
|
|||
@Output() readonly visibleChange = new EventEmitter<boolean>(); |
|||
|
|||
@Select(PermissionManagementState.getPermissionGroups) |
|||
groups$: Observable<PermissionGroupDto[]>; |
|||
|
|||
@Select(PermissionManagementState.getEntityDisplayName) |
|||
entityName$: Observable<string>; |
|||
|
|||
selectedGroup: PermissionGroupDto; |
|||
|
|||
permissions: PermissionGrantInfoDto[] = []; |
|||
|
|||
selectThisTab = false; |
|||
|
|||
selectAllTab = false; |
|||
|
|||
modalBusy = false; |
|||
|
|||
trackByFn: TrackByFunction<PermissionGroupDto> = (_, item) => item.name; |
|||
|
|||
get selectedGroupPermissions$(): Observable<PermissionWithStyle[]> { |
|||
const margin = `margin-${ |
|||
(document.body.dir as LocaleDirection) === 'rtl' ? 'right' : 'left' |
|||
}.px`;
|
|||
|
|||
return this.groups$.pipe( |
|||
map(groups => |
|||
this.selectedGroup |
|||
? groups.find(group => group.name === this.selectedGroup.name).permissions |
|||
: [], |
|||
), |
|||
map<PermissionGrantInfoDto[], PermissionWithStyle[]>(permissions => |
|||
permissions.map( |
|||
permission => |
|||
(({ |
|||
...permission, |
|||
style: { [margin]: findMargin(permissions, permission) }, |
|||
isGranted: this.permissions.find(per => per.name === permission.name).isGranted, |
|||
} as any) as PermissionWithStyle), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
|
|||
constructor( |
|||
protected store: Store, |
|||
protected configState: ConfigStateService, |
|||
protected appConfigService: AbpApplicationConfigurationService, |
|||
) {} |
|||
|
|||
getChecked(name: string) { |
|||
return (this.permissions.find(per => per.name === name) || { isGranted: false }).isGranted; |
|||
} |
|||
|
|||
isGrantedByOtherProviderName(grantedProviders: ProviderInfoDto[]): boolean { |
|||
if (grantedProviders.length) { |
|||
return grantedProviders.findIndex(p => p.providerName !== this.providerName) > -1; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
onClickCheckbox(clickedPermission: PermissionGrantInfoDto, value) { |
|||
if ( |
|||
clickedPermission.isGranted && |
|||
this.isGrantedByOtherProviderName(clickedPermission.grantedProviders) |
|||
) |
|||
return; |
|||
|
|||
setTimeout(() => { |
|||
this.permissions = this.permissions.map(per => { |
|||
if (clickedPermission.name === per.name) { |
|||
return { ...per, isGranted: !per.isGranted }; |
|||
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) { |
|||
return { ...per, isGranted: false }; |
|||
} else if (clickedPermission.parentName === per.name && !clickedPermission.isGranted) { |
|||
return { ...per, isGranted: true }; |
|||
} |
|||
|
|||
return per; |
|||
}); |
|||
|
|||
this.setTabCheckboxState(); |
|||
this.setGrantCheckboxState(); |
|||
}, 0); |
|||
} |
|||
|
|||
setTabCheckboxState() { |
|||
this.selectedGroupPermissions$.pipe(take(1)).subscribe(permissions => { |
|||
const selectedPermissions = permissions.filter(per => per.isGranted); |
|||
const element = document.querySelector('#select-all-in-this-tabs') as any; |
|||
|
|||
if (selectedPermissions.length === permissions.length) { |
|||
element.indeterminate = false; |
|||
this.selectThisTab = true; |
|||
} else if (selectedPermissions.length === 0) { |
|||
element.indeterminate = false; |
|||
this.selectThisTab = false; |
|||
} else { |
|||
element.indeterminate = true; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
setGrantCheckboxState() { |
|||
const selectedAllPermissions = this.permissions.filter(per => per.isGranted); |
|||
const checkboxElement = document.querySelector('#select-all-in-all-tabs') as any; |
|||
|
|||
if (selectedAllPermissions.length === this.permissions.length) { |
|||
checkboxElement.indeterminate = false; |
|||
this.selectAllTab = true; |
|||
} else if (selectedAllPermissions.length === 0) { |
|||
checkboxElement.indeterminate = false; |
|||
this.selectAllTab = false; |
|||
} else { |
|||
checkboxElement.indeterminate = true; |
|||
} |
|||
} |
|||
|
|||
onClickSelectThisTab() { |
|||
this.selectedGroupPermissions$.pipe(take(1)).subscribe(permissions => { |
|||
permissions.forEach(permission => { |
|||
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders)) |
|||
return; |
|||
|
|||
const index = this.permissions.findIndex(per => per.name === permission.name); |
|||
|
|||
this.permissions = [ |
|||
...this.permissions.slice(0, index), |
|||
{ ...this.permissions[index], isGranted: !this.selectThisTab }, |
|||
...this.permissions.slice(index + 1), |
|||
]; |
|||
}); |
|||
}); |
|||
|
|||
this.setGrantCheckboxState(); |
|||
} |
|||
|
|||
onClickSelectAll() { |
|||
this.permissions = this.permissions.map(permission => ({ |
|||
...permission, |
|||
isGranted: |
|||
this.isGrantedByOtherProviderName(permission.grantedProviders) || !this.selectAllTab, |
|||
})); |
|||
|
|||
this.selectThisTab = !this.selectAllTab; |
|||
} |
|||
|
|||
onChangeGroup(group: PermissionGroupDto) { |
|||
this.selectedGroup = group; |
|||
this.setTabCheckboxState(); |
|||
} |
|||
|
|||
submit() { |
|||
const unchangedPermissions = getPermissions( |
|||
this.store.selectSnapshot(PermissionManagementState.getPermissionGroups), |
|||
); |
|||
|
|||
const changedPermissions: UpdatePermissionDto[] = this.permissions |
|||
.filter(per => |
|||
unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted === |
|||
per.isGranted |
|||
? false |
|||
: true, |
|||
) |
|||
.map(({ name, isGranted }) => ({ name, isGranted })); |
|||
|
|||
if (!changedPermissions.length) { |
|||
this.visible = false; |
|||
return; |
|||
} |
|||
|
|||
this.modalBusy = true; |
|||
this.store |
|||
.dispatch( |
|||
new UpdatePermissions({ |
|||
providerKey: this.providerKey, |
|||
providerName: this.providerName, |
|||
permissions: changedPermissions, |
|||
}), |
|||
) |
|||
.pipe( |
|||
switchMap(() => |
|||
this.shouldFetchAppConfig() |
|||
? this.appConfigService.get().pipe(tap(res => this.configState.setState(res))) |
|||
: of(null), |
|||
), |
|||
finalize(() => (this.modalBusy = false)), |
|||
) |
|||
.subscribe(() => { |
|||
this.visible = false; |
|||
}); |
|||
} |
|||
|
|||
openModal() { |
|||
if (!this.providerKey || !this.providerName) { |
|||
throw new Error('Provider Key and Provider Name are required.'); |
|||
} |
|||
|
|||
return this.store |
|||
.dispatch( |
|||
new GetPermissions({ |
|||
providerKey: this.providerKey, |
|||
providerName: this.providerName, |
|||
}), |
|||
) |
|||
.pipe( |
|||
pluck('PermissionManagementState', 'permissionRes'), |
|||
tap((permissionRes: GetPermissionListResultDto) => { |
|||
this.selectedGroup = permissionRes.groups[0]; |
|||
this.permissions = getPermissions(permissionRes.groups); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
initModal() { |
|||
this.setTabCheckboxState(); |
|||
this.setGrantCheckboxState(); |
|||
} |
|||
|
|||
getAssignedCount(groupName: string) { |
|||
return this.permissions.reduce( |
|||
(acc, val) => (val.name.split('.')[0] === groupName && val.isGranted ? acc + 1 : acc), |
|||
0, |
|||
); |
|||
} |
|||
|
|||
shouldFetchAppConfig() { |
|||
const currentUser = this.configState.getOne('currentUser') as CurrentUserDto; |
|||
|
|||
if (this.providerName === 'R') return currentUser.roles.some(role => role === this.providerKey); |
|||
|
|||
if (this.providerName === 'U') return currentUser.id === this.providerKey; |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
function findMargin(permissions: PermissionGrantInfoDto[], permission: PermissionGrantInfoDto) { |
|||
const parentPermission = permissions.find(per => per.name === permission.parentName); |
|||
|
|||
if (parentPermission && parentPermission.parentName) { |
|||
let margin = 20; |
|||
return (margin += findMargin(permissions, parentPermission)); |
|||
} |
|||
|
|||
return parentPermission ? 20 : 0; |
|||
} |
|||
|
|||
function getPermissions(groups: PermissionGroupDto[]): PermissionGrantInfoDto[] { |
|||
return groups.reduce((acc, val) => [...acc, ...val.permissions], []); |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export const enum ePermissionManagementComponents { |
|||
PermissionManagement = 'PermissionManagement.PermissionManagementComponent', |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './permission-management'; |
|||
@ -0,0 +1,19 @@ |
|||
import { EventEmitter } from '@angular/core'; |
|||
import { GetPermissionListResultDto } from '../proxy/models'; |
|||
|
|||
export namespace PermissionManagement { |
|||
export interface State { |
|||
permissionRes: GetPermissionListResultDto; |
|||
} |
|||
|
|||
export interface PermissionManagementComponentInputs { |
|||
visible: boolean; |
|||
readonly providerName: string; |
|||
readonly providerKey: string; |
|||
readonly hideBadges: boolean; |
|||
} |
|||
|
|||
export interface PermissionManagementComponentOutputs { |
|||
readonly visibleChange: EventEmitter<boolean>; |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
import { CoreModule } from '@abp/ng.core'; |
|||
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
|||
import { NgModule } from '@angular/core'; |
|||
import { NgxsModule } from '@ngxs/store'; |
|||
import { PermissionManagementComponent } from './components/permission-management.component'; |
|||
import { PermissionManagementState } from './states/permission-management.state'; |
|||
|
|||
@NgModule({ |
|||
declarations: [PermissionManagementComponent], |
|||
imports: [CoreModule, ThemeSharedModule, NgxsModule.forFeature([PermissionManagementState])], |
|||
exports: [PermissionManagementComponent], |
|||
}) |
|||
export class PermissionManagementModule {} |
|||
@ -0,0 +1,17 @@ |
|||
# Proxy Generation Output |
|||
|
|||
This directory includes the output of the latest proxy generation. |
|||
The files and folders in it will be overwritten when proxy generation is run again. |
|||
Therefore, please do not place your own content in this folder. |
|||
|
|||
In addition, `generate-proxy.json` works like a lock file. |
|||
It includes information used by the proxy generator, so please do not delete or modify it. |
|||
|
|||
Finally, the name of the files and folders should not be changed for two reasons: |
|||
- Proxy generator will keep creating them at those paths and you will have multiple copies of the same content. |
|||
- ABP Suite generates files which include imports from this folder. |
|||
|
|||
> **Important Notice:** If you are building a module and are planning to publish to npm, |
|||
> some of the generated proxies are likely to be exported from public-api.ts file. In such a case, |
|||
> please make sure you export files directly and not from barrel exports. In other words, |
|||
> do not include index.ts exports in your public-api.ts exports. |
|||
File diff suppressed because it is too large
@ -0,0 +1,2 @@ |
|||
export * from './models'; |
|||
export * from './permissions.service'; |
|||
@ -0,0 +1,34 @@ |
|||
|
|||
export interface GetPermissionListResultDto { |
|||
entityDisplayName: string; |
|||
groups: PermissionGroupDto[]; |
|||
} |
|||
|
|||
export interface PermissionGrantInfoDto { |
|||
name: string; |
|||
displayName: string; |
|||
parentName: string; |
|||
isGranted: boolean; |
|||
allowedProviders: string[]; |
|||
grantedProviders: ProviderInfoDto[]; |
|||
} |
|||
|
|||
export interface PermissionGroupDto { |
|||
name: string; |
|||
displayName: string; |
|||
permissions: PermissionGrantInfoDto[]; |
|||
} |
|||
|
|||
export interface ProviderInfoDto { |
|||
providerName: string; |
|||
providerKey: string; |
|||
} |
|||
|
|||
export interface UpdatePermissionDto { |
|||
name: string; |
|||
isGranted: boolean; |
|||
} |
|||
|
|||
export interface UpdatePermissionsDto { |
|||
permissions: UpdatePermissionDto[]; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
import type { GetPermissionListResultDto, UpdatePermissionsDto } from './models'; |
|||
import { RestService } from '@abp/ng.core'; |
|||
import { Injectable } from '@angular/core'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class PermissionsService { |
|||
apiName = 'AbpPermissionManagement'; |
|||
|
|||
get = (providerName: string, providerKey: string) => |
|||
this.restService.request<any, GetPermissionListResultDto>({ |
|||
method: 'GET', |
|||
url: '/api/permission-management/permissions', |
|||
params: { providerName, providerKey }, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
update = (providerName: string, providerKey: string, input: UpdatePermissionsDto) => |
|||
this.restService.request<any, void>({ |
|||
method: 'PUT', |
|||
url: '/api/permission-management/permissions', |
|||
params: { providerName, providerKey }, |
|||
body: input, |
|||
}, |
|||
{ apiName: this.apiName }); |
|||
|
|||
constructor(private restService: RestService) {} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './permission-management-state.service'; |
|||
@ -0,0 +1,27 @@ |
|||
import { Injectable } from '@angular/core'; |
|||
import { Store } from '@ngxs/store'; |
|||
import { PermissionManagementState } from '../states/permission-management.state'; |
|||
import { PermissionManagement } from '../models'; |
|||
import { GetPermissions, UpdatePermissions } from '../actions'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class PermissionManagementStateService { |
|||
constructor(private store: Store) {} |
|||
|
|||
getPermissionGroups() { |
|||
return this.store.selectSnapshot(PermissionManagementState.getPermissionGroups); |
|||
} |
|||
getEntityDisplayName() { |
|||
return this.store.selectSnapshot(PermissionManagementState.getEntityDisplayName); |
|||
} |
|||
|
|||
dispatchGetPermissions(...args: ConstructorParameters<typeof GetPermissions>) { |
|||
return this.store.dispatch(new GetPermissions(...args)); |
|||
} |
|||
|
|||
dispatchUpdatePermissions(...args: ConstructorParameters<typeof UpdatePermissions>) { |
|||
return this.store.dispatch(new UpdatePermissions(...args)); |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './permission-management.state'; |
|||
@ -0,0 +1,47 @@ |
|||
import { Injectable } from '@angular/core'; |
|||
import { Action, Selector, State, StateContext } from '@ngxs/store'; |
|||
import { tap } from 'rxjs/operators'; |
|||
import { GetPermissions, UpdatePermissions } from '../actions/permission-management.actions'; |
|||
import { PermissionManagement } from '../models/permission-management'; |
|||
import { ProviderInfoDto } from '../proxy/models'; |
|||
import { PermissionsService } from '../proxy/permissions.service'; |
|||
|
|||
@State<PermissionManagement.State>({ |
|||
name: 'PermissionManagementState', |
|||
defaults: { permissionRes: {} } as PermissionManagement.State, |
|||
}) |
|||
@Injectable() |
|||
export class PermissionManagementState { |
|||
@Selector() |
|||
static getPermissionGroups({ permissionRes }: PermissionManagement.State) { |
|||
return permissionRes.groups || []; |
|||
} |
|||
|
|||
@Selector() |
|||
static getEntityDisplayName({ permissionRes }: PermissionManagement.State): string { |
|||
return permissionRes.entityDisplayName; |
|||
} |
|||
|
|||
constructor(private service: PermissionsService) {} |
|||
|
|||
@Action(GetPermissions) |
|||
permissionManagementGet( |
|||
{ patchState }: StateContext<PermissionManagement.State>, |
|||
{ payload: { providerKey, providerName } = {} as ProviderInfoDto }: GetPermissions, |
|||
) { |
|||
return this.service.get(providerName, providerKey).pipe( |
|||
tap(permissionResponse => |
|||
patchState({ |
|||
permissionRes: permissionResponse, |
|||
}), |
|||
), |
|||
); |
|||
} |
|||
|
|||
@Action(UpdatePermissions) |
|||
permissionManagementUpdate(_, { payload }: UpdatePermissions) { |
|||
return this.service.update(payload.providerName, payload.providerKey, { |
|||
permissions: payload.permissions, |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; |
|||
import { PermissionManagementStateService } from '../services/permission-management-state.service'; |
|||
import { PermissionManagementState } from '../states/permission-management.state'; |
|||
import { Store } from '@ngxs/store'; |
|||
import * as PermissionManagementActions from '../actions'; |
|||
|
|||
describe('PermissionManagementStateService', () => { |
|||
let service: PermissionManagementStateService; |
|||
let spectator: SpectatorService<PermissionManagementStateService>; |
|||
let store: SpyObject<Store>; |
|||
|
|||
const createService = createServiceFactory({ |
|||
service: PermissionManagementStateService, |
|||
mocks: [Store], |
|||
}); |
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
service = spectator.service; |
|||
store = spectator.inject(Store); |
|||
}); |
|||
test('should have the all PermissionManagementState static methods', () => { |
|||
const reg = /(?<=static )(.*)(?=\()/gm; |
|||
PermissionManagementState.toString() |
|||
.match(reg) |
|||
.forEach(fnName => { |
|||
expect(service[fnName]).toBeTruthy(); |
|||
|
|||
const spy = jest.spyOn(store, 'selectSnapshot'); |
|||
spy.mockClear(); |
|||
|
|||
const isDynamicSelector = PermissionManagementState[fnName].name !== 'memoized'; |
|||
|
|||
if (isDynamicSelector) { |
|||
PermissionManagementState[fnName] = jest.fn((...args) => args); |
|||
service[fnName]('test', 0, {}); |
|||
expect(PermissionManagementState[fnName]).toHaveBeenCalledWith('test', 0, {}); |
|||
} else { |
|||
service[fnName](); |
|||
expect(spy).toHaveBeenCalledWith(PermissionManagementState[fnName]); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
test('should have a dispatch method for every PermissionManagementState action', () => { |
|||
const reg = /(?<=dispatch)(\w+)(?=\()/gm; |
|||
PermissionManagementStateService.toString() |
|||
.match(reg) |
|||
.forEach(fnName => { |
|||
expect(PermissionManagementActions[fnName]).toBeTruthy(); |
|||
|
|||
const spy = jest.spyOn(store, 'dispatch'); |
|||
spy.mockClear(); |
|||
|
|||
const params = Array.from(new Array(PermissionManagementActions[fnName].length)); |
|||
|
|||
service[`dispatch${fnName}`](...params); |
|||
expect(spy).toHaveBeenCalledWith(new PermissionManagementActions[fnName](...params)); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,12 @@ |
|||
/* |
|||
* Public API Surface of permission-management |
|||
*/ |
|||
|
|||
export * from './lib/permission-management.module'; |
|||
export * from './lib/actions'; |
|||
export * from './lib/components'; |
|||
export * from './lib/enums/components'; |
|||
export * from './lib/models'; |
|||
export * from './lib/services'; |
|||
export * from './lib/states'; |
|||
export * from './lib/proxy'; |
|||
@ -0,0 +1 @@ |
|||
import 'jest-preset-angular/setup-jest'; |
|||
@ -0,0 +1,13 @@ |
|||
{ |
|||
"extends": "../../tsconfig.base.json", |
|||
"files": [], |
|||
"include": [], |
|||
"references": [ |
|||
{ |
|||
"path": "./tsconfig.lib.json" |
|||
}, |
|||
{ |
|||
"path": "./tsconfig.spec.json" |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
{ |
|||
"extends": "./tsconfig.json", |
|||
"compilerOptions": { |
|||
"outDir": "../../dist/out-tsc", |
|||
"target": "es2015", |
|||
"declaration": true, |
|||
"declarationMap": true, |
|||
"inlineSources": true, |
|||
"types": [], |
|||
"lib": ["dom", "es2018"] |
|||
}, |
|||
"exclude": ["src/test-setup.ts", "**/*.spec.ts"], |
|||
"include": ["**/*.ts"] |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
|||
{ |
|||
"extends": "./tsconfig.lib.json", |
|||
"compilerOptions": { |
|||
"declarationMap": false |
|||
}, |
|||
"angularCompilerOptions": { |
|||
"compilationMode": "partial", |
|||
"enableIvy": false |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"extends": "./tsconfig.json", |
|||
"compilerOptions": { |
|||
"outDir": "../../dist/out-tsc", |
|||
"module": "commonjs", |
|||
"types": ["jest", "node"] |
|||
}, |
|||
"files": ["src/test-setup.ts"], |
|||
"include": ["**/*.spec.ts", "**/*.d.ts"] |
|||
} |
|||
Loading…
Reference in new issue