This is an automated email from the ASF dual-hosted git repository.

pdallig pushed a commit to branch branch-0.12
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/branch-0.12 by this push:
     new aed246d630 [ZEPPELIN-6283] Decouple 'isRevisionSupported' state from 
configuration API response
aed246d630 is described below

commit aed246d630c3eeec149e598d379dc40041de6a99
Author: SeungYoung Oh <seung...@naver.com>
AuthorDate: Wed Aug 20 15:38:48 2025 +0900

    [ZEPPELIN-6283] Decouple 'isRevisionSupported' state from configuration API 
response
    
    ### What is this PR for?
    This PR separates the `isRevisionSupported` field from the configuration 
API into a dedicated endpoint.
    No internal logic is changed.
    * `isRevisionSupported` seems to be more related to Notebook logic rather 
than configuration. It is included in the WebSocket configuration response, 
making the code more complex and less extensible.
      * For example, the REST configuration response doesn’t have 
`isRevisionSupported`. Adding it would require injecting a Notebook instance 
into ConfigurationsRestApi, which doesn’t seem related to configurations. It 
feels like a code smell.
    * On the client, it was previously fetched globally. Now it is fetched in 
the Notebook page. There seems to be no need to use it as a global state.
    
    ### What type of PR is it?
    Refactoring
    
    ### Todos
    
    ### What is the Jira issue?
    - [ZEPPELIN-6283](https://issues.apache.org/jira/browse/ZEPPELIN-6283)
    
    ### How should this be tested?
    * Run `NotebookTest.testRevisionSupported()`
    * Verify on both New UI and Old UI
    
    ### Questions:
    * Does the license files need to update? N
    * Is there breaking changes for older versions? N
    * Does this needs documentation? N
    
    Closes #5030 from seung-00/feature/ZEPPELIN-6283.
    
    Signed-off-by: Philipp Dallig <philipp.dal...@gmail.com>
    (cherry picked from commit 6dd03ab5bd07f192f7ec6381c651add65eb17a1f)
    Signed-off-by: Philipp Dallig <philipp.dal...@gmail.com>
---
 .../org/apache/zeppelin/rest/NotebookRestApi.java     | 12 ++++++++++++
 .../org/apache/zeppelin/socket/NotebookServer.java    |  1 -
 .../src/interfaces/message-common.interface.ts        |  1 -
 .../interfaces/{notebook-search.ts => notebook.ts}    |  4 ++++
 zeppelin-web-angular/src/app/interfaces/public-api.ts |  2 +-
 .../notebook-search/notebook-search.component.ts      |  8 ++++----
 .../notebook/action-bar/action-bar.component.ts       | 19 ++++++++++++++-----
 ...notebook-search.service.ts => notebook.service.ts} | 10 +++++++---
 .../src/app/share/header/header.component.ts          |  6 +++---
 .../notebook-repository.controller.js                 |  1 -
 zeppelin-web/src/app/notebook/notebook-actionBar.html |  2 +-
 zeppelin-web/src/app/notebook/notebook.controller.js  | 17 +++++++++++++++++
 .../src/components/navbar/navbar.controller.js        | 12 ------------
 .../java/org/apache/zeppelin/notebook/Notebook.java   |  2 +-
 14 files changed, 64 insertions(+), 33 deletions(-)

diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
index 5d997268f4..db5275f76f 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
@@ -221,6 +221,18 @@ public class NotebookRestApi extends AbstractRestApi {
     }
   }
 
+  /**
+   * Get notebook capabilities.
+   */
+  @GET
+  @Path("capabilities")
+  @ZeppelinApi
+  public Response getNotebookCapabilities() {
+      Map<String, Object> capabilities = Map.of("isRevisionSupported", 
notebook.isRevisionSupported());
+
+      return new JsonResponse<>(Status.OK, "", capabilities).build();
+  }
+
   /**
    * Set note authorization information.
    */
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index 03637fbd2c..fabdd8eea6 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -1561,7 +1561,6 @@ public class NotebookServer implements 
AngularObjectRegistryListener,
           @Override
           public void onSuccess(Map<String, String> properties, ServiceContext 
context) throws IOException {
             super.onSuccess(properties, context);
-            properties.put("isRevisionSupported", 
String.valueOf(getNotebook().isRevisionSupported()));
             conn.send(serializeMessage(new 
Message(OP.CONFIGURATIONS_INFO).put("configurations", properties)));
           }
         });
diff --git 
a/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-common.interface.ts
 
b/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-common.interface.ts
index 82cb8e9d3c..b8011826f3 100644
--- 
a/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-common.interface.ts
+++ 
b/zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-common.interface.ts
@@ -57,7 +57,6 @@ export interface ConfigurationsInfo {
     'zeppelin.websocket.max.text.message.size': string;
     'zeppelin.notebook.git.remote.origin': string;
     'zeppelin.server.authorization.header.clear': string;
-    isRevisionSupported: string;
     'zeppelin.interpreter.dep.mvnRepo': string;
     'zeppelin.ssl': string;
     'zeppelin.notebook.autoInterpreterBinding': string;
diff --git a/zeppelin-web-angular/src/app/interfaces/notebook-search.ts 
b/zeppelin-web-angular/src/app/interfaces/notebook.ts
similarity index 90%
rename from zeppelin-web-angular/src/app/interfaces/notebook-search.ts
rename to zeppelin-web-angular/src/app/interfaces/notebook.ts
index 267ee943a5..c6c591524b 100644
--- a/zeppelin-web-angular/src/app/interfaces/notebook-search.ts
+++ b/zeppelin-web-angular/src/app/interfaces/notebook.ts
@@ -17,3 +17,7 @@ export interface NotebookSearchResultItem {
   text: string;
   header: string;
 }
+
+export interface NotebookCapabilities {
+  isRevisionSupported: boolean;
+}
diff --git a/zeppelin-web-angular/src/app/interfaces/public-api.ts 
b/zeppelin-web-angular/src/app/interfaces/public-api.ts
index e762a5c366..7e15c29918 100644
--- a/zeppelin-web-angular/src/app/interfaces/public-api.ts
+++ b/zeppelin-web-angular/src/app/interfaces/public-api.ts
@@ -17,4 +17,4 @@ export * from './message-interceptor';
 export * from './security';
 export * from './credential';
 export * from './notebook-repo';
-export * from './notebook-search';
+export * from './notebook';
diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook-search/notebook-search.component.ts
 
b/zeppelin-web-angular/src/app/pages/workspace/notebook-search/notebook-search.component.ts
index de02a290bf..1269a5bcd4 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook-search/notebook-search.component.ts
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook-search/notebook-search.component.ts
@@ -13,7 +13,7 @@
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, 
OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { NotebookSearchResultItem } from '@zeppelin/interfaces';
-import { NotebookSearchService } from 
'@zeppelin/services/notebook-search.service';
+import { NotebookService } from '@zeppelin/services/notebook.service';
 import { Subject } from 'rxjs';
 import { filter, map, switchMap, takeUntil, tap } from 'rxjs/operators';
 
@@ -33,7 +33,7 @@ export class NotebookSearchComponent implements OnInit, 
OnDestroy {
       this.searching = true;
       this.searchTerm = queryStr;
     }),
-    switchMap(queryStr => this.notebookSearchService.search(queryStr))
+    switchMap(queryStr => this.notebookService.search(queryStr))
   );
 
   results: NotebookSearchResultItem[] = [];
@@ -47,7 +47,7 @@ export class NotebookSearchComponent implements OnInit, 
OnDestroy {
   constructor(
     private cdr: ChangeDetectorRef,
     private router: ActivatedRoute,
-    private notebookSearchService: NotebookSearchService
+    private notebookService: NotebookService
   ) {}
 
   ngOnInit() {
@@ -59,7 +59,7 @@ export class NotebookSearchComponent implements OnInit, 
OnDestroy {
   }
 
   ngOnDestroy(): void {
-    this.notebookSearchService.clear();
+    this.notebookService.clearQuery();
     this.destroy$.next();
     this.destroy$.complete();
   }
diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/action-bar/action-bar.component.ts
 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/action-bar/action-bar.component.ts
index 1c73730cc7..a4da1c22e2 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/action-bar/action-bar.component.ts
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/action-bar/action-bar.component.ts
@@ -17,6 +17,7 @@ import {
   EventEmitter,
   Inject,
   Input,
+  OnInit,
   Output
 } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
@@ -26,8 +27,9 @@ import { NzModalService } from 'ng-zorro-antd/modal';
 import { MessageListener, MessageListenersManager } from '@zeppelin/core';
 import { TRASH_FOLDER_ID_TOKEN } from '@zeppelin/interfaces';
 import { MessageReceiveDataTypeMap, Note, OP, RevisionListItem } from 
'@zeppelin/sdk';
-import { MessageService, NoteActionService, NoteStatusService, SaveAsService, 
TicketService } from '@zeppelin/services';
+import { MessageService, NoteStatusService, SaveAsService, TicketService } 
from '@zeppelin/services';
 
+import { NotebookService } from '@zeppelin/services/notebook.service';
 import { NoteCreateComponent } from 
'@zeppelin/share/note-create/note-create.component';
 
 @Component({
@@ -36,7 +38,7 @@ import { NoteCreateComponent } from 
'@zeppelin/share/note-create/note-create.com
   styleUrls: ['./action-bar.component.less'],
   changeDetection: ChangeDetectionStrategy.OnPush
 })
-export class NotebookActionBarComponent extends MessageListenersManager {
+export class NotebookActionBarComponent extends MessageListenersManager 
implements OnInit {
   @Input() note!: Exclude<Note['note'], undefined>;
   @Input() isOwner = true;
   @Input() looknfeel: 'report' | 'default' | 'simple' = 'default';
@@ -52,12 +54,12 @@ export class NotebookActionBarComponent extends 
MessageListenersManager {
   @Output() readonly editorHideChange = new EventEmitter<boolean>();
   @Output() readonly tableHideChange = new EventEmitter<boolean>();
   lfOption: Array<'report' | 'default' | 'simple'> = ['default', 'simple', 
'report'];
+  isRevisionSupported: boolean = false;
   isNoteParagraphRunning = false;
   principal = this.ticketService.ticket.principal;
   editorHide = false;
   commitVisible = false;
   tableHide = false;
-  isRevisionSupported: boolean;
   cronOption = [
     { name: 'None', value: undefined },
     { name: '1m', value: '0 0/1 * * * ?' },
@@ -68,6 +70,7 @@ export class NotebookActionBarComponent extends 
MessageListenersManager {
     { name: '12h', value: '0 0 0/12 * * ?' },
     { name: '1d', value: '0 0 0 * * ?' }
   ];
+
   updateNoteName(name: string) {
     const trimmedNewName = name.trim();
     if (trimmedNewName.length > 0 && this.note.name !== trimmedNewName) {
@@ -288,8 +291,8 @@ export class NotebookActionBarComponent extends 
MessageListenersManager {
     private nzMessageService: NzMessageService,
     private router: Router,
     private cdr: ChangeDetectorRef,
-    private noteActionService: NoteActionService,
     private noteStatusService: NoteStatusService,
+    private notebookService: NotebookService,
     @Inject(TRASH_FOLDER_ID_TOKEN) public TRASH_FOLDER_ID: string,
     private activatedRoute: ActivatedRoute,
     private saveAsService: SaveAsService
@@ -299,6 +302,12 @@ export class NotebookActionBarComponent extends 
MessageListenersManager {
     if (!this.ticketService.configuration) {
       throw new Error('Configuration is not loaded');
     }
-    this.isRevisionSupported = 
JSON.parse(this.ticketService.configuration.isRevisionSupported);
+  }
+
+  ngOnInit(): void {
+    this.notebookService.capabilities().subscribe(c => {
+      this.isRevisionSupported = c.isRevisionSupported;
+      this.cdr.markForCheck();
+    });
   }
 }
diff --git a/zeppelin-web-angular/src/app/services/notebook-search.service.ts 
b/zeppelin-web-angular/src/app/services/notebook.service.ts
similarity index 83%
rename from zeppelin-web-angular/src/app/services/notebook-search.service.ts
rename to zeppelin-web-angular/src/app/services/notebook.service.ts
index 7371eec736..93d5a7a933 100644
--- a/zeppelin-web-angular/src/app/services/notebook-search.service.ts
+++ b/zeppelin-web-angular/src/app/services/notebook.service.ts
@@ -13,7 +13,7 @@
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 
-import { NotebookSearchResultItem } from '@zeppelin/interfaces';
+import { NotebookCapabilities, NotebookSearchResultItem } from 
'@zeppelin/interfaces';
 import { BaseRest } from '@zeppelin/services/base-rest';
 import { BaseUrlService } from '@zeppelin/services/base-url.service';
 import { BehaviorSubject } from 'rxjs';
@@ -21,7 +21,7 @@ import { BehaviorSubject } from 'rxjs';
 @Injectable({
   providedIn: 'root'
 })
-export class NotebookSearchService extends BaseRest {
+export class NotebookService extends BaseRest {
   private queryStr$ = new BehaviorSubject<string | null>(null);
 
   constructor(baseUrlService: BaseUrlService, private http: HttpClient) {
@@ -32,7 +32,7 @@ export class NotebookSearchService extends BaseRest {
     return this.queryStr$.asObservable();
   }
 
-  clear() {
+  clearQuery() {
     this.queryStr$.next(null);
   }
 
@@ -44,4 +44,8 @@ export class NotebookSearchService extends BaseRest {
       }
     });
   }
+
+  capabilities() {
+    return 
this.http.get<NotebookCapabilities>(this.restUrl`/notebook/capabilities`);
+  }
 }
diff --git a/zeppelin-web-angular/src/app/share/header/header.component.ts 
b/zeppelin-web-angular/src/app/share/header/header.component.ts
index 7d044f9e99..f1a1982db2 100644
--- a/zeppelin-web-angular/src/app/share/header/header.component.ts
+++ b/zeppelin-web-angular/src/app/share/header/header.component.ts
@@ -20,7 +20,7 @@ import { filter, takeUntil } from 'rxjs/operators';
 import { MessageListener, MessageListenersManager } from '@zeppelin/core';
 import { MessageReceiveDataTypeMap, OP } from '@zeppelin/sdk';
 import { MessageService, TicketService } from '@zeppelin/services';
-import { NotebookSearchService } from 
'@zeppelin/services/notebook-search.service';
+import { NotebookService } from '@zeppelin/services/notebook.service';
 import { AboutZeppelinComponent } from 
'@zeppelin/share/about-zeppelin/about-zeppelin.component';
 
 @Component({
@@ -69,7 +69,7 @@ export class HeaderComponent extends MessageListenersManager 
implements OnInit,
     private nzModalService: NzModalService,
     public messageService: MessageService,
     private router: Router,
-    private notebookSearchService: NotebookSearchService,
+    private notebookService: NotebookService,
     private cdr: ChangeDetectorRef
   ) {
     super(messageService);
@@ -92,7 +92,7 @@ export class HeaderComponent extends MessageListenersManager 
implements OnInit,
         this.cdr.markForCheck();
       });
 
-    this.notebookSearchService
+    this.notebookService
       .queried()
       .pipe(takeUntil(this.destroy$))
       .subscribe(queryStr => (this.queryStr = queryStr));
diff --git 
a/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js 
b/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js
index d6d13b32c7..ed4d48c9c8 100644
--- a/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js
+++ b/zeppelin-web/src/app/notebook-repository/notebook-repository.controller.js
@@ -65,7 +65,6 @@ function NotebookRepositoryCtrl($http, baseUrlSrv, ngToast) {
     $http.get(baseUrlSrv.getRestApiBase() + '/notebook-repositories')
       .success(function(data, status, headers, config) {
         vm.notebookRepos = data.body;
-        console.log('ya notebookRepos %o', vm.notebookRepos);
       }).error(function(data, status, headers, config) {
         if (status === 401) {
           ngToast.danger({
diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html 
b/zeppelin-web/src/app/notebook/notebook-actionBar.html
index e3d4fff507..34605abd44 100644
--- a/zeppelin-web/src/app/notebook/notebook-actionBar.html
+++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html
@@ -125,7 +125,7 @@ limitations under the License.
       </button>
     </span>
 
-    <span class="labelBtn btn-group" role="group" 
ng-if="isRevisionSupported()" >
+    <span class="labelBtn btn-group" role="group" ng-if="isRevisionSupported" >
       <div class="btn-group" role="group">
         <button type="button"
                 class="btn btn-default btn-xs dropdown-toggle"
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js 
b/zeppelin-web/src/app/notebook/notebook.controller.js
index 3b1a13f299..67a088cd9f 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -39,6 +39,7 @@ function NotebookCtrl($scope, $route, $routeParams, 
$location, $rootScope,
   $scope.collaborativeModeUsers = [];
   $scope.looknfeelOption = ['default', 'simple', 'report'];
   $scope.noteFormTitle = null;
+  $scope.isRevisionSupported = false;
   $scope.cronOption = [
     {name: 'None', value: undefined},
     {name: '1m', value: '0 0/1 * * * ?'},
@@ -169,9 +170,25 @@ function NotebookCtrl($scope, $route, $routeParams, 
$location, $rootScope,
     });
   };
 
+  const initializeRevisionSupported = function() {
+    $http.get(baseUrlSrv.getRestApiBase() + '/notebook/capabilities')
+      .then(function(response) {
+        $scope.isRevisionSupported = response.data.body.isRevisionSupported;
+      })
+      .catch(function(error) {
+        console.error('Error fetching notebook capabilities:', error);
+        ngToast.danger({
+          content: 'Failed to fetch notebook capabilities',
+          verticalPosition: 'bottom',
+          timeout: 3000,
+        });
+      });
+  };
+
   /** Init the new controller */
   const initNotebook = function() {
     noteVarShareService.clear();
+    initializeRevisionSupported();
     if ($routeParams.revisionId) {
       websocketMsgSrv.getNoteByRevision($routeParams.noteId, 
$routeParams.revisionId);
     } else {
diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js 
b/zeppelin-web/src/components/navbar/navbar.controller.js
index 503b9cd9aa..b35c60eac9 100644
--- a/zeppelin-web/src/components/navbar/navbar.controller.js
+++ b/zeppelin-web/src/components/navbar/navbar.controller.js
@@ -31,7 +31,6 @@ function NavCtrl($scope, $rootScope, $http, $routeParams, 
$location,
   vm.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
   vm.isFilterNote = isFilterNote;
   vm.numberOfNotesDisplayed = 10;
-  let revisionSupported = false;
 
   $scope.query = {q: ''};
 
@@ -263,13 +262,6 @@ function NavCtrl($scope, $rootScope, $http, $routeParams, 
$location,
     return 'top';
   };
 
-  $scope.$on('configurationsInfo', function(scope, event) {
-    // Server send this parameter is String
-    if(event.configurations['isRevisionSupported']==='true') {
-      revisionSupported = true;
-    }
-  });
-
   $scope.resolveNewUiHref = function() {
     if (location.pathname === '/') {
       return '/new';
@@ -277,8 +269,4 @@ function NavCtrl($scope, $rootScope, $http, $routeParams, 
$location,
       return '/';
     }
   };
-
-  $rootScope.isRevisionSupported = function() {
-    return revisionSupported;
-  };
 }
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
index 07ac519651..e8b92b3cc1 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
@@ -848,7 +848,7 @@ public class Notebook {
     }
   }
 
-  public Boolean isRevisionSupported() {
+  public boolean isRevisionSupported() {
     if(!zConf.getBoolean(ConfVars.ZEPPELIN_NOTEBOOK_VERSIONED_MODE_ENABLE)) {
       return false;
     }

Reply via email to