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

chanholee pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new 8251dc4e01 [ZEPPELIN-6366] Separate WebSocket max message size into a 
dedicated REST API
8251dc4e01 is described below

commit 8251dc4e013cde02879d186240155da8f996bdb7
Author: SeungYoung Oh <[email protected]>
AuthorDate: Thu Jan 22 15:49:09 2026 +0900

    [ZEPPELIN-6366] Separate WebSocket max message size into a dedicated REST 
API
    
    ### What is this PR for?
    Currently, configuration data is fetched through both REST API and 
WebSocket channels. However, the WebSocket path does not perform permission 
checks, and the only required data from it is the WebSocket max message size.
    
    I extracted the websocket max message size field into a dedicated REST API, 
to improve security and simplify configuration handling.
    
    ### What type of PR is it?
    Improvement
    
    ### Todos
    * [ ] #5060
    
    ### What is the Jira issue?
    * Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/6366
    
    ### How should this be tested?
    - Check the configuration page (/configuration)
    - Check the notebook page (/notebook/{notebook_id})
    
    ### Screenshots (if appropriate)
    
    ### Questions:
    * Does the license files need to update? N
    * Is there breaking changes for older versions? Y
    * Does this needs documentation? N
    
    
    Closes #5099 from seung-00/feature/ZEPPELIN-6366.
    
    Signed-off-by: ChanHo Lee <[email protected]>
---
 conf/shiro.ini.template                            |  1 +
 .../apache/zeppelin/integration/ZeppelinIT.java    |  1 +
 .../zeppelin/rest/ConfigurationsRestApi.java       | 15 ++++++++
 .../zeppelin/service/ConfigurationService.java     |  4 ++
 .../src/interfaces/message-common.interface.ts     |  4 ++
 .../notebook/action-bar/action-bar.component.ts    | 15 ++++++--
 .../src/app/services/configuration.service.ts      | 12 +++++-
 .../share/note-import/note-import.component.html   |  2 +-
 .../app/share/note-import/note-import.component.ts | 12 +++---
 .../src/app/notebook/notebook.controller.js        | 43 +++++++++++-----------
 .../note-import/note-import.controller.js          | 12 +++---
 11 files changed, 82 insertions(+), 39 deletions(-)

diff --git a/conf/shiro.ini.template b/conf/shiro.ini.template
index 363b222334..6721d175f9 100644
--- a/conf/shiro.ini.template
+++ b/conf/shiro.ini.template
@@ -123,6 +123,7 @@ admin = *
 /api/interpreter/setting/restart/** = authc
 /api/interpreter/** = authc, roles[admin]
 /api/notebook-repositories/** = authc, roles[admin]
+/api/configurations/client/** = anon
 /api/configurations/** = authc, roles[admin]
 /api/credential/** = authc, roles[admin]
 /api/admin/** = authc, roles[admin]
diff --git 
a/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
 
b/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
index 20a90b3cc0..4d94ce69c7 100644
--- 
a/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
+++ 
b/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
@@ -334,6 +334,7 @@ class ZeppelinIT extends AbstractZeppelinIT {
 
       // Click on 1 paragraph to trigger z.runParagraph() function
 
+      ZeppelinITUtils.sleep(1000, false);
       clickAndWait(By.xpath(getParagraphXPath(1) + 
"//div[@id=\"angularRunParagraph\"]"));
 
       waitForParagraph(2, "FINISHED");
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ConfigurationsRestApi.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ConfigurationsRestApi.java
index 78b8f8ef04..4e6f88e1a1 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ConfigurationsRestApi.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ConfigurationsRestApi.java
@@ -46,6 +46,21 @@ public class ConfigurationsRestApi extends AbstractRestApi {
     this.configurationService = configurationService;
   }
 
+  @GET
+  @Path("client")
+  @ZeppelinApi
+  public Response getClientConfigurations() {
+    try {
+        int maxMessageSize = configurationService.getWsMaxMessageSize();
+
+        return new JsonResponse<>(Status.OK, "", Map.of(
+                "wsMaxMessageSize", maxMessageSize
+        )).build();
+        } catch (Exception e) {
+        return new JsonResponse<>(Status.INTERNAL_SERVER_ERROR, "Fail to get 
max message size", e).build();
+    }
+  }
+
   @GET
   @Path("all")
   @ZeppelinApi
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/ConfigurationService.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/ConfigurationService.java
index a73c0a9bb6..a3b226770d 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/ConfigurationService.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/ConfigurationService.java
@@ -43,6 +43,10 @@ public class ConfigurationService {
     return properties;
   }
 
+  public int getWsMaxMessageSize() {
+    return Integer.parseInt(zConf.getWebsocketMaxTextMessageSize());
+  }
+
   public Map<String, String> getPropertiesWithPrefix(String prefix,
                                                      ServiceContext context,
                                                      
ServiceCallback<Map<String, String>> callback)
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 f8fcf52c77..2ebbe71f52 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
@@ -29,6 +29,10 @@ export interface Ticket {
   roles: string;
 }
 
+export interface ClientConfigurations {
+  wsMaxMessageSize: number;
+}
+
 export interface ConfigurationsInfo {
   configurations: {
     'zeppelin.war.tempdir': string;
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 4d3cbe86c6..8a68abb0db 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
@@ -29,7 +29,14 @@ 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, NotebookService, NoteStatusService, SaveAsService, 
TicketService } from '@zeppelin/services';
+import {
+  ConfigurationService,
+  MessageService,
+  NotebookService,
+  NoteStatusService,
+  SaveAsService,
+  TicketService
+} from '@zeppelin/services';
 import { NoteCreateComponent, ShortcutComponent } from '@zeppelin/share';
 
 @Component({
@@ -189,11 +196,12 @@ export class NotebookActionBarComponent extends 
MessageListenersManager implemen
     });
   }
 
-  exportNote() {
+  async exportNote() {
     if (!this.ticketService.configuration) {
       throw new Error('Configuration is not loaded');
     }
-    const sizeLimit = 
+this.ticketService.configuration['zeppelin.websocket.max.text.message.size'];
+
+    const sizeLimit = await this.configurationService.fetchWsMaxMessageSize();
     const jsonContent = JSON.stringify(this.note);
     if (jsonContent.length > sizeLimit) {
       this.nzModalService.confirm({
@@ -324,6 +332,7 @@ export class NotebookActionBarComponent extends 
MessageListenersManager implemen
     @Inject(TRASH_FOLDER_ID_TOKEN) public TRASH_FOLDER_ID: string,
     private nzModalService: NzModalService,
     private ticketService: TicketService,
+    private configurationService: ConfigurationService,
     private nzMessageService: NzMessageService,
     private router: Router,
     private cdr: ChangeDetectorRef,
diff --git a/zeppelin-web-angular/src/app/services/configuration.service.ts 
b/zeppelin-web-angular/src/app/services/configuration.service.ts
index fdc8104374..068d7ff9f4 100644
--- a/zeppelin-web-angular/src/app/services/configuration.service.ts
+++ b/zeppelin-web-angular/src/app/services/configuration.service.ts
@@ -13,8 +13,10 @@
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 
+import { Observable } from 'rxjs';
+import { ClientConfigurations } from '@zeppelin/sdk';
+import { BaseUrlService } from '@zeppelin/services/base-url.service';
 import { BaseRest } from './base-rest';
-import { BaseUrlService } from './base-url.service';
 
 @Injectable({
   providedIn: 'root'
@@ -27,7 +29,13 @@ export class ConfigurationService extends BaseRest {
     super(baseUrlService);
   }
 
-  getAll() {
+  fetchWsMaxMessageSize(): Promise<number> {
+    const configurations = 
this.http.get<ClientConfigurations>(this.restUrl`/configurations/client`);
+
+    return configurations.toPromise().then(config => config.wsMaxMessageSize);
+  }
+
+  getAll(): Observable<{ [p: string]: string }> {
     return this.http.get<{ [key: string]: string 
}>(this.restUrl`/configurations/all`);
   }
 }
diff --git 
a/zeppelin-web-angular/src/app/share/note-import/note-import.component.html 
b/zeppelin-web-angular/src/app/share/note-import/note-import.component.html
index ae099ca1c9..b7899e4b91 100644
--- a/zeppelin-web-angular/src/app/share/note-import/note-import.component.html
+++ b/zeppelin-web-angular/src/app/share/note-import/note-import.component.html
@@ -28,7 +28,7 @@
       <p class="ant-upload-text">Click or drag JSON file to this area to 
upload</p>
       <p class="ant-upload-hint">
         JSON file size cannot exceed
-        <strong class="tips warning">{{ maxLimit | humanizeBytes }}</strong>
+        <strong class="tips warning">{{ wsMaxLimit | humanizeBytes }}</strong>
       </p>
     </nz-upload>
   </nz-tab>
diff --git 
a/zeppelin-web-angular/src/app/share/note-import/note-import.component.ts 
b/zeppelin-web-angular/src/app/share/note-import/note-import.component.ts
index b92e4d62a1..98d298b769 100644
--- a/zeppelin-web-angular/src/app/share/note-import/note-import.component.ts
+++ b/zeppelin-web-angular/src/app/share/note-import/note-import.component.ts
@@ -12,9 +12,8 @@
 
 import { HttpClient } from '@angular/common/http';
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from 
'@angular/core';
-import { MessageService, TicketService } from '@zeppelin/services';
+import { ConfigurationService, MessageService, TicketService } from 
'@zeppelin/services';
 
-import { get } from 'lodash';
 import { NzModalRef } from 'ng-zorro-antd/modal';
 import { NzUploadFile } from 'ng-zorro-antd/upload';
 
@@ -32,7 +31,7 @@ export class NoteImportComponent extends 
MessageListenersManager implements OnIn
   importUrl?: string;
   errorText?: string;
   importLoading = false;
-  maxLimit = get(this.ticketService.configuration, 
['zeppelin.websocket.max.text.message.size'], null);
+  wsMaxLimit?: number;
 
   @MessageListener(OP.IMPORT_NOTE)
   noteImported(_: MessageReceiveDataTypeMap[OP.IMPORT_NOTE]) {
@@ -59,7 +58,7 @@ export class NoteImportComponent extends 
MessageListenersManager implements OnIn
 
   beforeUpload = (file: NzUploadFile): boolean => {
     this.errorText = '';
-    if (file.size !== undefined && this.maxLimit && file.size > 
Number.parseInt(this.maxLimit, 10)) {
+    if (file.size !== undefined && this.wsMaxLimit && file.size > 
this.wsMaxLimit) {
       this.errorText = 'File size limit Exceeded!';
     } else {
       const reader = new FileReader();
@@ -102,6 +101,7 @@ export class NoteImportComponent extends 
MessageListenersManager implements OnIn
   constructor(
     public messageService: MessageService,
     private ticketService: TicketService,
+    private configurationService: ConfigurationService,
     private cdr: ChangeDetectorRef,
     private nzModalRef: NzModalRef,
     private httpClient: HttpClient
@@ -109,5 +109,7 @@ export class NoteImportComponent extends 
MessageListenersManager implements OnIn
     super(messageService);
   }
 
-  ngOnInit() {}
+  async ngOnInit() {
+    this.wsMaxLimit = await this.configurationService.fetchWsMaxMessageSize();
+  }
 }
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js 
b/zeppelin-web/src/app/notebook/notebook.controller.js
index 67a088cd9f..2a987f8a67 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -267,30 +267,29 @@ function NotebookCtrl($scope, $route, $routeParams, 
$location, $rootScope,
     return note && note.path ? note.path.split('/')[1] === TRASH_FOLDER_ID : 
false;
   };
 
-  // Export notebook
-  let limit = 0;
+  $scope.exportNote = function() {
+    $http.get(baseUrlSrv.getRestApiBase() + 
'/configurations/client').then(function(response) {
+      const limit = response.data.body.wsMessageMaxSize;
 
-  websocketMsgSrv.listConfigurations();
-  $scope.$on('configurationsInfo', function(scope, event) {
-    limit = event.configurations['zeppelin.websocket.max.text.message.size'];
-  });
+      let jsonContent = JSON.stringify($scope.note, null, 2);
 
-  $scope.exportNote = function() {
-    let jsonContent = JSON.stringify($scope.note, null, 2);
-    if (jsonContent.length > limit) {
-      BootstrapDialog.confirm({
-        closable: true,
-        title: 'Note size exceeds importable limit (' + limit + ')',
-        message: 'Do you still want to export this note?',
-        callback: function(result) {
-          if (result) {
-            saveAsService.saveAs(jsonContent, $scope.note.name + '_' + 
$scope.note.id, 'zpln');
-          }
-        },
-      });
-    } else {
-      saveAsService.saveAs(jsonContent, $scope.note.name + '_' + 
$scope.note.id, 'zpln');
-    }
+      if (jsonContent.length > limit) {
+        BootstrapDialog.confirm({
+          closable: true,
+          title: 'Note size exceeds importable limit (' + limit + ')',
+          message: 'Do you still want to export this note?',
+          callback: function(result) {
+            if (result) {
+              saveAsService.saveAs(jsonContent, $scope.note.name + '_' + 
$scope.note.id, 'zpln');
+            }
+          },
+        });
+      } else {
+        saveAsService.saveAs(jsonContent, $scope.note.name + '_' + 
$scope.note.id, 'zpln');
+      }
+    }).catch(function(err) {
+      console.error('Error while fetching max message size', err);
+    });
   };
 
   // Export nbformat
diff --git a/zeppelin-web/src/components/note-import/note-import.controller.js 
b/zeppelin-web/src/components/note-import/note-import.controller.js
index fb14ac2edd..8ccfb6731f 100644
--- a/zeppelin-web/src/components/note-import/note-import.controller.js
+++ b/zeppelin-web/src/components/note-import/note-import.controller.js
@@ -16,7 +16,7 @@ import './note-import.css';
 
 angular.module('zeppelinWebApp').controller('NoteImportCtrl', NoteImportCtrl);
 
-function NoteImportCtrl($scope, $timeout, websocketMsgSrv) {
+function NoteImportCtrl($scope, $timeout, websocketMsgSrv, $http, baseUrlSrv) {
   'ngInject';
 
   let vm = this;
@@ -26,11 +26,11 @@ function NoteImportCtrl($scope, $timeout, websocketMsgSrv) {
   $scope.maxLimit = '';
   let limit = 0;
 
-  websocketMsgSrv.listConfigurations();
-  $scope.$on('configurationsInfo', function(scope, event) {
-    limit = event.configurations['zeppelin.websocket.max.text.message.size'];
-    $scope.maxLimit = Math.round(limit / 1048576);
-  });
+  $http.get(baseUrlSrv.getRestApiBase() + '/configurations/client')
+    .then(function(response) {
+      limit = response.data.body.wsMaxMessageSize;
+      $scope.maxLimit = Math.round(limit / 1048576);
+    });
 
   vm.resetFlags = function() {
     $scope.note = {};

Reply via email to