diff --git a/src/apis/html-page-api.js b/src/apis/html-page-api.js
index 003127c..c3643b5 100644
--- a/src/apis/html-page-api.js
+++ b/src/apis/html-page-api.js
@@ -52,12 +52,24 @@ class HTMLPageAPI {
return this.req.put(url, data, { headers: { 'Content-Type': 'application/json' } });
}
- listRows(page_id, table_name, start, limit) {
+ listRows(page_id, table_name, start, limit, preview_table_config) {
const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-rows/`;
const params = { page_id, table_name, start, limit };
+ if (preview_table_config && typeof preview_table_config === 'object') {
+ params.preview_table_config = JSON.stringify(preview_table_config);
+ }
return this.req.get(url, { params });
}
+ queryRows(page_id, table_name, conditions, start, limit, preview_table_config) {
+ const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-rows/query/`;
+ const data = { page_id, table_name, conditions, start, limit };
+ if (preview_table_config && typeof preview_table_config === 'object') {
+ data.preview_table_config = preview_table_config;
+ }
+ return this.req.post(url, data);
+ }
+
listCollaborators() {
const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-collaborators/`;
return this.req.get(url);
@@ -69,33 +81,48 @@ class HTMLPageAPI {
return this.req.post(url, data);
}
- addRow(page_id, table_name, row_data) {
+ addRow(page_id, table_name, row_data, preview_table_config) {
const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-rows/`;
const data = { page_id, table_name, row_data };
+ if (preview_table_config && typeof preview_table_config === 'object') {
+ data.preview_table_config = preview_table_config;
+ }
return this.req.post(url, data);
}
- addRows(page_id, table_name, rows_data) {
+ addRows(page_id, table_name, rows_data, preview_table_config) {
const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-rows/batch/`;
const data = { page_id, table_name, rows_data };
+ if (preview_table_config && typeof preview_table_config === 'object') {
+ data.preview_table_config = preview_table_config;
+ }
return this.req.post(url, data);
}
- updateRow(page_id, table_name, row_id, row_data) {
+ updateRow(page_id, table_name, row_id, row_data, preview_table_config) {
const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-rows/`;
const data = { page_id, table_name, row_id, row_data };
+ if (preview_table_config && typeof preview_table_config === 'object') {
+ data.preview_table_config = preview_table_config;
+ }
return this._sendPut(url, data);
}
- updateRows(page_id, table_name, rows_data) {
+ updateRows(page_id, table_name, rows_data, preview_table_config) {
const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-rows/batch/`;
const data = { page_id, table_name, rows_data };
+ if (preview_table_config && typeof preview_table_config === 'object') {
+ data.preview_table_config = preview_table_config;
+ }
return this._sendPut(url, data);
}
- deleteRows(page_id, table_name, rows_ids) {
+ deleteRows(page_id, table_name, rows_ids, preview_table_config) {
const url = `${this.server}api/v2.1/universal-apps/${this.appUuid}/html-page-rows/`;
const data = { page_id, table_name, rows_ids };
+ if (preview_table_config && typeof preview_table_config === 'object') {
+ data.preview_table_config = preview_table_config;
+ }
return this._sendDelete(url, data);
}
diff --git a/src/iframe-adapter.js b/src/iframe-adapter.js
index d62d2c3..1a524ae 100644
--- a/src/iframe-adapter.js
+++ b/src/iframe-adapter.js
@@ -10,6 +10,7 @@ export const POST_MESSAGE_REQUEST_TYPE = {
GET_ACCESS_TOKEN: 'get_access_token',
GET_APP_UUID: 'get_app_uuid',
GET_PAGE_ID: 'get_page_id',
+ GET_PREVIEW_TABLE_CONFIGS: 'get_preview_table_configs',
};
const WINDOW_EVENT_SOURCE_TYPE = {
diff --git a/src/sdk.js b/src/sdk.js
index 4ab9342..b46cc18 100644
--- a/src/sdk.js
+++ b/src/sdk.js
@@ -21,6 +21,10 @@ export class HTMLPageSDK {
if (!this.options.pageId) {
this.options.pageId = await this.iframeAdapter.request(POST_MESSAGE_REQUEST_TYPE.GET_PAGE_ID);
}
+ if (this.options.pageId === 'ai_agent' && !Array.isArray(this.options.previewTableConfigs)) {
+ const previewTableConfigs = await this.iframeAdapter.request(POST_MESSAGE_REQUEST_TYPE.GET_PREVIEW_TABLE_CONFIGS);
+ this.options.previewTableConfigs = Array.isArray(previewTableConfigs) ? previewTableConfigs : [];
+ }
if (this.options.accountToken) {
// dev: try to get access-token via accountToken
const { server, accountToken, appUuid } = this.options;
@@ -35,7 +39,23 @@ export class HTMLPageSDK {
}
listRows({ tableName, start, limit }) {
- return this.htmlPageAPI.listRows(this.options.pageId, tableName, start, limit);
+ const previewTableConfig = this._getPreviewTableConfig({ tableName });
+ return this.htmlPageAPI.listRows(this.options.pageId, tableName, start, limit, previewTableConfig);
+ }
+
+ queryRows({ tableName, conditions, start, limit }) {
+ const previewTableConfig = this._getPreviewTableConfig({ tableName });
+ return this.htmlPageAPI.queryRows(this.options.pageId, tableName, conditions, start, limit, previewTableConfig);
+ }
+
+ _getPreviewTableConfig({ tableName }) {
+ if (this.options.pageId !== 'ai_agent' || !Array.isArray(this.options.previewTableConfigs)) return undefined;
+ const tableConfig = this.options.previewTableConfigs.find(config => tableName && config?.table_name === tableName);
+ if (!tableConfig) return undefined;
+ return {
+ table_id: tableConfig.table_id,
+ permissions: { ...(tableConfig.permissions || {}) },
+ };
}
listCollaborators() {
@@ -47,11 +67,13 @@ export class HTMLPageSDK {
}
addRow({ tableName, rowData }) {
- return this.htmlPageAPI.addRow(this.options.pageId, tableName, rowData);
+ const previewTableConfig = this._getPreviewTableConfig({ tableName });
+ return this.htmlPageAPI.addRow(this.options.pageId, tableName, rowData, previewTableConfig);
}
updateRow({ tableName, rowId, rowData }) {
- return this.htmlPageAPI.updateRow(this.options.pageId, tableName, rowId, rowData);
+ const previewTableConfig = this._getPreviewTableConfig({ tableName });
+ return this.htmlPageAPI.updateRow(this.options.pageId, tableName, rowId, rowData, previewTableConfig);
}
deleteRow({ tableName, rowId }) {
@@ -60,15 +82,18 @@ export class HTMLPageSDK {
}
batchAddRows({ tableName, rowsData }) {
- return this.htmlPageAPI.addRows(this.options.pageId, tableName, rowsData);
+ const previewTableConfig = this._getPreviewTableConfig({ tableName });
+ return this.htmlPageAPI.addRows(this.options.pageId, tableName, rowsData, previewTableConfig);
}
batchUpdateRows({ tableName, rowsData }) {
- return this.htmlPageAPI.updateRows(this.options.pageId, tableName, rowsData);
+ const previewTableConfig = this._getPreviewTableConfig({ tableName });
+ return this.htmlPageAPI.updateRows(this.options.pageId, tableName, rowsData, previewTableConfig);
}
batchDeleteRows({ tableName, rowsIds }) {
- return this.htmlPageAPI.deleteRows(this.options.pageId, tableName, rowsIds);
+ const previewTableConfig = this._getPreviewTableConfig({ tableName });
+ return this.htmlPageAPI.deleteRows(this.options.pageId, tableName, rowsIds, previewTableConfig);
}
uploadFile({ file }) {
diff --git a/tests/html-page-api.test.js b/tests/html-page-api.test.js
index 079cd28..1e52332 100644
--- a/tests/html-page-api.test.js
+++ b/tests/html-page-api.test.js
@@ -31,6 +31,62 @@ function createApi() {
return { api, get, post, put, del };
}
+describe('HTMLPageAPI.queryRows', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('queryRows posts table name and conditions to the query endpoint', () => {
+ const { api, post } = createApi();
+
+ const response = { data: { metadata: [], results: [] } };
+ post.mockReturnValue(response);
+
+ const conditions = [{ columnName: 'Phone', value: '13800138000' }];
+ const result = api.queryRows('page-1', 'Orders', conditions, 0, 100);
+
+ expect(result).toEqual(response);
+ expect(post).toHaveBeenCalledWith(
+ 'https://example.com/api/v2.1/universal-apps/app-uuid/html-page-rows/query/',
+ {
+ page_id: 'page-1',
+ table_name: 'Orders',
+ conditions,
+ start: 0,
+ limit: 100,
+ },
+ );
+ });
+
+ it('queryRows sends preview_table_config for ai_agent preview', () => {
+ const { api, post } = createApi();
+ const previewTableConfig = {
+ table_id: 'tbl-1',
+ permissions: {
+ query_rows_permission: {
+ enabled: true,
+ columns_keys: ['phone'],
+ query_columns: [{ column_key: 'phone', enable_fuzzy_query: true }],
+ },
+ },
+ };
+
+ api.queryRows('ai_agent', 'Orders', [{ columnName: 'Phone', value: '138' }], 0, 20, previewTableConfig);
+
+ expect(post).toHaveBeenCalledWith(
+ 'https://example.com/api/v2.1/universal-apps/app-uuid/html-page-rows/query/',
+ {
+ page_id: 'ai_agent',
+ table_name: 'Orders',
+ conditions: [{ columnName: 'Phone', value: '138' }],
+ start: 0,
+ limit: 20,
+ preview_table_config: previewTableConfig,
+ },
+ );
+ });
+});
+
describe('HTMLPageAPI.listRows', () => {
beforeEach(() => {
jest.clearAllMocks();
@@ -95,6 +151,26 @@ describe('HTMLPageAPI.listRows', () => {
},
);
});
+
+ it('listRows serializes preview_table_config', () => {
+ const { api, get } = createApi();
+ const previewTableConfig = { table_id: 'tbl-1', permissions: {} };
+
+ api.listRows('ai_agent', 'TableName', 0, 100, previewTableConfig);
+
+ expect(get).toHaveBeenCalledWith(
+ 'https://example.com/api/v2.1/universal-apps/app-uuid/html-page-rows/',
+ {
+ params: {
+ page_id: 'ai_agent',
+ table_name: 'TableName',
+ start: 0,
+ limit: 100,
+ preview_table_config: JSON.stringify(previewTableConfig),
+ },
+ },
+ );
+ });
});
describe('HTMLPageAPI.listCollaborators', () => {
@@ -271,6 +347,52 @@ describe('HTMLPageAPI.deleteRows(s)', () => {
},
);
});
+
+ it('write APIs send preview_table_config', () => {
+ const { api, post, put, del } = createApi();
+ const previewTableConfig = { table_id: 'tbl-1', permissions: {} };
+
+ api.addRow('ai_agent', 'TableName', { Name: 'A' }, previewTableConfig);
+ api.addRows('ai_agent', 'TableName', [{ Name: 'B' }], previewTableConfig);
+ api.updateRow('ai_agent', 'TableName', 'row-1', { Name: 'C' }, previewTableConfig);
+ api.updateRows('ai_agent', 'TableName', [{ row_id: 'row-1', row: { Name: 'D' } }], previewTableConfig);
+ api.deleteRows('ai_agent', 'TableName', ['row-1'], previewTableConfig);
+
+ expect(post).toHaveBeenNthCalledWith(1, expect.any(String), {
+ page_id: 'ai_agent',
+ table_name: 'TableName',
+ row_data: { Name: 'A' },
+ preview_table_config: previewTableConfig,
+ });
+ expect(post).toHaveBeenNthCalledWith(2, expect.any(String), {
+ page_id: 'ai_agent',
+ table_name: 'TableName',
+ rows_data: [{ Name: 'B' }],
+ preview_table_config: previewTableConfig,
+ });
+ expect(put).toHaveBeenNthCalledWith(1, expect.any(String), {
+ page_id: 'ai_agent',
+ table_name: 'TableName',
+ row_id: 'row-1',
+ row_data: { Name: 'C' },
+ preview_table_config: previewTableConfig,
+ }, expect.any(Object));
+ expect(put).toHaveBeenNthCalledWith(2, expect.any(String), {
+ page_id: 'ai_agent',
+ table_name: 'TableName',
+ rows_data: [{ row_id: 'row-1', row: { Name: 'D' } }],
+ preview_table_config: previewTableConfig,
+ }, expect.any(Object));
+ expect(del).toHaveBeenCalledWith(expect.any(String), {
+ headers: { 'Content-Type': 'application/json' },
+ data: {
+ page_id: 'ai_agent',
+ table_name: 'TableName',
+ rows_ids: ['row-1'],
+ preview_table_config: previewTableConfig,
+ },
+ });
+ });
});
describe('HTMLPageAPI.upload', () => {
diff --git a/tests/sdk-init.test.js b/tests/sdk-init.test.js
index 82263ff..a0b347f 100644
--- a/tests/sdk-init.test.js
+++ b/tests/sdk-init.test.js
@@ -24,6 +24,7 @@ jest.mock('../src/iframe-adapter', () => ({
GET_ACCESS_TOKEN: 'get_access_token',
GET_APP_UUID: 'get_app_uuid',
GET_PAGE_ID: 'get_page_id',
+ GET_PREVIEW_TABLE_CONFIGS: 'get_preview_table_configs',
},
}));
@@ -78,4 +79,21 @@ describe('HTMLPageSDK.init', () => {
expect(sdk.options.pageId).toBe('page-1');
expect(sdk.options.accountToken).toBe('account-token');
});
+
+ it('loads table permission configs for ai_agent preview', async () => {
+ const previewTableConfigs = [{ table_id: 'REW7', permissions: {} }];
+ mockRequest
+ .mockResolvedValueOnce('https://example.com')
+ .mockResolvedValueOnce('app-uuid')
+ .mockResolvedValueOnce('ai_agent')
+ .mockResolvedValueOnce(previewTableConfigs)
+ .mockResolvedValueOnce('access-token');
+
+ const sdk = new HTMLPageSDK();
+ await sdk.init();
+
+ expect(mockRequest).toHaveBeenNthCalledWith(4, 'get_preview_table_configs');
+ expect(mockRequest).toHaveBeenNthCalledWith(5, 'get_access_token');
+ expect(sdk.options.previewTableConfigs).toEqual(previewTableConfigs);
+ });
});
diff --git a/tests/sdk.test.js b/tests/sdk.test.js
index 4a671a5..3d5bd88 100644
--- a/tests/sdk.test.js
+++ b/tests/sdk.test.js
@@ -1,6 +1,7 @@
import { HTMLPageSDK } from '../src/sdk';
const mockListRows = jest.fn();
+const mockQueryRows = jest.fn();
const mockListCollaborators = jest.fn();
const mockResolveUsers = jest.fn();
const mockAddRow = jest.fn();
@@ -19,6 +20,7 @@ jest.mock('../src/iframe-adapter', () => ({
GET_ACCESS_TOKEN: 'get_access_token',
GET_APP_UUID: 'get_app_uuid',
GET_PAGE_ID: 'get_page_id',
+ GET_PREVIEW_TABLE_CONFIGS: 'get_preview_table_configs',
},
}));
@@ -55,7 +57,93 @@ describe('rows', () => {
});
expect(result).toBe(response);
- expect(mockListRows).toHaveBeenCalledWith('page-1', 'TableName', 0, 100);
+ expect(mockListRows).toHaveBeenCalledWith('page-1', 'TableName', 0, 100, undefined);
+ });
+
+ it('query rows by table and column names', () => {
+ const sdk = new HTMLPageSDK({ pageId: 'page-1' });
+ sdk.htmlPageAPI = { queryRows: mockQueryRows };
+
+ const conditions = [{ columnName: 'Order Number', value: 'TEST2026070001' }];
+ sdk.queryRows({ tableName: 'Order', conditions });
+
+ expect(mockQueryRows).toHaveBeenCalledWith('page-1', 'Order', conditions, undefined, undefined, undefined);
+ });
+
+ it('query rows includes the configured table permissions for ai_agent preview', () => {
+ const previewTableConfig = {
+ table_id: 'REW7',
+ table_name: 'Order',
+ permissions: {
+ query_rows_permission: {
+ enabled: true,
+ columns_keys: ['0000', 'wbyd'],
+ query_columns: [{ column_key: '0000', enable_fuzzy_query: true, case_sensitive: false }],
+ },
+ },
+ };
+ const sdk = new HTMLPageSDK({ pageId: 'ai_agent', previewTableConfigs: [previewTableConfig] });
+ sdk.htmlPageAPI = { queryRows: mockQueryRows };
+
+ const conditions = [{ columnName: 'Order Number', value: '202607' }];
+ sdk.queryRows({ tableName: 'Order', conditions });
+
+ expect(mockQueryRows).toHaveBeenCalledWith(
+ 'ai_agent',
+ 'Order',
+ conditions,
+ undefined,
+ undefined,
+ {
+ table_id: 'REW7',
+ permissions: previewTableConfig.permissions,
+ },
+ );
+ });
+
+ it('row APIs include the matching table permissions for ai_agent preview', () => {
+ const previewTableConfig = {
+ table_id: 'REW7',
+ table_name: 'Order',
+ permissions: {
+ view_rows_permission: { enabled: true, columns_keys: ['0000'] },
+ add_rows_permission: { enabled: true, columns_keys: ['0000'] },
+ edit_rows_permission: { enabled: true, columns_keys: ['0000'] },
+ delete_rows_permission: { enabled: true },
+ },
+ };
+ const expectedConfig = {
+ table_id: 'REW7',
+ permissions: previewTableConfig.permissions,
+ };
+ const sdk = new HTMLPageSDK({ pageId: 'ai_agent', previewTableConfigs: [previewTableConfig] });
+ sdk.htmlPageAPI = {
+ listRows: mockListRows,
+ addRow: mockAddRow,
+ updateRow: mockUpdateRow,
+ deleteRows: mockDeleteRows,
+ addRows: mockAddRows,
+ updateRows: mockUpdateRows,
+ };
+
+ sdk.listRows({ tableName: 'Order' });
+ sdk.addRow({ tableName: 'Order', rowData: { OrderNumber: '001' } });
+ sdk.updateRow({ tableName: 'Order', rowId: 'row-1', rowData: { OrderNumber: '002' } });
+ sdk.batchAddRows({ tableName: 'Order', rowsData: [{ OrderNumber: '003' }] });
+ sdk.batchUpdateRows({ tableName: 'Order', rowsData: [{ row_id: 'row-1', row: { OrderNumber: '004' } }] });
+ sdk.batchDeleteRows({ tableName: 'Order', rowsIds: ['row-1'] });
+
+ expect(mockListRows).toHaveBeenCalledWith('ai_agent', 'Order', undefined, undefined, expectedConfig);
+ expect(mockAddRow).toHaveBeenCalledWith('ai_agent', 'Order', { OrderNumber: '001' }, expectedConfig);
+ expect(mockUpdateRow).toHaveBeenCalledWith('ai_agent', 'Order', 'row-1', { OrderNumber: '002' }, expectedConfig);
+ expect(mockAddRows).toHaveBeenCalledWith('ai_agent', 'Order', [{ OrderNumber: '003' }], expectedConfig);
+ expect(mockUpdateRows).toHaveBeenCalledWith(
+ 'ai_agent',
+ 'Order',
+ [{ row_id: 'row-1', row: { OrderNumber: '004' } }],
+ expectedConfig,
+ );
+ expect(mockDeleteRows).toHaveBeenCalledWith('ai_agent', 'Order', ['row-1'], expectedConfig);
});
it('list collaborators', () => {
@@ -121,11 +209,11 @@ describe('rows', () => {
expect(sdk.batchUpdateRows({ tableName: 'TableName', rowsData: [{ row_id: 'row-1', Name: 'Jane' }] })).toEqual(updateRowsResponse);
expect(sdk.batchDeleteRows({ tableName: 'TableName', rowsIds: ['row-1'] })).toEqual(deleteRowsResponse);
- expect(mockAddRow).toHaveBeenCalledWith('page-1', 'TableName', { Name: 'John' });
- expect(mockUpdateRow).toHaveBeenCalledWith('page-1', 'TableName', 'row-1', { Name: 'Jane' });
- expect(mockDeleteRows).toHaveBeenCalledWith('page-1', 'TableName', ['row-1']);
- expect(mockAddRows).toHaveBeenCalledWith('page-1', 'TableName', [{ Name: 'John' }]);
- expect(mockUpdateRows).toHaveBeenCalledWith('page-1', 'TableName', [{ row_id: 'row-1', Name: 'Jane' }]);
+ expect(mockAddRow).toHaveBeenCalledWith('page-1', 'TableName', { Name: 'John' }, undefined);
+ expect(mockUpdateRow).toHaveBeenCalledWith('page-1', 'TableName', 'row-1', { Name: 'Jane' }, undefined);
+ expect(mockDeleteRows).toHaveBeenCalledWith('page-1', 'TableName', ['row-1'], undefined);
+ expect(mockAddRows).toHaveBeenCalledWith('page-1', 'TableName', [{ Name: 'John' }], undefined);
+ expect(mockUpdateRows).toHaveBeenCalledWith('page-1', 'TableName', [{ row_id: 'row-1', Name: 'Jane' }], undefined);
expect(mockDeleteRows).toHaveBeenCalledTimes(2);
});
});