diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html
index 6dc08633df8b..f17bf0cdf6f3 100644
--- a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html
+++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html
@@ -61,6 +61,7 @@
[noteId]="note.id"
[(activatedExtension)]="activatedExtension"
[permissions]="permissions"
+ (permissionsSaved)="getPermissions(note)"
>
}
@case ('revisions') {
diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.html b/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.html
index 37595eabb3d6..3f4a73d59f26 100644
--- a/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.html
+++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.html
@@ -29,13 +29,13 @@
Note Permissions (Only note owners can change)
- @for (item of permissions.owners; track item) {
+ @for (item of draftPermissions.owners; track item) {
}
@for (item of listOfUserAndRole; track item) {
@@ -56,13 +56,13 @@ Note Permissions (Only note owners can change)
- @for (item of permissions.writers; track item) {
+ @for (item of draftPermissions.writers; track item) {
}
@for (item of listOfUserAndRole; track item) {
@@ -83,13 +83,13 @@ Note Permissions (Only note owners can change)
- @for (item of permissions.runners; track item) {
+ @for (item of draftPermissions.runners; track item) {
}
@for (item of listOfUserAndRole; track item) {
@@ -110,13 +110,13 @@ Note Permissions (Only note owners can change)
- @for (item of permissions.readers; track item) {
+ @for (item of draftPermissions.readers; track item) {
}
@for (item of listOfUserAndRole; track item) {
diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.spec.ts b/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.spec.ts
new file mode 100644
index 000000000000..908ffdca201e
--- /dev/null
+++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.spec.ts
@@ -0,0 +1,112 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ChangeDetectorRef, SimpleChange } from '@angular/core';
+import { NzMessageService } from 'ng-zorro-antd/message';
+import { NzModalService } from 'ng-zorro-antd/modal';
+import { of } from 'rxjs';
+import { describe, expect, it, vi } from 'vitest';
+
+import { Permissions } from '@zeppelin/interfaces';
+import { SecurityService, TicketService } from '@zeppelin/services';
+
+import { NotebookPermissionsComponent } from './permissions.component';
+
+const savedPermissions = (): Permissions => ({
+ owners: ['alice'],
+ writers: ['bob'],
+ runners: ['carol'],
+ readers: ['dave']
+});
+
+const createComponent = (permissions: Permissions) => {
+ const setPermissions = vi.fn(() => of(undefined));
+ const component = new NotebookPermissionsComponent(
+ { setPermissions } as unknown as SecurityService,
+ { markForCheck: vi.fn() } as unknown as ChangeDetectorRef,
+ { success: vi.fn() } as unknown as NzMessageService,
+ { ticket: { principal: 'alice' } } as unknown as TicketService,
+ { create: vi.fn() } as unknown as NzModalService
+ );
+ component.noteId = 'note-1';
+ component.permissions = permissions;
+ component.ngOnInit();
+ return { component, setPermissions };
+};
+
+const editEveryList = (component: NotebookPermissionsComponent) => {
+ component.draftPermissions.owners = [];
+ component.draftPermissions.writers.push('eve');
+ component.draftPermissions.runners = ['frank'];
+ component.draftPermissions.readers.pop();
+};
+
+describe('NotebookPermissionsComponent cancel', () => {
+ it('leaves the parent permissions unchanged when edits are cancelled', () => {
+ const parent = savedPermissions();
+ const { component, setPermissions } = createComponent(parent);
+
+ editEveryList(component);
+ component.closePermissions();
+
+ expect(parent).toEqual(savedPermissions());
+ expect(setPermissions).not.toHaveBeenCalled();
+ });
+
+ it('shows the saved values when the panel is reopened after cancel', () => {
+ const parent = savedPermissions();
+ const { component: first } = createComponent(parent);
+ editEveryList(first);
+ first.closePermissions();
+
+ const { component: reopened } = createComponent(parent);
+
+ expect(reopened.draftPermissions).toEqual(savedPermissions());
+ });
+
+ it('restores the draft from the saved values on reset', () => {
+ const { component } = createComponent(savedPermissions());
+ editEveryList(component);
+
+ component.resetPermissions();
+
+ expect(component.draftPermissions).toEqual(savedPermissions());
+ });
+
+ it('refreshes the draft when new saved permissions arrive', () => {
+ const { component } = createComponent(savedPermissions());
+ editEveryList(component);
+ const next: Permissions = { owners: ['zoe'], writers: [], runners: [], readers: [] };
+
+ component.permissions = next;
+ component.ngOnChanges({ permissions: new SimpleChange(undefined, next, false) });
+
+ expect(component.draftPermissions).toEqual(next);
+ });
+});
+
+describe('NotebookPermissionsComponent save', () => {
+ it('persists all four edited lists and reports them to the parent', () => {
+ const parent = savedPermissions();
+ const { component, setPermissions } = createComponent(parent);
+ const saved = vi.fn();
+ component.permissionsSaved.subscribe(saved);
+ const expected: Permissions = { owners: ['alice'], writers: ['bob', 'eve'], runners: ['frank'], readers: [] };
+
+ component.draftPermissions = { ...expected, writers: [...expected.writers] };
+ component.savePermissions();
+
+ expect(setPermissions).toHaveBeenCalledWith('note-1', expected);
+ expect(saved).toHaveBeenCalledWith(expected);
+ expect(parent).toEqual(savedPermissions());
+ });
+});
diff --git a/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.ts b/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.ts
index f6015e09b313..2d4438d45225 100644
--- a/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.ts
+++ b/zeppelin-web-angular/src/app/pages/workspace/notebook/permissions/permissions.component.ts
@@ -18,7 +18,8 @@ import {
Input,
OnChanges,
OnInit,
- Output
+ Output,
+ SimpleChanges
} from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd/message';
@@ -27,6 +28,13 @@ import { NzModalService } from 'ng-zorro-antd/modal';
import { Permissions } from '@zeppelin/interfaces';
import { SecurityService, TicketService } from '@zeppelin/services';
+const clonePermissions = (permissions: Permissions): Permissions => ({
+ owners: [...permissions.owners],
+ writers: [...permissions.writers],
+ runners: [...permissions.runners],
+ readers: [...permissions.readers]
+});
+
@Component({
selector: 'zeppelin-notebook-permissions',
templateUrl: './permissions.component.html',
@@ -41,7 +49,8 @@ export class NotebookPermissionsComponent implements OnInit, OnChanges {
@Output() readonly activatedExtensionChange = new EventEmitter<
'interpreter' | 'permissions' | 'revisions' | 'hide'
>();
- permissionsBack!: Permissions;
+ @Output() readonly permissionsSaved = new EventEmitter();
+ draftPermissions!: Permissions;
listOfUserAndRole: Array<{ text: string; children: string[] }> = [];
savePermissions() {
@@ -57,7 +66,7 @@ export class NotebookPermissionsComponent implements OnInit, OnChanges {
'Please fill the [Owners] field. If not, it will set as current user. ' +
`Current user : [ ${this.ticketService.ticket.principal.trim()} ]`,
nzOnOk: () => {
- this.permissions.owners = [this.ticketService.ticket.principal];
+ this.draftPermissions.owners = [this.ticketService.ticket.principal];
this.setPermissions();
},
nzOnCancel: () => {
@@ -87,18 +96,20 @@ export class NotebookPermissionsComponent implements OnInit, OnChanges {
}
setPermissions() {
- this.securityService.setPermissions(this.noteId, this.permissions).subscribe(() => {
+ const saved = clonePermissions(this.draftPermissions);
+ this.securityService.setPermissions(this.noteId, saved).subscribe(() => {
this.nzMessageService.success('Permissions Saved Successfully');
+ this.permissionsSaved.emit(saved);
this.closePermissions();
});
}
resetPermissions() {
- this.permissions = { ...this.permissionsBack };
+ this.draftPermissions = clonePermissions(this.permissions);
}
isOwnerEmpty() {
- return !this.permissions.owners.some(o => o.trim().length > 0);
+ return !this.draftPermissions.owners.some(o => o.trim().length > 0);
}
searchUser(search: string) {
@@ -130,10 +141,12 @@ export class NotebookPermissionsComponent implements OnInit, OnChanges {
) {}
ngOnInit() {
- this.permissionsBack = { ...this.permissions };
+ this.resetPermissions();
}
- ngOnChanges(): void {
- this.permissionsBack = { ...this.permissions };
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.permissions) {
+ this.resetPermissions();
+ }
}
}