├── appsscript.json ├── .codeclimate.yml ├── LICENCE ├── BatchRequests.js └── README.md /appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Asia/Tokyo", 3 | "dependencies": {}, 4 | "exceptionLogging": "STACKDRIVER", 5 | "oauthScopes": [ 6 | "https://www.googleapis.com/auth/drive", 7 | "https://www.googleapis.com/auth/drive.scripts", 8 | "https://www.googleapis.com/auth/script.external_request", 9 | "https://mail.google.com/", 10 | "https://www.googleapis.com/auth/calendar" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - ruby 8 | - javascript 9 | - python 10 | - php 11 | eslint: 12 | enabled: true 13 | fixme: 14 | enabled: true 15 | ratings: 16 | paths: 17 | - "**.inc" 18 | - "**.js" 19 | - "**.jsx" 20 | - "**.module" 21 | - "**.php" 22 | - "**.py" 23 | - "**.rb" 24 | exclude_paths: 25 | - "tests/" 26 | - "spec/" 27 | - "**/vendor/" 28 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 Kanshi TANAIKE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /BatchRequests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GitHub https://github.com/tanaikech/BatchRequest
3 | * Run BatchRequest
4 | * @param {Object} Object Object 5 | * @return {Object} Return Object 6 | */ 7 | function Do(object) { 8 | return new BatchRequest(object).Do(); 9 | } 10 | 11 | /** 12 | * Run enhanced "Do" method of BatchRequest. Requests more than 100 can be used and the result values are parsed.
13 | * @param {Object} Object Object 14 | * @return {Object} Return Object 15 | */ 16 | function EDo(object) { 17 | return new BatchRequest(object).EDo(); 18 | } 19 | 20 | /** 21 | * Get batch path for using batch requests. On August 12, 2020, in order to use batch requests, the batch path is required to be used to the endpoint of the batch requests..
22 | * @param {string} name Name of Google API you want to use. For example, when you want to use Drive API, please put "drive". 23 | * @param {string} version Version of Google API you want to use. For example, when you want to use Drive API v2, please put "v2". When this is not used, the latest version is used as the default. 24 | * @return {Object} Return Object 25 | */ 26 | function getBatchPath(name, version) { 27 | return new BatchRequest("getBatchPath").getBatchPath(name, version); 28 | } 29 | ; 30 | (function(r) { 31 | var BatchRequest; 32 | BatchRequest = (function() { 33 | var createRequest, parser, parserAsBinary, splitByteArrayBySearchData; 34 | 35 | BatchRequest.name = "BatchRequest"; 36 | 37 | function BatchRequest(p_) { 38 | var bP, batchPath; 39 | if (typeof p_ === "object") { 40 | if (!p_.hasOwnProperty("requests")) { 41 | throw new Error("'requests' property was not found in object."); 42 | } 43 | this.p = p_.requests.slice(); 44 | this.url = "https://www.googleapis.com/batch"; 45 | if (p_.batchPath) { 46 | bP = p_.batchPath.trim(); 47 | batchPath = ""; 48 | if (~bP.indexOf("batch/")) { 49 | batchPath = bP.replace("batch", ""); 50 | } else { 51 | batchPath = bP.slice(0, 1) === "/" ? bP : "/" + bP; 52 | } 53 | this.url += batchPath; 54 | } 55 | this.at = p_.accessToken || ScriptApp.getOAuthToken(); 56 | this.lb = "\r\n"; 57 | this.boundary = "xxxxxxxxxx"; 58 | this.useFetchAll = "useFetchAll" in p_ ? p_.useFetchAll : false; 59 | this.exportDataAsBlob = "exportDataAsBlob" in p_ ? p_.exportDataAsBlob : false; 60 | } 61 | } 62 | 63 | BatchRequest.prototype.Do = function() { 64 | var e, params, res; 65 | try { 66 | params = createRequest.call(this, this.p); 67 | res = UrlFetchApp.fetch(this.url, params); 68 | } catch (error) { 69 | e = error; 70 | throw new Error(e); 71 | } 72 | return res; 73 | }; 74 | 75 | BatchRequest.prototype.EDo = function() { 76 | var e, i, k, l, limit, obj, params, ref, ref1, reqs, res, split; 77 | try { 78 | if (this.useFetchAll) { 79 | limit = 100; 80 | split = Math.ceil(this.p.length / limit); 81 | reqs = []; 82 | for (i = k = 0, ref = split; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) { 83 | params = createRequest.call(this, this.p.splice(0, limit)); 84 | params.url = this.url; 85 | reqs.push(params); 86 | } 87 | r = UrlFetchApp.fetchAll(reqs); 88 | res = r.reduce(function(ar, e) { 89 | var obj; 90 | if (e.getResponseCode() !== 200) { 91 | ar.push(e.getContentText()); 92 | } else { 93 | obj = this.exportDataAsBlob ? parserAsBinary.call(this, e) : parser.call(this, e.getContentText()); 94 | ar = ar.concat(obj); 95 | } 96 | return ar; 97 | }, []); 98 | } else { 99 | limit = 100; 100 | split = Math.ceil(this.p.length / limit); 101 | res = []; 102 | for (i = l = 0, ref1 = split; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) { 103 | params = createRequest.call(this, this.p.splice(0, limit)); 104 | r = UrlFetchApp.fetch(this.url, params); 105 | if (r.getResponseCode() !== 200) { 106 | res.push(r.getContentText()); 107 | } else { 108 | obj = this.exportDataAsBlob ? parserAsBinary.call(this, r) : parser.call(this, r.getContentText()); 109 | res = res.concat(obj); 110 | } 111 | } 112 | } 113 | } catch (error) { 114 | e = error; 115 | throw new Error(e); 116 | } 117 | return res; 118 | }; 119 | 120 | BatchRequest.prototype.getBatchPath = function(name, version) { 121 | var batchPath, discoveryRestUrl, obj1, obj2, res1, res2, url; 122 | version = version === void 0 ? "" : version; 123 | if (!name) { 124 | throw new Error("Please set API name you want to search."); 125 | } 126 | url = "https://www.googleapis.com/discovery/v1/apis?preferred=" + (version ? "false" : "true") + "&name=" + encodeURIComponent(name.toLowerCase()); 127 | res1 = UrlFetchApp.fetch(url, { 128 | muteHttpExceptions: true 129 | }); 130 | if (res1.getResponseCode() !== 200) { 131 | throw new Error("Batch path cannot be found."); 132 | } 133 | obj1 = JSON.parse(res1.getContentText()); 134 | if (!obj1.items) { 135 | throw new Error("Batch path cannot be found."); 136 | } 137 | discoveryRestUrl = ((version.toString() === "" ? obj1.items[0] : (obj1.items.filter(function(e) { 138 | return e.version === version; 139 | }))[0]) || {}).discoveryRestUrl; 140 | if (!discoveryRestUrl) { 141 | throw new Error("Batch path cannot be found."); 142 | } 143 | res2 = UrlFetchApp.fetch(discoveryRestUrl, { 144 | muteHttpExceptions: true 145 | }); 146 | if (res2.getResponseCode() !== 200) { 147 | throw new Error("Batch path cannot be found."); 148 | } 149 | obj2 = JSON.parse(res2); 150 | batchPath = obj2.batchPath; 151 | return batchPath; 152 | }; 153 | 154 | parser = function(d_) { 155 | var regex, temp; 156 | temp = d_.split("--batch"); 157 | regex = /{[\S\s]+}/g; 158 | if (!d_.match(regex)) { 159 | return d_; 160 | } 161 | return temp.slice(1, temp.length - 1).map(function(e) { 162 | if (regex.test(e)) { 163 | return JSON.parse(e.match(regex)[0]); 164 | } 165 | return e; 166 | }); 167 | }; 168 | 169 | splitByteArrayBySearchData = function(baseData_, searchData_) { 170 | var bLen, idx, res, search; 171 | search = searchData_.join(""); 172 | bLen = searchData_.length; 173 | res = []; 174 | idx = 0; 175 | while (idx !== -1) { 176 | idx = baseData_.findIndex(function(_, i, a) { 177 | return (Array(bLen).fill(null).map(function(_, j) { 178 | return a[j + i]; 179 | })).join("") === search; 180 | }); 181 | if (idx !== -1) { 182 | res.push(baseData_.splice(0, idx)); 183 | baseData_.splice(0, bLen); 184 | } else { 185 | res.push(baseData_.splice(0)); 186 | } 187 | } 188 | return res; 189 | }; 190 | 191 | parserAsBinary = function(d_) { 192 | var baseData, blobs, check, res1, search, searchData; 193 | check = d_.getContentText().match(/--batch.*/); 194 | if (!check) { 195 | throw new Error("Valid response value is not returned."); 196 | } 197 | search = check[0]; 198 | baseData = d_.getContent(); 199 | searchData = Utilities.newBlob(search).getBytes(); 200 | res1 = splitByteArrayBySearchData.call(this, baseData, searchData); 201 | res1.shift(); 202 | res1.pop(); 203 | blobs = res1.map(function(e, i) { 204 | var data, dataSize, metadata, rrr; 205 | rrr = splitByteArrayBySearchData.call(this, e, [13, 10, 13, 10]); 206 | data = rrr.pop(); 207 | metadata = Utilities.newBlob(rrr.flat()).getDataAsString(); 208 | dataSize = Number(metadata.match(/Content-Length:(.*)/)[1]); 209 | return Utilities.newBlob(data.splice(0, dataSize)).setName("blob" + (i + 1)); 210 | }); 211 | return blobs; 212 | }; 213 | 214 | createRequest = function(d_) { 215 | var contentId, data, e, params; 216 | try { 217 | contentId = 0; 218 | data = "--" + this.boundary + this.lb; 219 | d_.forEach((function(_this) { 220 | return function(e) { 221 | data += "Content-Type: application/http" + _this.lb; 222 | data += "Content-ID: " + ++contentId + _this.lb + _this.lb; 223 | data += e.method + " " + e.endpoint + _this.lb; 224 | data += e.accessToken ? "Authorization: Bearer " + e.accessToken + _this.lb : ""; 225 | data += e.requestBody ? "Content-Type: application/json; charset=utf-8" + _this.lb + _this.lb : _this.lb; 226 | data += e.requestBody ? JSON.stringify(e.requestBody) + _this.lb : ""; 227 | return data += "--" + _this.boundary + _this.lb; 228 | }; 229 | })(this)); 230 | params = { 231 | muteHttpExceptions: true, 232 | method: "post", 233 | contentType: "multipart/mixed; boundary=" + this.boundary, 234 | payload: Utilities.newBlob(data).getBytes(), 235 | headers: { 236 | Authorization: 'Bearer ' + this.at 237 | } 238 | }; 239 | } catch (error) { 240 | e = error; 241 | throw new Error(e); 242 | } 243 | return params; 244 | }; 245 | 246 | return BatchRequest; 247 | 248 | })(); 249 | return r.BatchRequest = BatchRequest; 250 | })(this); 251 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BatchRequest 2 | 3 | 4 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENCE) 5 | 6 | 7 | 8 | # Overview 9 | 10 | **This is a library for running Batch Requests using Google Apps Script (GAS).** 11 | 12 | 13 | 14 | # Description 15 | 16 | When users use Google's APIs, one quota is used for one API call. When the batch request is used, several APIs can be called by one quota, although there are some limitations in the batch request. For example, in GAS, Drive API can be used be `DriveApp`. In this case, the quota is not used for using Drive API. (When `Drive` of Advanced Google Services is used, the quota is used.) But this is Drive API v2. If users want to use Drive API v3, it is required to directly request each endpoint of Drive API v3. The batch request is much useful for this situation. However, it is a bit difficult for users to use the batch request. Because the batch request is requested by `multipart/mixed`. I thought that the script may become a bit complicated, because of the request of `multipart/mixed` using `UrlFetchApp`. And although I had been looking for the libraries for the batch request, I couldn't find them. So I created this. 17 | 18 | ## Sample scripts 19 | 20 | - [Managing A Lot Of Google Calendar Events using Batch Requests with Google Apps Script](https://github.com/tanaikech/Managing-A-Lot-Of-Google-Calendar-Events-using-Batch-Requests-with-Google-Apps-Script) 21 | - [Batch Requests for Drive API using Google Apps Script](https://github.com/tanaikech/Batch-Requests-for-Drive-API-using-Google-Apps-Script) 22 | 23 | # Library's project key 24 | 25 | ``` 26 | 1HLv6tWz0oXFOJHerBTP8HsNmhpRqssijJatC92bv9Ym6HSN69_UuzcDk 27 | ``` 28 | 29 | 30 | 31 | # How to install 32 | 33 | In order to use this library, please install this library. 34 | 35 | 1. [Install BatchRequest library](https://developers.google.com/apps-script/guides/libraries). 36 | - Library's project key is **`1HLv6tWz0oXFOJHerBTP8HsNmhpRqssijJatC92bv9Ym6HSN69_UuzcDk`**. 37 | 1. For APIs you want to use, please enable the APIs at API console. 38 | - Recently, when it enabled APIs, I had an experience that I was made to wait for several minutes for enabling APIs. So when you enabled APIs at API console, if the error related to APIs occurs, please run again after several minutes. 39 | 40 | ## About scopes 41 | 42 | About the install of scopes using at this library, users are not required to install scopes. Because this library can automatically install the required scopes to the project which installed this library. The detail information about this can be seen at [here](https://gist.github.com/tanaikech/23ddf599a4155b66f1029978bba8153b). 43 | 44 | - `https://www.googleapis.com/auth/drive` 45 | - `https://www.googleapis.com/auth/drive.scripts` 46 | - `https://www.googleapis.com/auth/script.external_request` 47 | - `https://mail.google.com/` 48 | - `https://www.googleapis.com/auth/calendar` 49 | 50 | > IMPORTANT: Above 5 scopes are installed in this library. If you want to use APIs except for Calendar API, Drive API and Gmail API, please install the scopes for the APIs using [Manifests](https://developers.google.com/apps-script/concepts/manifests) to the project installed this library. Also there is [a GAS library for managing Manifests](https://github.com/tanaikech/ManifestsApp). 51 | 52 | # Methods 53 | 54 | | Method | Description | 55 | | :------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 56 | | [Do(object)](#do) | This is a simple method for the batch request. The maximum number of requests is 100. The raw values from the batch request are returned. | 57 | | [EDo(object)](#edo) | This method is the enhanced `Do()` method. When this method is used, the result values from the batch requests are parsed. And also, the number of requests more than 100 can be used. In this case, the split of the number of requests is processed for the limitation of 100. | 58 | | [getBatchPath(name, version)](#getbatchpath) | Get batch path for using batch requests. On August 12, 2020, in order to use batch requests, the batch path is required to be used to the endpoint of the batch requests. This method can simply retrieve the batch path from the name of Google API. And, the retrieved batch path can be used in [Do(object)](#do) and [EDo(object)](#edo) methods. | 59 | 60 | # Usage 61 | 62 | 63 | 64 | ## Method: Do() 65 | 66 | A sample script is as follows. This sample script renames 2 files using [update of Drive API v3](https://developers.google.com/drive/v3/reference/files/update). 67 | 68 | ```javascript 69 | var requests = { 70 | batchPath: "batch/drive/v3", // batch path. This will be introduced in the near future. 71 | requests: [ 72 | // In this method, this the maximum number of requests should be 100. 73 | { 74 | method: "PATCH", 75 | endpoint: 76 | "https://www.googleapis.com/drive/v3/files/### file ID1 ###?fields=name", 77 | requestBody: { name: "sample1" }, 78 | accessToken: ScriptApp.getOAuthToken(), // If this key is used, this access token is used. 79 | }, 80 | { 81 | method: "PATCH", 82 | endpoint: 83 | "https://www.googleapis.com/drive/v3/files/### file ID2 ###?fields=name", 84 | requestBody: { name: "sample2" }, 85 | }, 86 | ], 87 | accessToken: "###", // If you want to use the specific access token, please use this. 88 | }; 89 | var result = BatchRequest.Do(requests); // Using this library 90 | Logger.log(result); 91 | ``` 92 | 93 | - [`batchPath`](https://developers.google.com/drive/v3/web/batch#details) will be introduced in the near future. But you have already been able to use this. `batchPath` can be retrieved by [Discovery](https://developers.google.com/discovery/v1/reference/apis). 94 | 95 | - This `batchPath` can be retrieved using [getBatchPath(name, version)](#getbatchpath). 96 | 97 | - If `accessToken` is used in the object of requests (At above sample, it's `requests.requests[0].accessToken`.), the `accessToken` is used for the individual request in the batch request. If `accessToken` is not used in the requests, this library uses `ScriptApp.getOAuthToken()` for the whole batch request. 98 | 99 | - If `accessToken` is used in the object (At above sample, it's `requests.accessToken`.), you can use the specific access token. For example, in this case, you can use the access token retrieved by the service account. 100 | 101 | 102 | 103 | ## Method: EDo() 104 | 105 | A sample script is as follows. This sample script renames 2 files using [update of Drive API v3](https://developers.google.com/drive/v3/reference/files/update). 106 | 107 | ```javascript 108 | var requests = { 109 | // useFetchAll: true, // When "useFetchAll" is true, the request is run with fetchAll method. The default is false. 110 | batchPath: "batch/drive/v3", // batch path. This will be introduced in the near future. 111 | requests: [ 112 | // In this method, this the number of requests can be used more than 100. 113 | { 114 | method: "PATCH", 115 | endpoint: 116 | "https://www.googleapis.com/drive/v3/files/### file ID1 ###?fields=name", 117 | requestBody: { name: "sample1" }, 118 | accessToken: ScriptApp.getOAuthToken(), // If this key is used, this access token is used. 119 | }, 120 | { 121 | method: "PATCH", 122 | endpoint: 123 | "https://www.googleapis.com/drive/v3/files/### file ID2 ###?fields=name", 124 | requestBody: { name: "sample2" }, 125 | }, 126 | ], 127 | accessToken: "###", // If you want to use the specific access token, please use this. 128 | // exportDataAsBlob: true, // When this option is used, the returned value from the batch request can be retrieved as Blob. 129 | }; 130 | var result = BatchRequest.EDo(requests); // Using this library 131 | Logger.log(result); 132 | ``` 133 | 134 | - In this method, the result values from the batch requests are parsed, and you can retrieve the result values as an array object. 135 | 136 | * [`batchPath`](https://developers.google.com/drive/v3/web/batch#details) will be introduced in the near future. But you have already been able to use this. `batchPath` can be retrieved by [Discovery](https://developers.google.com/discovery/v1/reference/apis). 137 | 138 | - This `batchPath` can be retrieved using [getBatchPath(name, version)](#getbatchpath). 139 | 140 | * If `accessToken` is used in the object of requests (At above sample, it's `requests.requests[0].accessToken`.), the `accessToken` is used for the individual request in the batch request. If `accessToken` is not used in the requests, this library uses `ScriptApp.getOAuthToken()` for the whole batch request. 141 | 142 | - If `accessToken` is used in the object (At above sample, it's `requests.accessToken`.), you can use the specific access token. For example, in this case, you can use the access token retrieved by the service account. 143 | 144 | * `requests` 145 | - [`batchPath`](https://developers.google.com/drive/v3/web/batch#details): `batchPath` can be retrieved by [Discovery](https://developers.google.com/discovery/v1/reference/apis). 146 | - `method`: GET, POST, PUT, PATCH, DELETE and so on. Please set this for the API you want to use. 147 | - `endpoint`: Endpoint of the API you want to use. 148 | - `requestBody`: Request body of the API you want to use. This library for Google APIs. So in this case, the request body is sent as JSON. 149 | - `useFetchAll`: When "useFetchAll" is true, the request is run with fetchAll method. The default is false. For example, when 200 batch requests are used, when `useFetchAll: true` is used, 2 requests which have 100 batch requests are run with the asynchronous process using fetchAll method. [Ref](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchallrequests) When `useFetchAll: false` is used, 2 requests which have 100 batch requests are run with the synchronous process using fetch method. [Ref](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchurl,-params) 150 | - `exportDataAsBlob`: When this option is used, the returned value from the batch request can be retrieved as Blob. By this, for example, when you export Google Spreadsheet as PDF data using the batch requests, the PDF data can be retrieved as Blob. 151 | 152 | 153 | 154 | ## Method: getBatchPath(name, version) 155 | 156 | After August 12, 2020, in order to use batch requests, the batch path is required to be used to the endpoint of the batch requests. And, the batch path is sometimes updated. So, when a constant batch path has been continued to be used, this might lead to the reason for an error. In this method, the batch path is retrieved from Discovery API. By this, the latest batch path can be always simply obtained from the name of Google API. And, the retrieved batch path can be used in [Do(object)](#do) and [EDo(object)](#edo) methods. 157 | 158 | The sample script is as follows. 159 | 160 | ```javascript 161 | var res = BatchRequest.getBatchPath("drive"); 162 | Logger.log(res); // <--- batch/drive/v3 163 | ``` 164 | 165 | - There are 2 arguments in `getBatchPath(arg1, arg2)`. `arg1` and `arg2` are the name of Google API and the version of Google API you want to use. 166 | 167 | - For example, when you want to use Drive API v2, please set `BatchRequest.getBatchPath("drive", "v2")`. When you want to use the latest version of Drive API, please use `BatchRequest.getBatchPath("drive", "v3")` or `BatchRequest.getBatchPath("drive")`. When the 2nd argument is not used, the latest version is used. 168 | 169 | - In this sample, `batch/drive/v3` is returned. For example, when `var res = BatchRequest.getBatchPath("calendar")` is used, `batch/calendar/v3`. 170 | 171 | # Limitations for batch request 172 | 173 | There are some limitations for the batch request. 174 | 175 | - In the current stage, the batch request can be used for the following APIs. The number of requests which can be used in one batch request has the limitations for each API. Please check the detail information from following links. 176 | 177 | - Calendar API: [https://developers.google.com/calendar/batch](https://developers.google.com/calendar/batch) 178 | - Cloud Storage: [https://cloud.google.com/storage/docs/json_api/v1/how-tos/batch](https://cloud.google.com/storage/docs/json_api/v1/how-tos/batch) 179 | - Directory API: [https://developers.google.com/admin-sdk/directory/v1/guides/batch](https://developers.google.com/admin-sdk/directory/v1/guides/batch) 180 | - Drive API: [https://developers.google.com/drive/v3/web/batch](https://developers.google.com/drive/v3/web/batch) 181 | - Gmail API: [https://developers.google.com/gmail/api/guides/batch](https://developers.google.com/gmail/api/guides/batch) 182 | 183 | - At Batch request, it can include only one kind of API in the requests. For example, Drive API and Gmail API cannot be used for one batch request. Only one API can be used. So as a sample, you can rename the filenames of several files using Drive API by one batch request. 184 | 185 | - The batch request is worked by the asynchronous processing. So the order of request is not guaranteed. 186 | 187 | # Appendix 188 | 189 | - About the limitation of number of request for one batch request 190 | - The batch request is worked by the asynchronous processing. Also [the fetchAll method is worked by the asynchronous processing.](https://gist.github.com/tanaikech/c0f383034045ab63c19604139ecb0728) I think that by using both, the number of requests for one batch request can be increased. 191 | - [RunAll](https://github.com/tanaikech/RunAll) 192 | 193 | - This is a library for running the concurrent processing using only native Google Apps Script (GAS). 194 | 195 | - Sample script using `exportDataAsBlob` is as follows. In this sample, the Spreadsheet and Document files are exported as PDF format using the batch requests. The exported PDF data is created as a PDF file to the root folder. When I answered [this thread on Stackoverflow](https://stackoverflow.com/q/75661391), when this option is added to this librar, I thought that it might be useful for users. 196 | 197 | ```javascript 198 | const fileIds = ["###SpreadsheetID1###", "###DocumentID1###", , , ,]; 199 | const requests = fileIds.map((id) => ({ 200 | method: "GET", 201 | endpoint: `https://www.googleapis.com/drive/v3/files/${id}/export?mimeType=application/pdf`, 202 | })); 203 | const reqs = { 204 | batchPath: "batch/drive/v3", 205 | requests, 206 | exportDataAsBlob: true, 207 | }; 208 | const blobs = BatchRequestTest.EDo(reqs); // Using this library 209 | blobs.forEach((b) => { 210 | if (b) { 211 | console.log({ filename: b.getName(), fileSize: b.getBytes().length }); 212 | DriveApp.createFile(b); 213 | } 214 | }); 215 | ``` 216 | 217 | # Important 218 | 219 | - On 2024, it seems that the specification of the batch request for exporting files has been changed. In the current stage, when ["Method: files.export" of Drive API v3](https://developers.google.com/drive/api/reference/rest/v3/files/export) is used in the batch request, `HTTP/1.1 302 Found` is returned instead of the file content. So, I reported it to the Google issue tracker. [https://issuetracker.google.com/issues/328105509](https://issuetracker.google.com/issues/328105509) If you want to use it, please add a star. By this, I believe that it will help resolve the specification. 220 | 221 | - I got a response from Google. You can see it at [this issue tracker](https://issuetracker.google.com/issues/328105509). By this, in the current stage, it seems that the files cannot be export with the batch request. So, in the current stage, it is required to export the files in a loop. 222 | 223 | --- 224 | 225 | 226 | 227 | # Licence 228 | 229 | [MIT](LICENCE) 230 | 231 | 232 | 233 | # Author 234 | 235 | [Tanaike](https://tanaikech.github.io/about/) 236 | 237 | If you have any questions and commissions for me, feel free to tell me. 238 | 239 | 240 | 241 | # Update History 242 | 243 | - v1.0.0 (May 2, 2018) 244 | 245 | 1. Initial release. 246 | 247 | - v1.1.0 (June 10, 2020) 248 | 249 | 1. New method of [`EDo()`](#edo) was added. This method is the enhanced `Do()` method. When this method is used, the result values from the batch requests are parsed. And also, the number of requests more than 100 can be used. In this case, the split of the number of requests is processed for the limitation of 100. 250 | 251 | - v1.1.1 (June 12, 2020) 252 | 253 | 1. Error handling for the input object was added. 254 | 255 | - v1.1.2 (June 12, 2020) 256 | 257 | 1. Removed a bug that when the returned value is empty, an error occurred. 258 | 259 | - v1.1.3 (January 13, 2021) 260 | 261 | 1. In order to give the access token from outside, the access token got to be able to be included in the object. By this, for example, you can use the access token retrieved by the service account. 262 | 263 | - v1.1.4 (March 13, 2021) 264 | 265 | 1. By [a pull request](https://github.com/tanaikech/BatchRequest/pull/2), the inputted request is used as the call by value instead of the call by reference. It's like `this.p = p_.requests.slice();`. 266 | 267 | - v1.2.0 (September 30, 2022) 268 | 269 | 1. A new method of [getBatchPath(name, version)](#getbatchpath) was added. On August 12, 2020, in order to use batch requests, the batch path is required to be used to the endpoint of the batch requests. This method can simply retrieve the batch path from the name of Google API. And, the retrieved batch path can be used in [Do(object)](#do) and [EDo(object)](#edo) methods. 270 | 271 | - v1.2.1 (March 8, 2023) 272 | 273 | 1. An option of `exportDataAsBlob` was added to the request object to the method of `EDo()`. [Ref](https://github.com/tanaikech/BatchRequest#method-edo) When this option is used, the response values from the batch requests are returned as Blob. By this, for example, when you export Google Spreadsheet as PDF data using the batch requests, the PDF data can be retrieved as Blob. 274 | 275 | - v1.2.2 (March 27, 2023) 276 | 277 | 1. A bug was removed. 278 | 279 | - v1.2.3 (March 27, 2023) 280 | 281 | 1. A bug was removed. 282 | 283 | [TOP](#top) 284 | --------------------------------------------------------------------------------