From 87ee6afb86111ccbb482c9c8dd3ee4e60ed41de6 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Sat, 12 Jul 2025 14:05:23 +0000 Subject: [PATCH 01/12] Add Asyncronous fetchAll --- AppSheetApp.js | 54 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/AppSheetApp.js b/AppSheetApp.js index 3923db1..1ee9ce5 100644 --- a/AppSheetApp.js +++ b/AppSheetApp.js @@ -26,13 +26,13 @@ * 4. Ensure that at least one unexpired **Application Access Key** is present. * Otherwise, click **Create Application Access Key**. * 5. When you are done, save the app. - * 6. Use your app ID and Access Key to connect Apps Script to your app + * 6. Use your app ID and Access Key to connect Apps Script to your app * * @param {String} appId AppSheet App ID. - * @param {String} applicationAccessKey AppSheet App Access Key. + * @param {String} applicationAccessKey AppSheet App Access Key. * @return {AppSheetApp} */ -function connect(appId, applicationAccessKey) { +function (appId, applicationAccessKey) { return new AppSheetApp(appId, applicationAccessKey); } @@ -77,7 +77,7 @@ function Edit(tableName, rows, properties = {}) { * * @param {String} tableName - specifies the name of the table * @param {Object[]} rows - **Optional**. You can omit the Selector property and specify input Rows containing the key values of the records to be read. - * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. + * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. * @returns {Object} AppSheet Response */ function Find(tableName, rows, properties = {}) { @@ -88,12 +88,12 @@ function Find(tableName, rows, properties = {}) { * Invoke an action * * @param {String} tableName - specifies the name of the table - * @param {String} action - The action name. + * @param {String} action - The action name. * @param {Object[]} rows - One or more Rows elements specifying the key field values of the rows to which the action is to be applied. * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] * @returns {Object} AppSheet Response */ -function Action(tableName, action, rows, properties = {}) { +function Action(tableName, action, rows, properties = {}) { return AppSheetApp._appSheetAPI(tableName, action, rows, properties); } @@ -104,7 +104,7 @@ class _AppSheet { /** * @constructor * @param {String} appId - AppSheet Application ID. - * @param {String} applicationAccessKey - AppSheet Access Key. + * @param {String} applicationAccessKey - AppSheet Access Key. * @return {_AppSheet} */ constructor(appId, applicationAccessKey) { @@ -132,6 +132,20 @@ class _AppSheet { return this._appSheetAPI(tableName, action, rows, properties); } + fetchAll(params){ + params = params.map(param => + this._appSheetRequest(param.tableName, param.action, param?.rows, param.properties) + ) + try { + const responses = UrlFetchApp.fetchAll(params); + return responses.map( + response => JSON.parse(response.getContentText()) + ); + } catch (e) { + return e; + } + } + _appSheetAPI(tableName, action, rows, properties) { const self = this; @@ -161,5 +175,31 @@ class _AppSheet { return e; } } + + _appSheetRequest(tableName, action, rows = [], properties = {}) { + const self = this; + + // based on https://www.googlecloudcommunity.com/gc/Tips-Tricks/Call-AppSheet-API-from-Apps-Script/m-p/447165 + const body = { + 'Action': action, + 'Rows': rows, + "Properties": properties + }; + + // Values universal to AppSheet API calls + const url = `https://api.appsheet.com/api/v2/apps/${self.appId}/tables/${tableName}/Action`; + const method = 'POST'; + + const params = { + 'url':url, + 'method': method, + 'contentType': 'application/json', + 'headers': { 'ApplicationAccessKey': self.applicationAccessKey }, + 'payload': JSON.stringify(body), + 'muteHttpExceptions': true + }; + + return params + } } var AppSheetApp = _AppSheet; \ No newline at end of file From 1812b53fd48634cc7b83347c886f9d4f2b63a02c Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Sat, 12 Jul 2025 14:19:07 +0000 Subject: [PATCH 02/12] Clean up appSheetRequest and appSheetAPI --- AppSheetApp.js | 89 +++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 56 deletions(-) diff --git a/AppSheetApp.js b/AppSheetApp.js index 1ee9ce5..7d5a630 100644 --- a/AppSheetApp.js +++ b/AppSheetApp.js @@ -132,41 +132,18 @@ class _AppSheet { return this._appSheetAPI(tableName, action, rows, properties); } - fetchAll(params){ - params = params.map(param => - this._appSheetRequest(param.tableName, param.action, param?.rows, param.properties) - ) - try { - const responses = UrlFetchApp.fetchAll(params); - return responses.map( - response => JSON.parse(response.getContentText()) - ); - } catch (e) { - return e; - } - } - + /** + * Make a request to the AppSheet API. + * @param {String} tableName - The name of the table. + * @param {String} action - The action to perform. + * @param {Object[]} rows - The rows to operate on. + * @param {Object} properties - Additional properties for the request. + */ _appSheetAPI(tableName, action, rows, properties) { - const self = this; + const params = this._appSheetRequest(tableName, action, rows, properties); + const url = params.url; + delete params.url; // url should not be a part of params - // based on https://www.googlecloudcommunity.com/gc/Tips-Tricks/Call-AppSheet-API-from-Apps-Script/m-p/447165 - const body = { - 'Action': action, - 'Rows': rows, - "Properties": properties - }; - - // Values universal to AppSheet API calls - const url = `https://api.appsheet.com/api/v2/apps/${self.appId}/tables/${tableName}/Action`; - const method = 'POST'; - - const params = { - 'method': method, - 'contentType': 'application/json', - 'headers': { 'ApplicationAccessKey': self.applicationAccessKey }, - 'payload': JSON.stringify(body), - 'muteHttpExceptions': true - }; try { const response = UrlFetchApp.fetch(url, params); @@ -176,30 +153,30 @@ class _AppSheet { } } - _appSheetRequest(tableName, action, rows = [], properties = {}) { - const self = this; - - // based on https://www.googlecloudcommunity.com/gc/Tips-Tricks/Call-AppSheet-API-from-Apps-Script/m-p/447165 - const body = { - 'Action': action, - 'Rows': rows, - "Properties": properties - }; - - // Values universal to AppSheet API calls - const url = `https://api.appsheet.com/api/v2/apps/${self.appId}/tables/${tableName}/Action`; - const method = 'POST'; - - const params = { - 'url':url, - 'method': method, - 'contentType': 'application/json', - 'headers': { 'ApplicationAccessKey': self.applicationAccessKey }, - 'payload': JSON.stringify(body), - 'muteHttpExceptions': true + /** + * Make multiple requests to the AppSheet API. + * @param {Array<{tableName: string, action: string, rows: Object[], properties: Object}>} params - An array of request parameters. + */ + fetchAll(params){ + const requests = params.map(param => { + return this._appSheetRequest(param.tableName, param.action, param.rows, param.properties); + }); + try { + const responses = UrlFetchApp.fetchAll(requests); + return responses.map(response => JSON.parse(response.getContentText())); + } catch (e) { + return e; + } + } + _appSheetRequest(tableName, action, rows = [], properties = {}) { + return { + url: `https://api.appsheet.com/api/v2/apps/${this.appId}/tables/${tableName}/Action`, + method: 'post', + contentType: 'application/json', + headers: { ApplicationAccessKey: this.applicationAccessKey }, + payload: JSON.stringify({ Action: action, Rows: rows, Properties: properties }), + muteHttpExceptions: true }; - - return params } } var AppSheetApp = _AppSheet; \ No newline at end of file From 294e0046db4cd8ee8c2ff81d697cf98ae13b7347 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Sat, 12 Jul 2025 15:23:04 +0000 Subject: [PATCH 03/12] Update fetchAll args --- AppSheetApp.js | 51 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/AppSheetApp.js b/AppSheetApp.js index 7d5a630..aa101fc 100644 --- a/AppSheetApp.js +++ b/AppSheetApp.js @@ -112,24 +112,44 @@ class _AppSheet { this.applicationAccessKey = applicationAccessKey; } - Add(tableName, rows, properties = {}) { - return this._appSheetAPI(tableName, 'Add', rows, properties); + Add(tableName, rows, properties = {}, paramOnly = false ) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Add', rows, properties): + } else { + return this._appSheetAPI(tableName, 'Add', rows, properties); + } } - Delete(tableName, rows, properties = {}) { - return this._appSheetAPI(tableName, 'Delete', rows, properties); + Delete(tableName, rows, properties = {}, paramOnly = false ) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Delete', rows, properties): + } else { + return this._appSheetAPI(tableName, 'Delete', rows, properties); + } } - Edit(tableName, rows, properties = {}) { - return this._appSheetAPI(tableName, 'Edit', rows, properties); + Edit(tableName, rows, properties = {}, paramOnly = false) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Edit', rows, properties): + } else { + return this._appSheetAPI(tableName, 'Edit', rows, properties); + } } - Find(tableName, rows, properties = {}) { - return this._appSheetAPI(tableName, 'Find', rows, properties); + Find(tableName, rows, properties = {}, paramOnly = false) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Find', rows, properties): + } else { + return this._appSheetAPI(tableName, 'Find', rows, properties); + } } - Action(tableName, action, rows, properties = {}) { - return this._appSheetAPI(tableName, action, rows, properties); + Action(tableName, action, rows, properties = {}, paramOnly = false) { + if(paramOnly) { + return this._appSheetParam(tableName,action, rows, properties): + } else { + return this._appSheetAPI(tableName, action, rows, properties); + } } /** @@ -140,7 +160,7 @@ class _AppSheet { * @param {Object} properties - Additional properties for the request. */ _appSheetAPI(tableName, action, rows, properties) { - const params = this._appSheetRequest(tableName, action, rows, properties); + const params = this._appSheetParam(tableName, action, rows, properties); const url = params.url; delete params.url; // url should not be a part of params @@ -157,18 +177,15 @@ class _AppSheet { * Make multiple requests to the AppSheet API. * @param {Array<{tableName: string, action: string, rows: Object[], properties: Object}>} params - An array of request parameters. */ - fetchAll(params){ - const requests = params.map(param => { - return this._appSheetRequest(param.tableName, param.action, param.rows, param.properties); - }); + fetchAll(...params){ try { - const responses = UrlFetchApp.fetchAll(requests); + const responses = UrlFetchApp.fetchAll(params); return responses.map(response => JSON.parse(response.getContentText())); } catch (e) { return e; } } - _appSheetRequest(tableName, action, rows = [], properties = {}) { + _appSheetParam(tableName, action, rows = [], properties = {}) { return { url: `https://api.appsheet.com/api/v2/apps/${this.appId}/tables/${tableName}/Action`, method: 'post', From 51927654ac508c7d9d3c21b7b00d3c77ab521e10 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Sat, 12 Jul 2025 15:32:36 +0000 Subject: [PATCH 04/12] Update file type --- AppSheetApp.js => AppSheetApp.gs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename AppSheetApp.js => AppSheetApp.gs (100%) diff --git a/AppSheetApp.js b/AppSheetApp.gs similarity index 100% rename from AppSheetApp.js rename to AppSheetApp.gs From 087a3f4d8e0bfc1d7295639e074de8486f52f69e Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Sun, 13 Jul 2025 00:47:33 +0800 Subject: [PATCH 05/12] Clean up --- AppSheetApp.gs | 396 ++++++++++++++++++++++++------------------------ appsscript.json | 14 +- 2 files changed, 204 insertions(+), 206 deletions(-) diff --git a/AppSheetApp.gs b/AppSheetApp.gs index aa101fc..b0d681f 100644 --- a/AppSheetApp.gs +++ b/AppSheetApp.gs @@ -1,199 +1,199 @@ -/** - * Copyright 2023 Cloud Technology Solutions Ltd. All Rights Reserved. - * 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. -**/ - -/** @interface **/ - -/** - * Connect to an AppSheet App - * To enable the AppSheet API in your app: - * - * 1. Open the app in the app editor. - * 2. Select **Settings > Integrations**. - * 3. Under **IN: from cloud services to your app**, enable the **Enable** toggle. - * This enables the API for the application as a whole. - * 4. Ensure that at least one unexpired **Application Access Key** is present. - * Otherwise, click **Create Application Access Key**. - * 5. When you are done, save the app. - * 6. Use your app ID and Access Key to connect Apps Script to your app - * - * @param {String} appId AppSheet App ID. - * @param {String} applicationAccessKey AppSheet App Access Key. - * @return {AppSheetApp} - */ -function (appId, applicationAccessKey) { - return new AppSheetApp(appId, applicationAccessKey); -} - -/** - * Add records to a table - * - * @param {String} tableName - specifies the name of the table - * @param {Object[]} rows - One or more Rows elements. Each individual Row value must normally include the key field values of the record to be added. However, if the key field contains an Initial value, you can omit the key field value. For example, you should omit the key field value when the key field has an Initial value of UNIQUEID() or RANDBETWEEN(). The system will initialize the key field to the Initial value. - * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] - * @returns {Object} AppSheet Response - */ -function Add(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Add', rows, properties); -} - -/** - * Delete records from a table - * - * @param {String} tableName - specifies the name of the table - * @param {Object[]} rows - One or more Rows elements to be deleted. Each Row value may contain field values of the key field values of the record to be deleted. - * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] - * @returns {Object} AppSheet Response - */ -function Delete(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Delete', rows, properties); -} - -/** - * Update records in a table - * - * @param {String} tableName - specifies the name of the table - * @param {Object[]} rows - One or more Row values to be updated. Each individual Row value must include the key field values of the record to be updated. - * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] - * @returns {Object} AppSheet Response - */ -function Edit(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Edit', rows, properties); -} - -/** - * Read records from a table - * - * @param {String} tableName - specifies the name of the table - * @param {Object[]} rows - **Optional**. You can omit the Selector property and specify input Rows containing the key values of the records to be read. - * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. - * @returns {Object} AppSheet Response - */ -function Find(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Find', rows, properties); -} - -/** - * Invoke an action - * - * @param {String} tableName - specifies the name of the table - * @param {String} action - The action name. - * @param {Object[]} rows - One or more Rows elements specifying the key field values of the rows to which the action is to be applied. - * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] - * @returns {Object} AppSheet Response - */ -function Action(tableName, action, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, action, rows, properties); -} - -/** - * AppSheetApp is used to interface the AppSheet API. - */ -class _AppSheet { - /** - * @constructor - * @param {String} appId - AppSheet Application ID. - * @param {String} applicationAccessKey - AppSheet Access Key. - * @return {_AppSheet} - */ - constructor(appId, applicationAccessKey) { - this.appId = appId; - this.applicationAccessKey = applicationAccessKey; - } - - Add(tableName, rows, properties = {}, paramOnly = false ) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Add', rows, properties): - } else { - return this._appSheetAPI(tableName, 'Add', rows, properties); - } - } - - Delete(tableName, rows, properties = {}, paramOnly = false ) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Delete', rows, properties): - } else { - return this._appSheetAPI(tableName, 'Delete', rows, properties); - } - } - - Edit(tableName, rows, properties = {}, paramOnly = false) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Edit', rows, properties): - } else { - return this._appSheetAPI(tableName, 'Edit', rows, properties); - } - } - - Find(tableName, rows, properties = {}, paramOnly = false) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Find', rows, properties): - } else { - return this._appSheetAPI(tableName, 'Find', rows, properties); - } - } - - Action(tableName, action, rows, properties = {}, paramOnly = false) { - if(paramOnly) { - return this._appSheetParam(tableName,action, rows, properties): - } else { - return this._appSheetAPI(tableName, action, rows, properties); - } - } - - /** - * Make a request to the AppSheet API. - * @param {String} tableName - The name of the table. - * @param {String} action - The action to perform. - * @param {Object[]} rows - The rows to operate on. - * @param {Object} properties - Additional properties for the request. - */ - _appSheetAPI(tableName, action, rows, properties) { - const params = this._appSheetParam(tableName, action, rows, properties); - const url = params.url; - delete params.url; // url should not be a part of params - - - try { - const response = UrlFetchApp.fetch(url, params); - return JSON.parse(response.getContentText()); - } catch (e) { - return e; - } - } - - /** - * Make multiple requests to the AppSheet API. - * @param {Array<{tableName: string, action: string, rows: Object[], properties: Object}>} params - An array of request parameters. - */ - fetchAll(...params){ - try { - const responses = UrlFetchApp.fetchAll(params); - return responses.map(response => JSON.parse(response.getContentText())); - } catch (e) { - return e; - } - } - _appSheetParam(tableName, action, rows = [], properties = {}) { - return { - url: `https://api.appsheet.com/api/v2/apps/${this.appId}/tables/${tableName}/Action`, - method: 'post', - contentType: 'application/json', - headers: { ApplicationAccessKey: this.applicationAccessKey }, - payload: JSON.stringify({ Action: action, Rows: rows, Properties: properties }), - muteHttpExceptions: true - }; - } -} +/** + * Copyright 2023 Cloud Technology Solutions Ltd. All Rights Reserved. + * 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. +**/ + +/** @interface **/ + +/** + * Connect to an AppSheet App + * To enable the AppSheet API in your app: + * + * 1. Open the app in the app editor. + * 2. Select **Settings > Integrations**. + * 3. Under **IN: from cloud services to your app**, enable the **Enable** toggle. + * This enables the API for the application as a whole. + * 4. Ensure that at least one unexpired **Application Access Key** is present. + * Otherwise, click **Create Application Access Key**. + * 5. When you are done, save the app. + * 6. Use your app ID and Access Key to connect Apps Script to your app + * + * @param {String} appId AppSheet App ID. + * @param {String} applicationAccessKey AppSheet App Access Key. + * @return {AppSheetApp} + */ +function connect(appId, applicationAccessKey) { + return new AppSheetApp(appId, applicationAccessKey); +} + +/** + * Add records to a table + * + * @param {String} tableName - specifies the name of the table + * @param {Object[]} rows - One or more Rows elements. Each individual Row value must normally include the key field values of the record to be added. However, if the key field contains an Initial value, you can omit the key field value. For example, you should omit the key field value when the key field has an Initial value of UNIQUEID() or RANDBETWEEN(). The system will initialize the key field to the Initial value. + * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @returns {Object} AppSheet Response + */ +function Add(tableName, rows, properties = {}) { + return AppSheetApp._appSheetAPI(tableName, 'Add', rows, properties); +} + +/** + * Delete records from a table + * + * @param {String} tableName - specifies the name of the table + * @param {Object[]} rows - One or more Rows elements to be deleted. Each Row value may contain field values of the key field values of the record to be deleted. + * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @returns {Object} AppSheet Response + */ +function Delete(tableName, rows, properties = {}) { + return AppSheetApp._appSheetAPI(tableName, 'Delete', rows, properties); +} + +/** + * Update records in a table + * + * @param {String} tableName - specifies the name of the table + * @param {Object[]} rows - One or more Row values to be updated. Each individual Row value must include the key field values of the record to be updated. + * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @returns {Object} AppSheet Response + */ +function Edit(tableName, rows, properties = {}) { + return AppSheetApp._appSheetAPI(tableName, 'Edit', rows, properties); +} + +/** + * Read records from a table + * + * @param {String} tableName - specifies the name of the table + * @param {Object[]} rows - **Optional**. You can omit the Selector property and specify input Rows containing the key values of the records to be read. + * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. + * @returns {Object} AppSheet Response + */ +function Find(tableName, rows, properties = {}) { + return AppSheetApp._appSheetAPI(tableName, 'Find', rows, properties); +} + +/** + * Invoke an action + * + * @param {String} tableName - specifies the name of the table + * @param {String} action - The action name. + * @param {Object[]} rows - One or more Rows elements specifying the key field values of the rows to which the action is to be applied. + * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @returns {Object} AppSheet Response + */ +function Action(tableName, action, rows, properties = {}) { + return AppSheetApp._appSheetAPI(tableName, action, rows, properties); +} + +/** + * AppSheetApp is used to interface the AppSheet API. + */ +class _AppSheet { + /** + * @constructor + * @param {String} appId - AppSheet Application ID. + * @param {String} applicationAccessKey - AppSheet Access Key. + * @return {_AppSheet} + */ + constructor(appId, applicationAccessKey) { + this.appId = appId; + this.applicationAccessKey = applicationAccessKey; + } + + Add(tableName, rows, properties = {}, paramOnly = false ) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Add', rows, properties) + } else { + return this._appSheetAPI(tableName, 'Add', rows, properties); + } + } + + Delete(tableName, rows, properties = {}, paramOnly = false ) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Delete', rows, properties) + } else { + return this._appSheetAPI(tableName, 'Delete', rows, properties); + } + } + + Edit(tableName, rows, properties = {}, paramOnly = false) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Edit', rows, properties) + } else { + return this._appSheetAPI(tableName, 'Edit', rows, properties); + } + } + + Find(tableName, rows, properties = {}, paramOnly = false) { + if(paramOnly) { + return this._appSheetParam(tableName, 'Find', rows, properties) + } else { + return this._appSheetAPI(tableName, 'Find', rows, properties); + } + } + + Action(tableName, action, rows, properties = {}, paramOnly = false) { + if(paramOnly) { + return this._appSheetParam(tableName,action, rows, properties) + } else { + return this._appSheetAPI(tableName, action, rows, properties); + } + } + + /** + * Make a request to the AppSheet API. + * @param {String} tableName - The name of the table. + * @param {String} action - The action to perform. + * @param {Object[]} rows - The rows to operate on. + * @param {Object} properties - Additional properties for the request. + */ + _appSheetAPI(tableName, action, rows, properties) { + const params = this._appSheetParam(tableName, action, rows, properties); + const url = params.url; + delete params.url; // url should not be a part of params + + + try { + const response = UrlFetchApp.fetch(url, params); + return JSON.parse(response.getContentText()); + } catch (e) { + return e; + } + } + + /** + * Make multiple requests to the AppSheet API. + * @param {Array<{tableName: string, action: string, rows: Object[], properties: Object}>} params - An array of request parameters. + */ + fetchAll(...params){ + try { + const responses = UrlFetchApp.fetchAll(params); + return responses.map(response => JSON.parse(response.getContentText())); + } catch (e) { + return e; + } + } + _appSheetParam(tableName, action, rows = [], properties = {}) { + return { + url: `https://api.appsheet.com/api/v2/apps/${this.appId}/tables/${tableName}/Action`, + method: 'post', + contentType: 'application/json', + headers: { ApplicationAccessKey: this.applicationAccessKey }, + payload: JSON.stringify({ Action: action, Rows: rows, Properties: properties }), + muteHttpExceptions: true + }; + } +} var AppSheetApp = _AppSheet; \ No newline at end of file diff --git a/appsscript.json b/appsscript.json index 9704d41..7398e43 100644 --- a/appsscript.json +++ b/appsscript.json @@ -1,9 +1,7 @@ -{ - "timeZone": "Europe/London", - "dependencies": {}, - "oauthScopes": [ - "https://www.googleapis.com/auth/script.external_request" - ], - "exceptionLogging": "STACKDRIVER", - "runtimeVersion": "V8" +{ + "timeZone": "Asia/Singapore", + "dependencies": { + }, + "exceptionLogging": "STACKDRIVER", + "runtimeVersion": "V8" } \ No newline at end of file From f75f9db95e2cdff8e616c7bc7ae4ea658eb9f450 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Sun, 13 Jul 2025 00:51:54 +0800 Subject: [PATCH 06/12] Update README.md Update library ID --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e4367d..3f17b16 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This project is already published as an Apps Script library, making it easy to i 1. In the **Libraries** section click on the **Add a library** button (+) 2. In the **Script ID** text box, enter - `19UWd_F9ht9KuE4gxeNdFG8qIMdBeTu5gXyecmPqr8yOEoVO8UcxBYVsJ` and click the **Lookup** button. + `AKfycbzMHTi6x3Kx0zeD_01AIYBYnUAVMRbiWG8RZmgIPsb44ZHxtsWH5jNoSwljEAmx5MAD` and click the **Lookup** button. 3. Choose a version in the dropdown box (usually best to pick the latest version). 4. Click the **Add** button. From 7f29b1cfd216df2137025fe7d5c29940f1569176 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Fri, 18 Jul 2025 21:04:31 +0800 Subject: [PATCH 07/12] Update README.md --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f17b16..dce72f5 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This project is already published as an Apps Script library, making it easy to i 1. In the **Libraries** section click on the **Add a library** button (+) 2. In the **Script ID** text box, enter - `AKfycbzMHTi6x3Kx0zeD_01AIYBYnUAVMRbiWG8RZmgIPsb44ZHxtsWH5jNoSwljEAmx5MAD` and click the **Lookup** button. + `1aXPRqSO_ulCdptqlpKm12o81pdOHUlxnr9n6Gw3AIXk0K8Xc_cNCbx4B` and click the **Lookup** button. 3. Choose a version in the dropdown box (usually best to pick the latest version). 4. Click the **Add** button. @@ -115,6 +115,7 @@ For more detailed information on the data about the actions, properties, rows an | [`Edit(tableName, rows, properties = {})`](#Edit) | Update records in a table. | | [`Find(tableName, rows, properties = {})`](#Find) | Read records from a table. | | [`Action(tableName, action, rows, properties = {})`](#Action) | Invoke an action. | +| [`FetchAll(request[])`](#FetchAll) | Process requests in parallel. | @@ -233,4 +234,17 @@ Invoke an action | `action` | String | The action name. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | + + + +## Action(tableName, action, rows, properties) ⇒ Object +Invoke an action + +| Param | Type | Description | +| --- | --- | --- | +| `tableName` | String | specifies the name of the table | +| `rows` | Array.<Object> | One or more Rows elements specifying the key field values of the rows to which the action is to be applied. | +| `action` | String | The action name. | +| `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | + **Returns**: Object - AppSheet Response From 29d76b843c92031e1891c4af41794090858f3219 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Sun, 17 Aug 2025 14:32:44 +0800 Subject: [PATCH 08/12] Update README.md Updated docs for FetchAll --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index dce72f5..0758a36 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,14 @@ The AppSheetApp service lets you access the AppSheet API using Apps Script. The - Read a table record - Update table records - Invoke an action you have defined in AppSheet (limited to certain action types) +- FetchAll methods in parallel > **Note:** The AppSheet API is supported for Enterprise plans only. `AppSheetApp` has been created by Martin Hawksey (https://g.dev/mhawksey), Collaboration Engineer at [CTS](https://cts.co/). +`FetchAll` has been added by Bien Lim, AppSheet enthusiast. + ## Enabling the AppSheet API To use the AppSheetApp service you need to generate an Application Access Key for your AppSheet app. To do this read the reference documentation on [enabling the API](https://support.google.com/appsheet/answer/10105769). @@ -110,12 +113,12 @@ For more detailed information on the data about the actions, properties, rows an | Method | Description | | :--------------------------------------------------------- | :------------------------------------- | | [`connect(appId, applicationAccessKey)`](#connect) | Connect to an AppSheet App. | -| [`Add(tableName, rows, properties = {})`](#Add) | Add records to a table. | -| [`Delete(tableName, rows, properties = {})`](#Delete) | Delete records from a table. | -| [`Edit(tableName, rows, properties = {})`](#Edit) | Update records in a table. | -| [`Find(tableName, rows, properties = {})`](#Find) | Read records from a table. | -| [`Action(tableName, action, rows, properties = {})`](#Action) | Invoke an action. | -| [`FetchAll(request[])`](#FetchAll) | Process requests in parallel. | +| [`Add(tableName, rows, properties = {}, isAsync = false)`](#Add) | Add records to a table. | +| [`Delete(tableName, rows, properties = {}, isAsync = false)`](#Delete) | Delete records from a table. | +| [`Edit(tableName, rows, properties = {}, isAsync = false)`](#Edit) | Update records in a table. | +| [`Find(tableName, rows, properties = {}, isAsync = false)`](#Find) | Read records from a table. | +| [`Action(tableName, action, rows, properties = {}, isAsync = false)`](#Action) | Invoke an action. | +| [`FetchAll(...request)`](#FetchAll) | Run multiple method requests in parallel. | @@ -143,7 +146,7 @@ const AppSheet = AppSheetApp.connect('YOUR_APP_ID', 'YOUR_ACCESS_KEY'); -## Add(tableName, rows, properties) ⇒ Object +## Add(tableName, rows, properties, isAsync) ⇒ Object Add records to a table | Param | Type | Description | @@ -151,11 +154,13 @@ Add records to a table | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | One or more Rows elements. Each individual Row value must normally include the key field values of the record to be added. However, if the key field contains an Initial value, you can omit the key field value. For example, you should omit the key field value when the key field has an Initial value of UNIQUEID() or RANDBETWEEN(). The system will initialize the key field to the Initial value. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. + **Returns**: Object - AppSheet Response -## Delete(tableName, rows, properties) ⇒ Object +## Delete(tableName, rows, properties, isAsync) ⇒ Object Delete records from a table | Param | Type | Description | @@ -163,12 +168,14 @@ Delete records from a table | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | One or more Rows elements to be deleted. Each Row value may contain field values of the key field values of the record to be deleted. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. + **Returns**: Object - AppSheet Response -## Edit(tableName, rows, properties) ⇒ Object +## Edit(tableName, rows, properties, isAsync) ⇒ Object Update records in a table @@ -176,7 +183,9 @@ Update records in a table | --- | --- | --- | | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | One or more Row values to be updated. Each individual Row value must include the key field values of the record to be updated. | -| `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +| `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398? +| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. +hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | **Returns**: Object - AppSheet Response @@ -219,6 +228,7 @@ function findRowsInTable(){ | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | **Optional**. You can omit the Selector property and specify input Rows containing the key values of the records to be read. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. | +| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. **Returns**: Object - AppSheet Response @@ -234,17 +244,39 @@ Invoke an action | `action` | String | The action name. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +**Returns**: Object - AppSheet Response -## Action(tableName, action, rows, properties) ⇒ Object -Invoke an action +## FetchAll(...request) +Run multiple method request in parallel. | Param | Type | Description | | --- | --- | --- | -| `tableName` | String | specifies the name of the table | -| `rows` | Array.<Object> | One or more Rows elements specifying the key field values of the rows to which the action is to be applied. | -| `action` | String | The action name. | -| `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +| `...request` | ...<Objects> | One or more Appsheet Methods with isAsync is true | + +``` +/** + * Return rows from a People table where age is greater or equal to 21 + * Run as user with the email an.example@email.com + */ +function parallelRequest(){ + const AppSheet = AppSheetApp.connect('YOUR_APP_ID', 'YOUR_ACCESS_KEY'); + + const responses = AppSheet.FetchAll( + + AppSheet.Add('People', sampleData1[] , properties, true), + AppSheet.Delete('People', sampleData2[] , properties, true), + AppSheet.Edit('People', sampleData3[] , properties, true), + AppSheet.Find('People', [], properties, true), + } + + const [ respFromAdd, respFromDelete, respFromEdit, respFromFind ] = responses +} +``` + + + + + -**Returns**: Object - AppSheet Response From 4eb1d0230cb33ed7d90b7a4964f23342afbd1715 Mon Sep 17 00:00:00 2001 From: Martin Hawksey Date: Mon, 18 Aug 2025 21:44:39 +0100 Subject: [PATCH 09/12] Update README.md Extended the fetchAll example --- README.md | 59 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 0758a36..87756cf 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Before you can start making calls to your AppSheet app you need to use the `conn Once you have connected to your app, you can use methods to add, delete, read, and update table records. The example below shows how to connect to your app and add two rows to a 'People' table: -``` +```javascript /** * Example function for connecting your AppSheet app */ @@ -79,7 +79,7 @@ function addRowsToTable() { The returned records include all field values. This includes virtual fields and field values computed by worksheet formulas. The following is an example of a response body that might be returned from an `Add` operation (when new records are created) or a `Find` operation (when records are retrieved). -``` +```javascript { "Rows": [ { @@ -135,7 +135,7 @@ To enable the AppSheet API in your app: 1. Use your app ID and Access Key to connect Apps Script to your app -``` +```javascript const AppSheet = AppSheetApp.connect('YOUR_APP_ID', 'YOUR_ACCESS_KEY'); ``` @@ -205,7 +205,7 @@ In the `Selector` property, you can specify an expression to select and format t The `Find` is performed under the identity of the application owner by default. Your can override this by specifying the `RunAsUserEmail` property in the request properties. -``` +```javascript /** * Return rows from a People table where age is greater or equal to 21 * Run as user with the email an.example@email.com @@ -255,24 +255,47 @@ Run multiple method request in parallel. | --- | --- | --- | | `...request` | ...<Objects> | One or more Appsheet Methods with isAsync is true | -``` +```javascript /** - * Return rows from a People table where age is greater or equal to 21 - * Run as user with the email an.example@email.com + * Executes multiple AppSheet API requests in parallel. + * This example shows how to perform Add, Delete, Edit, and Find operations simultaneously. */ function parallelRequest(){ - const AppSheet = AppSheetApp.connect('YOUR_APP_ID', 'YOUR_ACCESS_KEY'); - - const responses = AppSheet.FetchAll( - - AppSheet.Add('People', sampleData1[] , properties, true), - AppSheet.Delete('People', sampleData2[] , properties, true), - AppSheet.Edit('People', sampleData3[] , properties, true), - AppSheet.Find('People', [], properties, true), - } - - const [ respFromAdd, respFromDelete, respFromEdit, respFromFind ] = responses + // Replace with your actual App ID and Access Key + const AppSheet = new AppSheetApp('YOUR_APP_ID', 'YOUR_ACCESS_KEY'); + + // Placeholder data for demonstration + const properties = { "Locale": "en-US" }; + + // Sample data for adding a new record. The key field is usually omitted if it's auto-generated. + const dataToAdd = [{"Name": "John Doe", "Age": 30}]; + + // Sample data for editing an existing record. The key field is required to identify the row. + const dataToEdit = [{"ID": "unique-id-123", "Age": 31}]; + + // Sample data for deleting an existing record. The key field is required. + const dataToDelete = [{"ID": "unique-id-456"}]; + + // The FetchAll method takes multiple API calls as arguments. + // The 'true' argument tells each method to return a parameter object instead of + // making an immediate API call. These parameter objects are then passed to fetchAll(). + const responses = AppSheet.fetchAll( + AppSheet.Add('People', dataToAdd, properties, true), + AppSheet.Delete('People', dataToDelete, properties, true), + AppSheet.Edit('People', dataToEdit, properties, true), + AppSheet.Find('People', [], properties, true) + ); + + // The responses are returned in an array, in the same order as the requests. + const [ respFromAdd, respFromDelete, respFromEdit, respFromFind ] = responses; + + // You can now handle each response individually + console.log('Add Response:', respFromAdd); + console.log('Delete Response:', respFromDelete); + console.log('Edit Response:', respFromEdit); + console.log('Find Response:', respFromFind); } + ``` From 297349fcf3edccb432a8acb6e594db9d62c012af Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:06:41 +0800 Subject: [PATCH 10/12] Update README.md docs: typo --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0758a36..ea9cc97 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ The AppSheetApp service lets you access the AppSheet API using Apps Script. The - Read a table record - Update table records - Invoke an action you have defined in AppSheet (limited to certain action types) -- FetchAll methods in parallel +- fetchAll methods in parallel > **Note:** The AppSheet API is supported for Enterprise plans only. `AppSheetApp` has been created by Martin Hawksey (https://g.dev/mhawksey), Collaboration Engineer at [CTS](https://cts.co/). -`FetchAll` has been added by Bien Lim, AppSheet enthusiast. +`fetchAll` has been added by Bien Lim, AppSheet enthusiast. ## Enabling the AppSheet API @@ -118,7 +118,7 @@ For more detailed information on the data about the actions, properties, rows an | [`Edit(tableName, rows, properties = {}, isAsync = false)`](#Edit) | Update records in a table. | | [`Find(tableName, rows, properties = {}, isAsync = false)`](#Find) | Read records from a table. | | [`Action(tableName, action, rows, properties = {}, isAsync = false)`](#Action) | Invoke an action. | -| [`FetchAll(...request)`](#FetchAll) | Run multiple method requests in parallel. | +| [`fetchAll(...request)`](#fetchAll) | Run multiple method requests in parallel. | @@ -246,9 +246,9 @@ Invoke an action **Returns**: Object - AppSheet Response - + -## FetchAll(...request) +## fetchAll(...request) Run multiple method request in parallel. | Param | Type | Description | @@ -263,7 +263,7 @@ Run multiple method request in parallel. function parallelRequest(){ const AppSheet = AppSheetApp.connect('YOUR_APP_ID', 'YOUR_ACCESS_KEY'); - const responses = AppSheet.FetchAll( + const responses = AppSheet.fetchAll( AppSheet.Add('People', sampleData1[] , properties, true), AppSheet.Delete('People', sampleData2[] , properties, true), From 601657832a8b960fb182eb3daf3e6d2309c081a1 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:59:53 +0000 Subject: [PATCH 11/12] Fix naming consistency and js docs --- AppSheetApp.gs | 148 ++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 64 deletions(-) diff --git a/AppSheetApp.gs b/AppSheetApp.gs index b0d681f..08bdcc2 100644 --- a/AppSheetApp.gs +++ b/AppSheetApp.gs @@ -42,10 +42,13 @@ function connect(appId, applicationAccessKey) { * @param {String} tableName - specifies the name of the table * @param {Object[]} rows - One or more Rows elements. Each individual Row value must normally include the key field values of the record to be added. However, if the key field contains an Initial value, you can omit the key field value. For example, you should omit the key field value when the key field has an Initial value of UNIQUEID() or RANDBETWEEN(). The system will initialize the key field to the Initial value. * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] - * @returns {Object} AppSheet Response + * @param {Boolean} isAsync - **Optional** return a AppSheet API request object instead of making an immediate API call + * @returns {Object} AppSheet Response */ -function Add(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Add', rows, properties); +function Add(tableName, rows, properties = {}, isAsync = false) { + return isAsync + ? AppSheetApp._appSheetParam(tableName, "Add", rows, properties) + : AppSheetApp._appSheetAPI(tableName, "Add", rows, properties); } /** @@ -54,10 +57,13 @@ function Add(tableName, rows, properties = {}) { * @param {String} tableName - specifies the name of the table * @param {Object[]} rows - One or more Rows elements to be deleted. Each Row value may contain field values of the key field values of the record to be deleted. * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @param {Boolean} isAsync - **Optional** return a AppSheet API request object instead of making an immediate API call * @returns {Object} AppSheet Response */ -function Delete(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Delete', rows, properties); +function Delete(tableName, rows, properties = {}, isAsync = false) { + return isAsync + ? AppSheetApp._appSheetParam(tableName, "Delete", rows, properties) + : AppSheetApp._appSheetAPI(tableName, "Delete", rows, properties); } /** @@ -66,10 +72,13 @@ function Delete(tableName, rows, properties = {}) { * @param {String} tableName - specifies the name of the table * @param {Object[]} rows - One or more Row values to be updated. Each individual Row value must include the key field values of the record to be updated. * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @param {Boolean} isAsync - **Optional** if true, return a AppSheet API request object instead of making an immediate API call * @returns {Object} AppSheet Response */ -function Edit(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Edit', rows, properties); +function Edit(tableName, rows, properties = {}, isAsync = false) { + return isAsync + ? AppSheetApp._appSheetParam(tableName, "Edit", rows, properties) + : AppSheetApp._appSheetAPI(tableName, "Edit", rows, properties); } /** @@ -77,11 +86,14 @@ function Edit(tableName, rows, properties = {}) { * * @param {String} tableName - specifies the name of the table * @param {Object[]} rows - **Optional**. You can omit the Selector property and specify input Rows containing the key values of the records to be read. - * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. + * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. + * @param {Boolean} isAsync - **Optional** if true, return a AppSheet API request object instead of making an immediate API call * @returns {Object} AppSheet Response */ -function Find(tableName, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, 'Find', rows, properties); +function Find(tableName, rows, properties = {}, isAsync = false) { + return isAsync + ? AppSheetApp._appSheetParam(tableName, "Find", rows, properties) + : AppSheetApp._appSheetAPI(tableName, "Find", rows, properties); } /** @@ -91,10 +103,35 @@ function Find(tableName, rows, properties = {}) { * @param {String} action - The action name. * @param {Object[]} rows - One or more Rows elements specifying the key field values of the rows to which the action is to be applied. * @param {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @param {Boolean} isAsync - **Optional** if true, return a AppSheet API request object instead of making an immediate API call * @returns {Object} AppSheet Response */ -function Action(tableName, action, rows, properties = {}) { - return AppSheetApp._appSheetAPI(tableName, action, rows, properties); +function Action(tableName, action, rows, properties = {}, isAsync = false) { + return isAsync + ? AppSheetApp._appSheetParam(tableName, action , rows, properties) + : AppSheetApp._appSheetAPI(tableName, action , rows, properties); +} + +/** + * @typedef {object} requestObject + * @property {String} action - The action name. + * @property {Object[]} rows - One or more Rows elements specifying the key field values of the rows to which the action is to be applied. + * @property {Object} properties - **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] + * @property {Boolean} isAsync - **Optional** if true, return a AppSheet API request object instead of making an immediate API call + */ +/** + * Makes multiple requests to the AppSheet API. + * + * @param {...requestObject} params - A variable number of objects, where each object represents a single AppSheet API request. + * @returns {Object[]} An array of AppSheet API responses, with each response corresponding to a request in the order they were provided. + */ +function fetchAll(...params){ + try { + const responses = UrlFetchApp.fetchAll(params); + return responses.map(response => JSON.parse(response.getContentText())); + } catch (e) { + return e; + } } /** @@ -112,79 +149,62 @@ class _AppSheet { this.applicationAccessKey = applicationAccessKey; } - Add(tableName, rows, properties = {}, paramOnly = false ) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Add', rows, properties) - } else { - return this._appSheetAPI(tableName, 'Add', rows, properties); - } + Add(tableName, rows, properties = {}, isAsync = false ) { + return isAsync + ? this._appSheetParam(tableName, 'Add', rows, properties) + : this._appSheetAPI(tableName, 'Add', rows, properties) } - Delete(tableName, rows, properties = {}, paramOnly = false ) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Delete', rows, properties) - } else { - return this._appSheetAPI(tableName, 'Delete', rows, properties); - } + Delete(tableName, rows, properties = {}, isAsync = false ) { + return isAsync + ? this._appSheetParam(tableName, 'Delete', rows, properties) + : this._appSheetAPI(tableName, 'Delete', rows, properties); } - Edit(tableName, rows, properties = {}, paramOnly = false) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Edit', rows, properties) - } else { - return this._appSheetAPI(tableName, 'Edit', rows, properties); - } + Edit(tableName, rows, properties = {}, isAsync = false) { + return isAsync + ? this._appSheetParam(tableName, 'Edit', rows, properties) + : this._appSheetAPI(tableName, 'Edit', rows, properties); } - Find(tableName, rows, properties = {}, paramOnly = false) { - if(paramOnly) { - return this._appSheetParam(tableName, 'Find', rows, properties) - } else { - return this._appSheetAPI(tableName, 'Find', rows, properties); - } + Find(tableName, rows, properties = {}, isAsync = false) { + return isAsync + ? this._appSheetParam(tableName, 'Find', rows, properties) + : this._appSheetAPI(tableName, 'Find', rows, properties); } - Action(tableName, action, rows, properties = {}, paramOnly = false) { - if(paramOnly) { - return this._appSheetParam(tableName,action, rows, properties) - } else { - return this._appSheetAPI(tableName, action, rows, properties); - } + Action(tableName, action, rows, properties = {}, isAsync = false) { + return isAsync + ? this._appSheetParam(tableName,action, rows, properties) + : this._appSheetAPI(tableName, action, rows, properties); + } + + fetchAll(...params){ + try { + const responses = UrlFetchApp.fetchAll(params); + return responses.map(response => JSON.parse(response.getContentText())); + } catch (e) { + return e; + } } - /** - * Make a request to the AppSheet API. - * @param {String} tableName - The name of the table. - * @param {String} action - The action to perform. - * @param {Object[]} rows - The rows to operate on. - * @param {Object} properties - Additional properties for the request. - */ _appSheetAPI(tableName, action, rows, properties) { const params = this._appSheetParam(tableName, action, rows, properties); const url = params.url; - delete params.url; // url should not be a part of params - + + // Remove the url from params to avoid sending it in the request + // This is necessary because UrlFetchApp does not support sending the url in the params object + // and it expects the url to be a separate parameter. + delete params.url; try { const response = UrlFetchApp.fetch(url, params); return JSON.parse(response.getContentText()); } catch (e) { return e; - } + } } - /** - * Make multiple requests to the AppSheet API. - * @param {Array<{tableName: string, action: string, rows: Object[], properties: Object}>} params - An array of request parameters. - */ - fetchAll(...params){ - try { - const responses = UrlFetchApp.fetchAll(params); - return responses.map(response => JSON.parse(response.getContentText())); - } catch (e) { - return e; - } - } _appSheetParam(tableName, action, rows = [], properties = {}) { return { url: `https://api.appsheet.com/api/v2/apps/${this.appId}/tables/${tableName}/Action`, From 3977a4eb9317b36be229839e78432957e1eb6518 Mon Sep 17 00:00:00 2001 From: Bien <104995272+bienlim@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:13:16 +0000 Subject: [PATCH 12/12] Update README.md --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0fc1647..64421d9 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ Add records to a table | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | One or more Rows elements. Each individual Row value must normally include the key field values of the record to be added. However, if the key field contains an Initial value, you can omit the key field value. For example, you should omit the key field value when the key field has an Initial value of UNIQUEID() or RANDBETWEEN(). The system will initialize the key field to the Initial value. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | -| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. +| `isAsync` | Bool | **Optional** if true, return a AppSheet API request object instead of making an immediate API call | **Returns**: Object - AppSheet Response @@ -168,7 +168,7 @@ Delete records from a table | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | One or more Rows elements to be deleted. Each Row value may contain field values of the key field values of the record to be deleted. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | -| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. +| `isAsync` | Bool | **Optional** if true, return a AppSheet API request object instead of making an immediate API call | **Returns**: Object - AppSheet Response @@ -183,9 +183,9 @@ Update records in a table | --- | --- | --- | | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | One or more Row values to be updated. Each individual Row value must include the key field values of the record to be updated. | -| `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398? -| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. -hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +| `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +| `isAsync` | Bool | **Optional** if true, return a AppSheet API request object instead of making an immediate API call | + **Returns**: Object - AppSheet Response @@ -228,7 +228,7 @@ function findRowsInTable(){ | `tableName` | String | specifies the name of the table | | `rows` | Array.<Object> | **Optional**. You can omit the Selector property and specify input Rows containing the key values of the records to be read. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)]. Additionally the optional `Selector` property can used to specify an expression to select and format the rows returned [[Ref](https://support.google.com/appsheet/answer/10105770#:~:text=Read-,selected%20rows,-In%20the%20Selector)]. | -| `isAsync` | Bool | **Optional**. Default values is false. Only set as `TRUE` if called within `FetchAll`. +| `isAsync` | Bool | **Optional** if true, return a AppSheet API request object instead of making an immediate API call | **Returns**: Object - AppSheet Response @@ -243,6 +243,7 @@ Invoke an action | `rows` | Array.<Object> | One or more Rows elements specifying the key field values of the rows to which the action is to be applied. | | `action` | String | The action name. | | `properties` | Object | **Optional**. Optional properties such as Locale, Location, Timezone, and UserId. [[Ref](https://support.google.com/appsheet/answer/10105398?hl=en#:~:text=for%20the%20table.-,Properties,-The%20properties%20of)] | +| `isAsync` | Bool | **Optional** if true, return a AppSheet API request object instead of making an immediate API call | **Returns**: Object - AppSheet Response @@ -253,7 +254,7 @@ Run multiple method request in parallel. | Param | Type | Description | | --- | --- | --- | -| `...request` | ...<Objects> | One or more Appsheet Methods with isAsync is true | +| `...request` | ...<Objects> | One or more AppSheet API request object | ```javascript /** @@ -276,7 +277,7 @@ function parallelRequest(){ // Sample data for deleting an existing record. The key field is required. const dataToDelete = [{"ID": "unique-id-456"}]; - // The FetchAll method takes multiple API calls as arguments. + // The fetchAll method takes multiple API calls as arguments. // The 'true' argument tells each method to return a parameter object instead of // making an immediate API call. These parameter objects are then passed to fetchAll(). const responses = AppSheet.fetchAll(