Skip to content

Commit

Permalink
Refactor allowed actions using service
Browse files Browse the repository at this point in the history
* Refactor allowed actions using service

* Check editability for snippets

* Avoid adding invalid date to tooltip
  • Loading branch information
minottic authored Jan 31, 2024
1 parent 32a54c3 commit dd9a0ed
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 170 deletions.
6 changes: 3 additions & 3 deletions scilog/src/app/logbook/core/snippet/snippet.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
<mat-icon>info</mat-icon>
<span>Info</span>
</button>
<button mat-menu-item (click)="editSnippet()" [disabled]="!enableEdit? 'disabled' : null" *ngIf="showEditButtons">
<button mat-menu-item (click)="editSnippet()" [disabled]="!enableEdit.update" *ngIf="showEditButtons" matTooltip="{{ isActionAllowed.tooltips.update }}">
<mat-icon>edit</mat-icon>
<span>Edit</span>
</button>
<button mat-menu-item (click)="setDashboardName()" [disabled]="!enableEdit? 'disabled' : null">
<button mat-menu-item (click)="setDashboardName()" [disabled]="!enableEdit.update" matTooltip="{{ isActionAllowed.tooltips.update }}">
<mat-icon>assistant</mat-icon>
<span>Dashboard name</span>
</button>
Expand All @@ -33,7 +33,7 @@
<span>Reply</span>
</button>
<!-- <button mat-menu-item disabled> -->
<button mat-menu-item (click)="deleteSnippet()" [disabled]="!enableEdit? 'disabled' : null"
<button mat-menu-item (click)="deleteSnippet()" [disabled]="!enableEdit.delete" matTooltip="{{ isActionAllowed.tooltips.delete }}"
*ngIf="showEditButtons">
<mat-icon>delete</mat-icon>
<span>Delete</span>
Expand Down
45 changes: 30 additions & 15 deletions scilog/src/app/logbook/core/snippet/snippet.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { UserPreferencesService } from '@shared/user-preferences.service';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { WidgetItemConfig } from '@model/config';


describe('SnippetComponent', () => {
let component: SnippetComponent;
let fixture: ComponentFixture<SnippetComponent>;
Expand Down Expand Up @@ -215,35 +214,37 @@ describe('SnippetComponent', () => {
}));

it('should release the lock', async () => {
spyOn(component, 'allowEdit').and.returnValue(true);
component._enableEdit = false;
component._enableEdit = {update: false, delete: false};
component.snippetIsAccessedByAnotherUser = true;
spyOn(component['isActionAllowed'], 'canUpdate').and.returnValue(true);
spyOn(component['isActionAllowed'], 'canDelete').and.returnValue(true);
spyOn(component['isActionAllowed'], 'isNotExpired').and.returnValue(true);
await component.releaseLock();
expect(component.enableEdit).toEqual(true);
expect(component.enableEdit).toEqual({update: true, delete: true});
expect(component.snippetIsAccessedByAnotherUser).toEqual(false);
expect(component['logbookItemDataService'].deleteAllInProgressEditing)
.toHaveBeenCalledOnceWith(snippetMock.id);
})
});

it('should add the lock', () => {
spyOn(component, 'releaseLock');
spyOn(window, 'clearTimeout');
component.snippetIsAccessedByAnotherUser = false;
component.timerId = 123;
component._enableEdit = true;
component._enableEdit = {update: true, delete: true};
component.lockEdit();
expect(component.enableEdit).toEqual(false);
expect(component.enableEdit).toEqual({update: false, delete: false});
expect(component.snippetIsAccessedByAnotherUser).toEqual(true);
expect(window.clearTimeout).toHaveBeenCalledOnceWith(123);
})
});

it('should set the editing timeout', () => {
spyOn(window, "setTimeout");
component.timerId = null;
component.setEditTimeout(123);
expect(component.timerId).not.toEqual(null);
expect(window.setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function), 123);
})
});

it('should lock the edit timeout and set by', () => {
const avatarForUser = "aTestUserForlockEditUntilTimeout";
Expand All @@ -253,49 +254,63 @@ describe('SnippetComponent', () => {
expect(component.avatarHash).toEqual(avatarForUser);
expect(component.lockEdit).toHaveBeenCalledTimes(1);
expect(component.setEditTimeout).toHaveBeenCalledTimes(1);
})
});

it('should set locked', () => {
spyOn(component, "getLastEditedSnippet").and.returnValue({createdAt: "2023-01-01", updatedBy: "aUser"});
spyOn(Date.prototype, "getTime").and.returnValue(1672531200001);
spyOn(component, "lockEditUntilTimeout");
component.setLocked();
expect(component.lockEditUntilTimeout).toHaveBeenCalledOnceWith("aUser", component._timeoutMilliseconds - 1);
})
});

it('should release old lock', () => {
spyOn(component, "getLastEditedSnippet").and.returnValue({createdAt: "2023-01-01", updatedBy: "aUser"});
spyOn(Date.prototype, "getTime").and.returnValue(16725312000000);
spyOn(component, "releaseLock");
component.setLocked();
expect(component.releaseLock).toHaveBeenCalledTimes(1);
})
});

it('should get undefined from last edited snippet', () => {
const subs = [{snippetType: 'paragraph'}, {snippetType: 'paragraph'}];
expect(component.getLastEditedSnippet(subs)).toEqual(undefined);
})
});

it('should get first toDelete from last edited snippet', () => {
const subs = [
{ snippetType: "edit", toDelete: true, id: "1" },
{ snippetType: "edit", toDelete: true, id: "2" }
];
expect(component.getLastEditedSnippet(subs)).toEqual(subs[0]);
})
});

it('should get last snippet from last edited snippet', () => {
const subs = [
{snippetType: "edit", createdAt: "2023-01-02"},
{snippetType: "edit", createdAt: "2023-01-01"},
];
expect(component.getLastEditedSnippet(subs)).toEqual(subs[0]);
})
});

it('should call setLocked after subsnippets change', () => {
spyOn(component, "setLocked");
component.subsnippets = [{parentId: "123"}];
expect(component.setLocked).toHaveBeenCalledTimes(1);
});

[
{input: {v: true, canUpdate: true}, output: true},
{input: {v: true, canUpdate: false}, output: false},
{input: {v: false, canUpdate: false}, output: false},
{input: {v: false, canUpdate: true}, output: false},
].forEach((t, i) => {
it(`should test enableEdit ${i}`, () => {
spyOn(component['isActionAllowed'], "canUpdate").and.returnValue(t.input.canUpdate);
spyOn(component['isActionAllowed'], "canDelete").and.returnValue(true);
component.enableEdit = t.input.v;
expect(component._enableEdit.update).toEqual(t.output);
})
})

});
44 changes: 10 additions & 34 deletions scilog/src/app/logbook/core/snippet/snippet.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { trigger, state, style, transition, animate } from '@angular/animations'
import { WidgetItemConfig } from '@model/config';
import { Edits } from 'src/app/core/model/edits';
import { Basesnippets } from 'src/app/core/model/basesnippets';
import { IsAllowedService } from 'src/app/overview/is-allowed.service';


@Component({
Expand All @@ -27,7 +28,8 @@ import { Basesnippets } from 'src/app/core/model/basesnippets';
transition('highlight => default', animate('2000ms ease-out')),
transition('default => highlight', animate('200ms ease-in'))
])
]
],
providers: [IsAllowedService],
})
export class SnippetComponent implements OnInit {

Expand Down Expand Up @@ -74,7 +76,7 @@ export class SnippetComponent implements OnInit {
showImage: boolean;

timerId = null;
_enableEdit: boolean;
_enableEdit = {update: false, delete: false};
snippetIsAccessedByAnotherUser = false;
avatarHash = "anotherUser";

Expand All @@ -99,7 +101,8 @@ export class SnippetComponent implements OnInit {
private sanitizer: DomSanitizer,
public dialog: MatDialog,
private logbookItemDataService: LogbookItemDataService,
private userPreferences: UserPreferencesService) { }
private userPreferences: UserPreferencesService,
private isActionAllowed: IsAllowedService) { }

ngOnInit(): void {
if (this.snippet.isMessage) {
Expand Down Expand Up @@ -144,6 +147,7 @@ export class SnippetComponent implements OnInit {
this._hideMetadata = true;
}
// enable edit for snippet
this.isActionAllowed.snippet = this.snippet;
this.enableEdit = true;
this._subsnippets = new BehaviorSubject(this.snippet.subsnippets);
}
Expand All @@ -163,41 +167,13 @@ export class SnippetComponent implements OnInit {
});
}


public get enableEdit(): boolean {
public get enableEdit(): any {
return this._enableEdit;
}


public set enableEdit(v: boolean) {
if (v) {
// check if user a member of the ownerGroup before enabling access
if (this.allowEdit()) {
this._enableEdit = v;
}
} else {
this._enableEdit = v;
}

}

allowEdit() {
let _hasAccessPermission = false;
if (typeof this.userPreferences.userInfo.roles != 'undefined') {
_hasAccessPermission = this.userPreferences.userInfo.roles.some(entry => {
return this.snippet.readACL?.includes?.(entry)
});
}

let _isExpired = false;
if (typeof this.snippet?.expiresAt == 'undefined') {
_isExpired = true;
} else {
let _expirationTime = Date.parse(this.snippet.expiresAt);
_isExpired = _expirationTime < Date.now()
}

return (_hasAccessPermission && !_isExpired)
this._enableEdit.update = this.isActionAllowed.canUpdate() && v;
this._enableEdit.delete = this.isActionAllowed.canDelete() && v;
}

ngAfterViewChecked(): void {
Expand Down
2 changes: 1 addition & 1 deletion scilog/src/app/logbook/widgets/todos/todos.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div *ngFor="let task of tasks; let i=index" >
<mat-card class="task-mat-card">
<mat-card-content class="task-content">
<mat-checkbox (change)="toggleTaskIsDone(i)" [checked]="task.isDone" >
<mat-checkbox (change)="toggleTaskIsDone(i)" [checked]="task.isDone">
<div >

</div>
Expand Down
7 changes: 5 additions & 2 deletions scilog/src/app/logbook/widgets/todos/todos.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ const getConfig = () => ({});
describe('TodosComponent', () => {
let component: TodosComponent;
let fixture: ComponentFixture<TodosComponent>;


let isActionAllowedServiceSpy
beforeEach(waitForAsync(() => {
isActionAllowedServiceSpy = jasmine.createSpyObj("IsAllowedService", ["canUpdate", "canDelete"]);
isActionAllowedServiceSpy.tooltips = {update: '', delete: ''};
TestBed.configureTestingModule({
providers:[
{provide: LogbookInfoService, useClass: LogbookInfoMock},
Expand All @@ -65,4 +67,5 @@ describe('TodosComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

});
4 changes: 1 addition & 3 deletions scilog/src/app/logbook/widgets/todos/todos.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { TasksService } from '@shared/tasks.service';
import { LogbookInfoService } from '@shared/logbook-info.service';
import { ChangeStreamService } from '@shared/change-stream.service';
import { Subscription } from 'rxjs';
import { Basesnippets } from '@model/basesnippets';
import { Logbooks } from '@model/logbooks';
import { ViewsService } from '@shared/views.service';

@Component({
Expand Down Expand Up @@ -35,7 +33,7 @@ export class TodosComponent implements OnInit {

ngOnInit(): void {
// get TODOs from server
console.log("todo: adding subscriptions")
console.log("todo: adding subscriptions");
this.subscriptions.push(this.tasksService.currentTasks.subscribe(tasks => {
this.tasks = tasks;
this.numTasks = this.tasksService.numTasks;
Expand Down
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
Loading

0 comments on commit dd9a0ed

Please sign in to comment.