Skip to content

Commit

Permalink
Refactor allowed actions using service
Browse files Browse the repository at this point in the history
  • Loading branch information
minottic committed Jan 30, 2024
1 parent 32a54c3 commit 3f647a1
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 109 deletions.
12 changes: 6 additions & 6 deletions scilog/src/app/overview/add-logbook/add-logbook.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
<div>
<mat-form-field appearance="standard" required>
<mat-label>Title</mat-label>
<input matInput placeholder="Title" [formControl]="optionsFormGroup.get('title')" matTooltip="{{ tooltips.expired }}">
<input matInput placeholder="Title" [formControl]="optionsFormGroup.get('title')" matTooltip="{{ isActionAllowed.tooltips.expired }}">
<mat-error *ngIf="optionsFormGroup.get('title').hasError('required')">Please specify the title.</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field appearance="standard">
<mat-label>Location (Beamline or Instrument)</mat-label>
<mat-select [formControl]="optionsFormGroup.get('location')" (selectionChange)="selectLocation($event)" matTooltip="{{ tooltips.expired }}">
<mat-select [formControl]="optionsFormGroup.get('location')" (selectionChange)="selectLocation($event)" matTooltip="{{ isActionAllowed.tooltips.expired }}">
<ng-container *ngFor="let location of availLocations">
<mat-option [(value)]="location.id">{{ location?.location }}</mat-option>
</ng-container>
Expand All @@ -36,7 +36,7 @@
<mat-form-field appearance="standard" required>
<mat-label>ownerGroup</mat-label>
<input matInput placeholder="ownerGroup" [matAutocomplete]="autoOwnerGroup"
[formControl]="optionsFormGroup.get('ownerGroup')" matTooltip="{{ tooltips.ownerGroup }}">
[formControl]="optionsFormGroup.get('ownerGroup')" matTooltip="{{ isActionAllowed.tooltips.ownerGroup }}">
<mat-error *ngIf="optionsFormGroup.get('ownerGroup').hasError('required')">Please specify the owner
group.
</mat-error>
Expand Down Expand Up @@ -76,12 +76,12 @@
</mat-form-field>
</div>
<div>
<mat-slide-toggle [formControl]="optionsFormGroup.get('isPrivate')" matTooltip="{{ tooltips.expired }}">private</mat-slide-toggle>
<mat-slide-toggle [formControl]="optionsFormGroup.get('isPrivate')" matTooltip="{{ isActionAllowed.tooltips.expired }}">private</mat-slide-toggle>
</div>
<div>
<mat-form-field appearance="standard" required>
<mat-label>Description</mat-label>
<textarea matInput placeholder="Description" [formControl]="optionsFormGroup.get('description')" matTooltip="{{ tooltips.expired }}"></textarea>
<textarea matInput placeholder="Description" [formControl]="optionsFormGroup.get('description')" matTooltip="{{ isActionAllowed.tooltips.expired }}"></textarea>
</mat-form-field>
</div>
</form>
Expand All @@ -90,7 +90,7 @@
<img mat-card-image [src]="imageToShow" alt="" *ngIf="imageLoaded" />
<div>
<input style="display: none" type="file" (change)="onFileChanged($event)" #fileInput>
<button color="accent" aria-label="Thumbnail" class="mat-fab-bottom-right" (click)="fileInput.click()" disabled="{{ tooltips.expired }}" matTooltip="{{ tooltips.expired }}">
<button color="accent" aria-label="Thumbnail" class="mat-fab-bottom-right" (click)="fileInput.click()" disabled="{{ isActionAllowed.tooltips.expired }}" matTooltip="{{ isActionAllowed.tooltips.expired }}">
{{ thumbnailText }}
</button>
<button color="accent" aria-label="RemoveThumbnail" class="mat-fab-bottom-right" (click)="removeThumbnail()"
Expand Down
65 changes: 25 additions & 40 deletions scilog/src/app/overview/add-logbook/add-logbook.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { LogbookDataService, LogbookItemDataService } from '@shared/remote-data.
import { of } from 'rxjs';
import { MatChipInputEvent } from '@angular/material/chips';
import { SnackbarService } from 'src/app/core/snackbar.service';
import { IsAllowedService } from '../is-allowed.service';

class UserPreferencesMock {
userInfo = {
Expand All @@ -27,6 +28,7 @@ describe('AddLogbookComponent', () => {
let logbookItemDataServiceSpy:any;
let logbookDataSpy:any;
let snackBarSpy: SnackbarService;
let isActionAllowedService: IsAllowedService;

logbookItemDataServiceSpy = jasmine.createSpyObj("LogbookItemDataService", ["getFile"]);
logbookItemDataServiceSpy.getFile.and.returnValue([]);
Expand All @@ -46,7 +48,8 @@ describe('AddLogbookComponent', () => {
{provide: LogbookItemDataService, useValue: logbookItemDataServiceSpy},
{provide: UserPreferencesService, useClass: UserPreferencesMock},
{provide: LogbookDataService, useValue: logbookDataSpy},
{provide: SnackbarService, useValue: snackBarSpy}
{provide: SnackbarService, useValue: snackBarSpy},
{provide: IsAllowedService, useClass: isActionAllowedService}
],
})
.compileComponents();
Expand Down Expand Up @@ -112,49 +115,28 @@ describe('AddLogbookComponent', () => {
});

[
{input: ['roles'], output: true},
{input: [], output: false}
].forEach((t, i) => {
it(`should test isAdmin ${i}`, () => {
component['logbook'] = {adminACL: t.input};
expect(component['isAdmin']()).toEqual(t.output);
});
});

[
{input: '2200-12-12T00:00:00Z', output: undefined},
{input: '2023-12-12T00:00:00Z', output: jasmine.anything()}
].forEach((t, i) => {
it(`should test hasExpired ${i}`, () => {
component['logbook'] = {expiresAt: t.input};
component['setExpiredTooltip']();
expect(component.tooltips.expired).toEqual(t.output);
});
});

[
{input: ['roles'], output: [undefined, false]},
{input: ['admin'], output: [`Only members of 'admin' can change the ownerGroup`, true]}
true,
false
].forEach((t, i) => {
it(`should test setOwnerGroupWithEditability ${i}`, () => {
component['logbook'] = {adminACL: t.input, ownerGroup: 'ownerGroup'};
component['logbook'] = {ownerGroup: 'ownerGroup'};
spyOn(component['isActionAllowed'], 'canChangeOwnerGroup').and.returnValue(t)
component['setOwnerGroupWithEditability']();
expect(component.tooltips.ownerGroup).toEqual(t.output[0] as string);
expect(component['getForm']('ownerGroup').disabled).toEqual(t.output[1] as boolean);
expect(component['getForm']('ownerGroup').disabled).toEqual(!t);
});
});

[
{input: undefined, output: false},
{input: 'Expired', output: true}
true,
false
].forEach((t, i) => {
it(`should test setWithEditability ${i}`, () => {
component.tooltips.expired = t.input;
component['logbook'] = {name: 'a', location: 'b', expiresAt: t.input};
component['logbook'] = {name: 'a', location: 'b'};
component['isActionAllowed'].tooltips.expired = t? 'Expired': '';
component['setWithEditability']('title');
component['setWithEditability']('location');
expect(component['getForm']('title').disabled).toEqual(t.output);
expect(component['getForm']('location').disabled).toEqual(t.output);
expect(component['getForm']('title').disabled).toEqual(t);
expect(component['getForm']('location').disabled).toEqual(t);
});
});

Expand Down Expand Up @@ -183,14 +165,13 @@ describe('AddLogbookComponent', () => {
});

[
{input: {adminACL: ['roles']}, output: 0},
{input: {adminACL: []}, output: 1},
{input: {expiresAt: '2023-12-12T00:00:00Z'}, output: true},
{input: {expiresAt: '2200-12-12T00:00:00Z'}, output: false},
{input: {isAdmin: true}, output: 0},
{input: {isAdmin: false}, output: 1},
{input: {expired: 'expired'}, output: true},
{input: {expired: undefined}, output: false},
].forEach((t, i) => {
it(`should test setDefaults on update ${i}`, () => {
component.logbook = {
...t.input,
ownerGroup: 'ownerGroup',
accessGroups: ['accessGroups'],
name: 'name',
Expand All @@ -199,16 +180,20 @@ describe('AddLogbookComponent', () => {
isPrivate: true,
}
const ownerGroupValidatorsSpy = spyOn(component['getForm']('ownerGroup'), 'addValidators');
spyOn(component['isActionAllowed'], 'isAdmin').and.returnValue(t.input.isAdmin);
spyOn(component['isActionAllowed'], 'isNotExpired');
spyOn(component['isActionAllowed'], 'canChangeOwnerGroup');
component['isActionAllowed'].tooltips.expired = t.input.expired;
component['setDefaults']();
expect(component['getForm']('ownerGroup').value).toEqual('ownerGroup');
expect(component['getForm']('title').value).toEqual('name');
expect(component['getForm']('description').value).toEqual('description');
expect(component['getForm']('location').value).toEqual('location');
expect(component['getForm']('isPrivate').value).toEqual(true);
expect(component.accessGroups.value).toEqual(['accessGroups']);
if (t.input.adminACL)
if ('isAdmin' in t.input)
expect(ownerGroupValidatorsSpy).toHaveBeenCalledTimes(t.output as number);
if (t.input.expiresAt) {
if ('expired' in t.input) {
expect(component['getForm']('description').disabled).toEqual(t.output as boolean);
expect(component['getForm']('title').disabled).toEqual(t.output as boolean);
expect(component['getForm']('location').disabled).toEqual(t.output as boolean);
Expand Down
29 changes: 7 additions & 22 deletions scilog/src/app/overview/add-logbook/add-logbook.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { UserPreferencesService } from '@shared/user-preferences.service';
import { Logbooks } from '@model/logbooks';
import { LogbookDataService, LogbookItemDataService } from '@shared/remote-data.service';
import { SnackbarService } from 'src/app/core/snackbar.service';
import { IsAllowedService } from '../is-allowed.service';

function ownerGroupMemberValidator(groups: string[]): ValidatorFn {
return (control: AbstractControl): { forbiddenGroup: {value: string} } | null => {
Expand Down Expand Up @@ -105,8 +106,6 @@ export class AddLogbookComponent implements OnInit {
filteredAccessGroups: Observable<string[]>;
filteredOwnerGroups: Observable<string[]>;
accessGroupsAvail: string[] = [];
tooltips: {ownerGroup?: string, expired?: string} = {};


@ViewChild('accessGroupsInput') accessGroupsInput: ElementRef<HTMLInputElement>;
@ViewChild('auto') matAutocomplete: MatAutocomplete;
Expand All @@ -119,6 +118,7 @@ export class AddLogbookComponent implements OnInit {
private userPreferences: UserPreferencesService,
private logbookDataService: LogbookDataService,
private snackBar: SnackbarService,
protected isActionAllowed: IsAllowedService,
@Inject(MAT_DIALOG_DATA) data) {
this.optionsFormGroup = fb.group({
hideRequired: new UntypedFormControl(false),
Expand All @@ -131,8 +131,8 @@ export class AddLogbookComponent implements OnInit {
isPrivate: new UntypedFormControl(false)
});
this.logbook = data;
this.isActionAllowed.snippet = data;
console.log("inputData: ", data);

}

ngOnInit(): void {
Expand All @@ -154,39 +154,24 @@ export class AddLogbookComponent implements OnInit {

private setOwnerGroupWithEditability() {
this.setForm('ownerGroup');
if (!this.logbook.ownerGroup || this.isAdmin())
if (this.isActionAllowed.canChangeOwnerGroup())
return
this.getForm('ownerGroup').disable();
this.tooltips.ownerGroup = `Only members of '${this.logbook.adminACL}' can change the ownerGroup`;
}

private isAdmin() {
return this.accessGroupsAvail.some(group => this.logbook?.adminACL?.includes(group));
}

private setExpiredTooltip() {
const expiresAt = new Date(this.logbook.expiresAt);
if (!expiresAt || expiresAt > new Date()) return;
const expiresString = expiresAt.toLocaleDateString(
'en-GB',
{year: '2-digit', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit'}
);
this.tooltips.expired = `Editing time (${expiresString}) has passed`;
}

private setWithEditability(toKey: string) {
let fromKey: string;
if (toKey === 'title') fromKey = 'name';
this.setForm(toKey, fromKey ?? toKey);
if (this.tooltips.expired) this.getForm(toKey).disable();
if (this.isActionAllowed.tooltips.expired) this.getForm(toKey).disable();
}

setDefaults(){
this.accessGroupsAvail = this.userPreferences.userInfo?.roles;
if (!this.isAdmin())
if (!this.isActionAllowed.isAdmin())
this.getForm('ownerGroup').addValidators(ownerGroupMemberValidator(this.accessGroupsAvail));
if (this.logbook) {
this.setExpiredTooltip();
this.isActionAllowed.isNotExpired();
['title', 'description', 'location', 'isPrivate'].map(field => this.setWithEditability(field));
this.setOwnerGroupWithEditability();
this.accessGroups.setValue(this.logbook.accessGroups);
Expand Down
81 changes: 81 additions & 0 deletions scilog/src/app/overview/is-allowed.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { TestBed } from "@angular/core/testing";
import { IsAllowedService } from "./is-allowed.service";
import { UserPreferencesService } from "../core/user-preferences.service";

describe('IsAllowedService', () => {
let service: IsAllowedService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
IsAllowedService,
{ provide: UserPreferencesService, useValue: {userInfo: {roles: ['roles']}} },
],
});
service = TestBed.inject(IsAllowedService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});


[
{input: '2200-12-12T00:00:00Z', output: undefined},
{input: '2023-12-12T00:00:00Z', output: jasmine.anything()}
].forEach((t, i) => {
it(`should test isNotExpired ${i}`, () => {
service.snippet = {expiresAt: t.input};
expect(service.isNotExpired()).toEqual(!t.output? true: false);
expect(service.tooltips.expired).toEqual(t.output);
});
});

[
{input: ['roles'], output: []},
{input: ['someOtherRole'], output: ['someOtherRole', 'admin']}
].forEach((t, i) => {
it(`should test otherEnabledMembers ${i}`, () => {
service.snippet = {adminACL: t.input};
expect(service['otherEnabledMembers']('admin')).toEqual(t.output);
});
});

[
{input: ['roles'], output: undefined},
{input: ['someOtherRole'], output: "Enabled for members of 'someOtherRole,admin'"}
].forEach((t, i) => {
it(`should test isUserAllowed ${i}`, () => {
service.snippet = {updateACL: t.input};
expect(service.isUserAllowed('update')).toEqual(!t.output ? true: false);
expect(service.tooltips.update).toEqual(t.output);
});
});

[
{input: [[], []], output: undefined},
{input: [['someUpdate'], ['someDelete', 'admin']], output: "Enabled for members of 'someUpdate,someDelete,admin'"}
].forEach((t, i) => {
it(`should test isAnyEditAllowed ${i}`, () => {
spyOn<any>(service, 'otherEnabledMembers').and.returnValues(...t.input);
expect(service.isAnyEditAllowed()).toEqual(!t.output ? true: false);
expect(service.tooltips.edit).toEqual(t.output);
});
});

[
{input: {expired: 'Expired', check: true}, output: 'Expired'},
{input: {expired: 'Expired', check: false}, output: 'Update'},
{input: {expired: '', check: true}, output: 'Update'},
{input: {expired: '', check: false}, output: 'Update'},
].forEach((t, i) => {
it(`should test cascadeExpiration ${i}`, () => {
spyOn(service, 'isUserAllowed');
service.tooltips.expired = t.input.expired;
service.tooltips.update = 'Update';
service['cascadeExpiration']('update', t.input.check);
expect(service.tooltips.update).toEqual(t.output);
});
});

});
Loading

0 comments on commit 3f647a1

Please sign in to comment.