diff --git a/src/lib/permissions/permissions.directive.spec.ts b/src/lib/permissions/permissions.directive.spec.ts
index 62b18e4..c538647 100644
--- a/src/lib/permissions/permissions.directive.spec.ts
+++ b/src/lib/permissions/permissions.directive.spec.ts
@@ -3,6 +3,7 @@ import { IqserPermissionsModule } from '.';
import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { IqserPermissionsService } from './services/permissions.service';
import { IqserRolesService } from './services/roles.service';
+import { BehaviorSubject } from 'rxjs';
class BaseTestComponent {
isAuthorized() {}
@@ -184,16 +185,7 @@ describe('Permission directive with roles array', () => {
it('should show the component when key of role is the same', fakeAsync(() => {
permissionsService.add(awesomePermission);
rolesService.add({ ADMIN: [awesomePermission] });
- tick();
-
- const content = getFixtureContent();
- expect(content).toBeTruthy();
- expect(content.innerHTML).toEqual('123');
- }));
-
- it('should show the component when there is permission', fakeAsync(() => {
- permissionsService.add(awesomePermission);
- rolesService.add({ ADMIN: ['AWESOME'] });
+ rolesService.add({ GUEST: [awesomePermission] });
tick();
const content = getFixtureContent();
@@ -204,6 +196,7 @@ describe('Permission directive with roles array', () => {
it('should hide the component when user deletes all roles', fakeAsync(() => {
permissionsService.add(awesomePermission);
rolesService.add({ ADMIN: [awesomePermission] });
+ rolesService.add({ GUEST: [awesomePermission] });
tick();
const content = getFixtureContent();
@@ -216,9 +209,10 @@ describe('Permission directive with roles array', () => {
expect(getFixtureContent()).toEqual(null);
}));
- it('should hide the component when user deletes one roles', fakeAsync(() => {
+ it('should hide the component when user deletes one role', fakeAsync(() => {
permissionsService.add(awesomePermission);
rolesService.add({ ADMIN: [awesomePermission] });
+ rolesService.add({ GUEST: [awesomePermission] });
tick();
const content = getFixtureContent();
@@ -329,6 +323,7 @@ describe('Permission directive angular testing different async functions in role
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.resolve(true) });
+ rolesService.add({ GUEST: () => Promise.resolve(true) });
tick();
const content = getFixtureContent();
@@ -340,6 +335,7 @@ describe('Permission directive angular testing different async functions in role
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.resolve(false) });
+ rolesService.add({ GUEST: () => Promise.resolve(true) });
tick();
expect(getFixtureContent()).toEqual(null);
@@ -348,32 +344,29 @@ describe('Permission directive angular testing different async functions in role
it('should not show the component when promise rejects', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
- rolesService.add({ ADMIN: () => Promise.reject() });
+ rolesService.add({ ADMIN: () => Promise.resolve(true) });
+ rolesService.add({ GUEST: () => Promise.reject() });
tick();
expect(getFixtureContent()).toEqual(null);
}));
- it('should show the component when one of the promises fulfills', fakeAsync(() => {
+ it('should hide the component when one of the promises fulfills', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject(), GUEST: () => Promise.resolve(true) });
tick();
- const content = getFixtureContent();
- expect(content).toBeTruthy();
- expect(content.innerHTML).toEqual('
123
');
+ expect(getFixtureContent()).toEqual(null);
}));
- it('should show the component when one of the promises fulfills with true value', fakeAsync(() => {
+ it('should hide the component when one of the promises fulfills with true value', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject(), GUEST: () => Promise.resolve(true) });
tick();
- const content = getFixtureContent();
- expect(content).toBeTruthy();
- expect(content.innerHTML).toEqual('123
');
+ expect(getFixtureContent()).toEqual(null);
}));
it('should not show the component when all promises fails', fakeAsync(() => {
@@ -385,38 +378,23 @@ describe('Permission directive angular testing different async functions in role
expect(getFixtureContent()).toEqual(null);
}));
- it('should show the component when one of promises returns true', fakeAsync(() => {
+ it('should hide the component when one of promises returns true', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ GUEST: () => true, ADMIN: () => Promise.reject() });
tick();
- const content = getFixtureContent();
- expect(content).toBeTruthy();
- expect(content.innerHTML).toEqual('123
');
+ expect(getFixtureContent()).toEqual(null);
}));
- it('should show the component when 1 passes second fails', fakeAsync(() => {
+ it('should hide the component when 1 passes second fails', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject() });
- rolesService.add({ ADMIN: ['AWESOME'] });
+ rolesService.add({ GUEST: ['AWESOME'] });
tick();
- const content = getFixtureContent();
- expect(content).toBeTruthy();
- expect(content.innerHTML).toEqual('123
');
- }));
-
- it('should show the component when one rejects but another one fulfils', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
-
- rolesService.add({ ADMIN: () => Promise.reject(), GUEST: ['AWESOME'] });
- tick();
-
- const content = getFixtureContent();
- expect(content).toBeTruthy();
- expect(content.innerHTML).toEqual('123
');
}));
});
@@ -806,3 +784,154 @@ describe('Permissions directive with an empty array', () => {
expect(content.innerHTML).toEqual(`123`);
});
});
+
+describe('Permission directive with true if condition', () => {
+ @Component({
+ template: `
+ 123
+ `,
+ })
+ class TestComponent extends BaseTestComponent {}
+
+ beforeEach(() => configureTestBed(TestComponent));
+
+ it('should show the component when permission is present', fakeAsync(() => {
+ permissionsService.add(ADMIN);
+ tick();
+
+ const content = getFixtureContent();
+ expect(content).toBeTruthy();
+ expect(content.innerHTML).toEqual('123');
+ }));
+
+ it('should hide the component when permission is absent', fakeAsync(() => {
+ expect(getFixtureContent()).toEqual(null);
+ }));
+});
+
+describe('Permission directive with true if condition', () => {
+ @Component({
+ template: `
+ 123
+ `,
+ })
+ class TestComponent extends BaseTestComponent {}
+
+ beforeEach(() => configureTestBed(TestComponent));
+
+ it('should hide the component when permission is present', fakeAsync(() => {
+ permissionsService.add(ADMIN);
+ tick();
+
+ expect(getFixtureContent()).toEqual(null);
+ }));
+
+ it('should hide the component when permission is absent', fakeAsync(() => {
+ expect(getFixtureContent()).toEqual(null);
+ }));
+});
+
+describe('Permission directive with true promise if condition', () => {
+ @Component({
+ template: `
+ 123
+ `,
+ })
+ class TestComponent extends BaseTestComponent {
+ condition = Promise.resolve(true);
+ }
+
+ beforeEach(() => configureTestBed(TestComponent));
+
+ it('should show the component when permission is present', fakeAsync(() => {
+ permissionsService.add(ADMIN);
+ tick();
+
+ const content = getFixtureContent();
+ expect(content).toBeTruthy();
+ expect(content.innerHTML).toEqual('123');
+ }));
+
+ it('should hide the component when permission is absent', fakeAsync(() => {
+ expect(getFixtureContent()).toEqual(null);
+ }));
+});
+
+describe('Permission directive with false promise if condition', () => {
+ @Component({
+ template: ` `,
+ })
+ class TestComponent extends BaseTestComponent {
+ condition = Promise.resolve(false);
+ }
+
+ beforeEach(() => configureTestBed(TestComponent));
+
+ it('should hide the component when permission is present', fakeAsync(() => {
+ permissionsService.add(ADMIN);
+ tick();
+
+ expect(getFixtureContent()).toEqual(null);
+ }));
+
+ it('should hide the component when permission is absent', fakeAsync(() => {
+ expect(getFixtureContent()).toEqual(null);
+ }));
+});
+
+describe('Permissions directive with then block and if condition', () => {
+ @Component({
+ template: `
+ main
+
+
+ elseBlockContent
+
+
+
+ thenBlockContent
+
+ `,
+ })
+ class TestComponent extends BaseTestComponent {
+ condition = new BehaviorSubject(true);
+ }
+
+ beforeEach(() => configureTestBed(TestComponent));
+
+ it('should show else block when permission missing', fakeAsync(() => {
+ const content = getFixtureContent();
+ expect(content).toBeTruthy();
+ expect(content.innerHTML).toEqual(`elseBlockContent`);
+ }));
+
+ it('should show then block when permission added', fakeAsync(() => {
+ permissionsService.add('THEN_BLOCK');
+ tick();
+
+ const content = getFixtureContent();
+ expect(content).toBeTruthy();
+ expect(content.innerHTML).toEqual(`thenBlockContent`);
+ }));
+
+ it('should show else block when permission added but condition is false', fakeAsync(() => {
+ permissionsService.add('THEN_BLOCK');
+ (fixture.componentInstance as TestComponent).condition.next(false);
+ tick();
+
+ const content = getFixtureContent();
+ expect(content).toBeTruthy();
+ expect(content.innerHTML).toEqual(`elseBlockContent`);
+ }));
+
+ it('should show else block when permission is missing and condition is false', fakeAsync(() => {
+ (fixture.componentInstance as TestComponent).condition.next(false);
+ tick();
+
+ const content = getFixtureContent();
+ expect(content).toBeTruthy();
+ expect(content.innerHTML).toEqual(`elseBlockContent`);
+ }));
+});
diff --git a/src/lib/permissions/permissions.directive.ts b/src/lib/permissions/permissions.directive.ts
index f67aba3..e1273a3 100644
--- a/src/lib/permissions/permissions.directive.ts
+++ b/src/lib/permissions/permissions.directive.ts
@@ -12,12 +12,13 @@ import {
ɵstringify as stringify,
} from '@angular/core';
-import { merge, Subject, Subscription, switchMap } from 'rxjs';
-import { tap } from 'rxjs/operators';
+import { BehaviorSubject, combineLatest, merge, Observable, of, Subject, Subscription, switchMap } from 'rxjs';
+import { map, tap } from 'rxjs/operators';
import { IqserRolesService } from './services/roles.service';
import { IqserPermissionsService } from './services/permissions.service';
import { List } from '../utils';
+import { isBoolean } from './utils';
@Directive({
selector: '[allow]',
@@ -45,6 +46,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
readonly #updateView = new Subject();
readonly #subscription = new Subscription();
+ readonly #if = new BehaviorSubject | Observable>(of(true));
constructor(
private readonly _permissionsService: IqserPermissionsService,
@@ -54,7 +56,17 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
templateRef: TemplateRef,
) {
this.#thenTemplateRef = templateRef;
- this.#subscription = this.#updateView.pipe(switchMap(() => this.#waitForRolesAndPermissions())).subscribe();
+
+ const ifCondition$ = this.#if.pipe(
+ switchMap(condition => condition),
+ tap(console.log),
+ );
+ this.#subscription = combineLatest([ifCondition$, this.#updateView])
+ .pipe(
+ switchMap(([ifCondition]) => this.#waitForRolesAndPermissions().pipe(map(hasPermission => ifCondition && hasPermission))),
+ tap(isAllowed => (isAllowed ? this.#showThenBlock() : this.#showElseBlock())),
+ )
+ .subscribe();
}
@Input()
@@ -79,6 +91,15 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
this.#updateView.next();
}
+ @Input()
+ set allowIf(value: boolean | Promise | Observable) {
+ if (isBoolean(value)) {
+ this.#if.next(of(value));
+ } else {
+ this.#if.next(value);
+ }
+ }
+
/**
This assures that when the directive has an empty input (such as [allow]="") the view is updated
*/
@@ -91,17 +112,18 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
}
#waitForRolesAndPermissions() {
- return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(tap(() => this.#validate()));
+ return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(switchMap(() => this.#validate()));
}
- #validate(): void {
+ #validate() {
if (!this.#permissions) {
- return this.#showThenBlock();
+ return Promise.resolve(true);
}
const promises = [this._permissionsService.has(this.#permissions), this._rolesService.has(this.#permissions)];
- const result = Promise.all(promises).then(([hasPermission, hasRole]) => hasPermission || hasRole);
- result.then(isAllowed => (isAllowed ? this.#showThenBlock() : this.#showElseBlock())).catch(() => this.#showElseBlock());
+ return Promise.all(promises)
+ .then(([hasPermission, hasRole]) => hasPermission || hasRole)
+ .catch(() => false);
}
#showElseBlock() {