├── Generator-SRC ├── FetchApis.gs ├── GasIO.gs ├── README.md ├── SharedLibraryFunctions.gs └── WriteLibrary.gs ├── LICENSE ├── README.md └── appsscript.json /Generator-SRC/FetchApis.gs: -------------------------------------------------------------------------------- 1 | function onOpen(){ 2 | var ui = SpreadsheetApp.getUi(); 3 | var menu = ui.createMenu("Genereate API Libraries"); 4 | menu.addItem("Setup", "setUpSheet"); 5 | menu.addItem("Fetch API List", "getApiHeaders"); 6 | menu.addItem("Build Libraries", "buildLibraries"); 7 | menu.addToUi(); 8 | } 9 | 10 | 11 | // 0. Run clearAllSheets if you want to easily remove all the generated sheets. 12 | 13 | // 0a. If you need to change the google APIs Url to a GAE endpoint 14 | var discoveryUrl = "https://www.googleapis.com"; // GAE endpoint would look like -> https://grab-n-go-test.appspot.com/_ah/api 15 | 16 | // 1. Run getApiHeaders 17 | // 2. Run getAllApiDetails 18 | // 3. Set the output folder Id 19 | var libOutputfolderId = PropertiesService.getUserProperties().getProperty("folderId"); 20 | 21 | // 4. Run writeLibraries 22 | 23 | 24 | 25 | function buildLibraries(){ 26 | getAllApiDetails(); 27 | writeLibraries(); 28 | } 29 | 30 | function setUpSheet(){ 31 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 32 | var tsheet = ss.insertSheet("-", 0) 33 | tsheet.deleteRows(2, 999) 34 | var sheets = ss.getSheets(); 35 | for(var i = 1 ; i < sheets.length;i++){ 36 | ss.deleteSheet(sheets[i]) 37 | } 38 | tsheet.setName("Template"); 39 | 40 | var folderId = Browser.inputBox("Enter folderId where Libraries are to be saved"); 41 | PropertiesService.getUserProperties().setProperty("folderId", folderId); 42 | 43 | } 44 | 45 | function getApiHeaders() { 46 | var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('CurrentApis'), 47 | url = discoveryUrl + "/discovery/v1/apis"; 48 | items = JSON.parse(UrlFetchApp.fetch(url).getContentText()).items, 49 | apiList = [], thisApi = []; 50 | 51 | 52 | if(!ss){ 53 | ss = SpreadsheetApp.getActiveSpreadsheet().insertSheet('CurrentApis',{template:SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Template")}) 54 | } 55 | 56 | for (var i in items){ 57 | thisApi = [items[i].name, 58 | items[i].version, 59 | items[i].title, 60 | items[i].description, 61 | items[i].discoveryLink]; 62 | 63 | apiList.push(thisApi); 64 | thisApi = ""; 65 | } 66 | Logger.log(apiList) 67 | ss.clear(); 68 | var range = ss.getRange(1, 1, apiList.length, 5); 69 | range.setValues(apiList); 70 | SpreadsheetApp.flush(); 71 | //Browser.msgBox("Insert Checkboxes into Column F of CurrentApis sheet") 72 | var checkBoxRange = ss.getRange(1,6,apiList.length,1); 73 | var enforceCheckbox = SpreadsheetApp.newDataValidation(); 74 | enforceCheckbox.requireCheckbox(); 75 | enforceCheckbox.setAllowInvalid(false); 76 | enforceCheckbox.build(); 77 | 78 | checkBoxRange.setDataValidation(enforceCheckbox); 79 | 80 | 81 | } 82 | 83 | 84 | function getAllApiDetails(){ 85 | var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CurrentApis"); 86 | var apis = ss.getRange(1, 1, ss.getLastRow(), 6).getValues(); 87 | 88 | for(var i = 0; i < apis.length;i++){ 89 | if(apis[i][5] == false){ 90 | var oldSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(apis[i][0]+'-'+apis[i][1]); 91 | if(oldSheet){ 92 | SpreadsheetApp.getActiveSpreadsheet().deleteSheet(oldSheet) 93 | } 94 | continue; 95 | } 96 | getApi(apis[i][0],apis[i][1]); 97 | } 98 | } 99 | 100 | function writeLibraries(){ 101 | writeLibraries_(); 102 | } 103 | 104 | function getApi(api,ver){ 105 | try { 106 | var url = discoveryUrl + "/discovery/v1/apis/"+api+"/"+ver+"/rest", 107 | apiData = JSON.parse(UrlFetchApp.fetch(url)), 108 | ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(api+'-'+ver), 109 | apiParams = [],apiScopes = []; 110 | 111 | if(!ss){ 112 | ss = SpreadsheetApp.getActiveSpreadsheet().insertSheet(api+'-'+ver, 2, {template:SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Template")}) 113 | } 114 | 115 | ss.clear(); 116 | ss.appendRow([apiData.baseUrl]) 117 | 118 | ss.appendRow(["Scopes"]); 119 | if(apiData.auth){ 120 | for(var scope in apiData.auth.oauth2.scopes){ 121 | apiScopes.push(scope); 122 | } 123 | }else{ 124 | apiScopes = ["none"] 125 | } 126 | ss.appendRow(apiScopes) 127 | 128 | 129 | ss.appendRow(["Parameters"]) 130 | for(var i in apiData.parameters){ 131 | apiParams.push(i); 132 | } 133 | ss.appendRow(apiParams) 134 | 135 | 136 | ss.appendRow(["DocumentationUrl"]); 137 | ss.appendRow([apiData.documentationLink]); 138 | 139 | ss.appendRow(["Resources"]) 140 | getResources(ss,apiData); 141 | } catch(e) { 142 | Logger.log(e) 143 | } 144 | 145 | } 146 | 147 | 148 | function getResources(inSS,inObj){ 149 | 150 | 151 | if(inObj.methods){ 152 | var methods = []; 153 | for(var method in inObj.methods){ 154 | 155 | var apiString = "{\"id\":\"" + 156 | inObj.methods[method].id+ 157 | "\",\"method\":\""+ 158 | inObj.methods[method].httpMethod+ 159 | "\",\"urlPath\":\""+ 160 | inObj.methods[method].path+ 161 | "\",\"desc\":\""+ 162 | escape(inObj.methods[method].description)+ 163 | "\""; 164 | 165 | 166 | if(inObj.methods[method].parameterOrder){ 167 | apiString += ",\"params\":"+JSON.stringify(inObj.methods[method].parameterOrder); 168 | apiString += ",\"paramDesc\":"+JSON.stringify(inObj.methods[method].parameters); 169 | 170 | } 171 | 172 | 173 | if(inObj.methods[method].request != undefined){ 174 | apiString += ",\"postBody\":\""+inObj.methods[method].request.$ref+"\""; 175 | } 176 | 177 | 178 | apiString += ",\"fetchMethod\":"; 179 | if(inObj.methods[method].parameters && inObj.methods[method].parameters.pageToken){ 180 | 181 | apiString += "\"CALLPAGE\""; 182 | }else{ 183 | apiString += "\"CALL\""; 184 | } 185 | if(inObj.methods[method].response){ 186 | apiString += ",\"resource\":\""+inObj.methods[method].response.$ref+"\""; 187 | } 188 | 189 | apiString += "}"; 190 | methods.push(apiString); 191 | } 192 | inSS.appendRow(methods); 193 | } 194 | 195 | if(inObj.resources){ 196 | for(var resource in inObj.resources) 197 | getResources(inSS,inObj.resources[resource]) 198 | } 199 | 200 | } 201 | 202 | 203 | function clearAllSheets(){ 204 | var skipSheets = ["Template","CurrentApis"]; 205 | var ss = SpreadsheetApp.getActive(); 206 | var sheets = ss.getSheets(); 207 | for(var i = 0; i < sheets.length;i++){ 208 | if(skipSheets.indexOf(sheets[i].getName()) == -1){ 209 | ss.deleteSheet(sheets[i]); 210 | } 211 | } 212 | } 213 | 214 | 215 | var escape = function (str) { 216 | if(str){ 217 | return str 218 | .replace(/[\\]/g, '\\\\') 219 | .replace(/[\"]/g, '\\\"') 220 | .replace(/[\/]/g, '\\/') 221 | .replace(/[\b]/g, '\\b') 222 | .replace(/[\f]/g, '\\f') 223 | .replace(/[\n]/g, '\\n') 224 | .replace(/[\r]/g, '\\r') 225 | .replace(/[\t]/g, '\\t'); 226 | }else{ 227 | return str; 228 | } 229 | }; 230 | 231 | -------------------------------------------------------------------------------- /Generator-SRC/GasIO.gs: -------------------------------------------------------------------------------- 1 | function createNewProject(projectName,content,folderId){ 2 | 3 | ScriptAPILibrary.setTokenService(function(){ return ScriptApp.getOAuthToken()}); 4 | var newProject = ScriptAPILibrary.projectsCreate({title: projectName}); 5 | content.files[0].lastModifyUser = newProject.creator; 6 | var manifest = ScriptAPILibrary.projectsGetContent(newProject.scriptId).files[0]; 7 | content.files.push(manifest); 8 | var results = ScriptAPILibrary.projectsUpdateContent(newProject.scriptId, content); 9 | moveFile(newProject.scriptId,folderId); 10 | } 11 | 12 | 13 | 14 | function moveFile(fileId, folderId){ 15 | var file = DriveApp.getFileById(fileId); 16 | var currFolder = file.getParents().next(); 17 | var newFolder = DriveApp.getFolderById(folderId); 18 | newFolder.addFile(file); 19 | currFolder.removeFile(file); 20 | } -------------------------------------------------------------------------------- /Generator-SRC/README.md: -------------------------------------------------------------------------------- 1 | 1) Add this script to a Spreadsheet. 2 | 3 | 2) The Spreadsheet needs one sheet called `Template` that has a single row. This ensures you don't hit the max cell count when generating the API data. 4 | 5 | 3) Look in FetchApis.gs for more run instructions. 6 | -------------------------------------------------------------------------------- /Generator-SRC/SharedLibraryFunctions.gs: -------------------------------------------------------------------------------- 1 | var tokenService_; 2 | 3 | /* 4 | * Stores the function passed that is invoked to get a OAuth2 token; 5 | * @param {function} service The function used to get the OAuth2 token; 6 | * 7 | */ 8 | function setTokenService(service){ 9 | tokenService_ = service; 10 | } 11 | 12 | /* 13 | * Returns an OAuth2 token from your TokenService as a test 14 | * @return {string} An OAuth2 token 15 | * 16 | */ 17 | function testTokenService(){ 18 | return tokenService_(); 19 | } 20 | 21 | /** 22 | * Performs a Fetch 23 | * @param {string} url The endpoint for the URL with parameters 24 | * @param {Object.} options Options to override default fetch options 25 | * @returns {Object.} the fetch results 26 | * @private 27 | */ 28 | function CALL_(path,options){ 29 | var fetchOptions = {method:"",muteHttpExceptions:true, contentType:"application/json", headers:{Authorization:"Bearer "+tokenService_()}} 30 | var url = BASEURL_ + path; 31 | 32 | for(option in options){ 33 | fetchOptions[option] = options[option]; 34 | } 35 | 36 | var response = UrlFetchApp.fetch(url, fetchOptions) 37 | if(response.getResponseCode() != 200){ 38 | throw new Error(response.getContentText()) 39 | }else{ 40 | return JSON.parse(response.getContentText()); 41 | } 42 | } 43 | 44 | /** 45 | * Performs a Fetch and accumulation using pageToken parameter of the returned results 46 | * @param {string} url The endpoint for the URL with parameters 47 | * @param {Object.} options Options to override default fetch options 48 | * @param {string} returnParamPath The path of the parameter to be accumulated 49 | * @returns {Array.Object.} An array of objects 50 | * @private 51 | */ 52 | function CALLPAGE_(path,options, returnParamPath){ 53 | var fetchOptions = {method:"",muteHttpExceptions:true, contentType:"application/json", headers:{Authorization:"Bearer "+tokenService_()}} 54 | for(option in options){ 55 | fetchOptions[option] = options[option]; 56 | } 57 | var url = BASEURL_ + path; 58 | var returnArray = []; 59 | var nextPageToken; 60 | do{ 61 | if(nextPageToken){ 62 | url = buildUrl_(url, {pageToken:nextPageToken}); 63 | } 64 | var results = UrlFetchApp.fetch(url, fetchOptions); 65 | if(results.getResponseCode() != 200){ 66 | throw new Error(results.getContentText()); 67 | }else{ 68 | var resp = JSON.parse(results.getContentText()) 69 | nextPageToken = resp.nextPageToken; 70 | returnArray = returnArray.concat(resp[returnParamPath]) 71 | } 72 | url = BASEURL_ + path; 73 | }while(nextPageToken); 74 | return returnArray; 75 | } 76 | 77 | /** 78 | * Builds a complete URL from a base URL and a map of URL parameters. Written by Eric Koleda in the OAuth2 library 79 | * @param {string} url The base URL. 80 | * @param {Object.} params The URL parameters and values. 81 | * @returns {string} The complete URL. 82 | * @private 83 | */ 84 | function buildUrl_(url, params) { 85 | var params = params || {}; //allow for NULL options 86 | var paramString = Object.keys(params).map(function(key) { 87 | return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); 88 | }).join('&'); 89 | return url + (url.indexOf('?') >= 0 ? '&' : '?') + paramString; 90 | } 91 | -------------------------------------------------------------------------------- /Generator-SRC/WriteLibrary.gs: -------------------------------------------------------------------------------- 1 | function writeLibraries_(name){ 2 | var skipSheets = ["Template","CurrentApis"]; 3 | 4 | var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets(); 5 | 6 | for(var i in sheets){ 7 | if(skipSheets.indexOf(sheets[i].getName()) == -1 || (name && sheets[i].getName() == name)){ 8 | var ret = makeLibrary(sheets[i]) 9 | 10 | var createDateTime = new Date().toISOString(); 11 | var functionSet = {values:ret.functionList}; 12 | 13 | var libraryProject = 14 | { "files": 15 | [ 16 | { 17 | "name": ret.fileName, 18 | "type": "server_js", 19 | "source": ret.code, 20 | "lastModifyUser":{}, 21 | "createTime":createDateTime, 22 | "updateTime": createDateTime, 23 | "functionSet":functionSet 24 | } 25 | ] 26 | }; 27 | 28 | try{ 29 | createNewProject(ret.fileName,libraryProject,libOutputfolderId); 30 | } 31 | catch(e){Logger.log(e)} 32 | } 33 | } 34 | } 35 | 36 | 37 | 38 | 39 | function makeLibrary(sheet,addHeader){ 40 | 41 | var ss = sheet; 42 | var addHeader = addHeader || true; 43 | var resources = ss.getRange(9, 1, ss.getLastRow() , ss.getLastColumn()).getValues(); 44 | var docs = ss.getRange(7, 1).getValue(); 45 | var basePath = ss.getRange(1, 1).getValue(); 46 | var scopes = ss.getRange(3, 1, 1, 10).getValues(); 47 | var fileName = ss.getName().charAt(0).toUpperCase() + ss.getName().slice(1); 48 | var code = "" 49 | 50 | 51 | code += '\n\/**\n' 52 | code += '* Google Apps Script Library for the '+ss.getName()+' API\n'; 53 | code += "* \n"; 54 | code += '* Documentation can be found: \n'; 55 | code += '* '+ docs +'\n'; 56 | code += '* \n'; 57 | code += '* OAuth2 Scopes\n' 58 | for(var i in scopes[0]){ 59 | if(scopes[0][i] !== ""){ 60 | code += '* '+scopes[0][i] + "\n" 61 | } 62 | } 63 | code += '*\/\n\n'; 64 | 65 | code += 'var BASEURL_="'+basePath+'";\n'; 66 | if(addHeader){ 67 | code += ScriptApp.getResource('Generator-SRC/SharedLibraryFunctions').getDataAsString() + "\n"; 68 | } 69 | 70 | 71 | 72 | var functionList = []; 73 | for(var i in resources){ 74 | if(resources[i][0] !== ""){ 75 | var serviceObj = writeService(resources[i]) 76 | code += serviceObj.source; 77 | functionList = functionList.concat(serviceObj.functionNames) 78 | } 79 | } 80 | return {fileName:fileName,code:code,functionList:functionList} 81 | } 82 | 83 | 84 | 85 | function writeService(serviceObj){ 86 | var serviceFunctionsList = []; 87 | var serviceFunctions = ""; // ['self_.'+serviceName+' = function(){};']; 88 | for(var i in serviceObj){ 89 | if(serviceObj[i] !== ""){ 90 | var functionObj = writeFunction(JSON.parse(serviceObj[i])) 91 | serviceFunctions += functionObj.source; 92 | serviceFunctionsList.push({name:functionObj.name}); 93 | } 94 | } 95 | return {functionNames: serviceFunctionsList, source:serviceFunctions} 96 | } 97 | 98 | 99 | function writeFunction(functionObj){ 100 | var method = functionObj.method; 101 | var postBody = functionObj.postBody; 102 | 103 | //var url = ("\""+ functionObj.urlPath.replace(/\{/g,"\"+").replace(/\}/g,"+\"") + "\""); 104 | var url = "\"" + functionObj.urlPath; 105 | url = url.replace('+',''); //WHY do they add the + in the URL 106 | url = (url.replace(/\{/g,"\"+").replace(/\}/g,"+\"") + "\"").replace(/\+\"\"/g,''); 107 | var id = functionObj.id.split('.'); 108 | var service = id.shift(); 109 | 110 | var functionId = id.map(function(word,i){return (i == 0)?word: word.charAt(0).toUpperCase() + word.slice(1)}).join(''); 111 | var resourceName = functionObj.resource || "remove"; 112 | var fetchMethod = functionObj.fetchMethod; 113 | var params = functionObj.params || []; 114 | if(postBody){params.push(postBody+"Resource")} 115 | params.push("options"); 116 | 117 | 118 | 119 | var jsDoc = '\n\/**\n'; 120 | jsDoc += '* '+ functionObj.desc + '\n'; 121 | jsDoc += "*\n"; 122 | 123 | if(params.length > 0){ 124 | for(var i in params){ 125 | if(params[i] === "options"){ 126 | jsDoc += '* @param {object} options Keypair of all optional parameters for this call\n'; 127 | }else if(params[i].indexOf("Resource") != -1){ 128 | jsDoc += '* @param {object} '+params[i]+' An object containing the '+params[i]+' for this method\n'; 129 | }else 130 | { 131 | var description = functionObj.paramDesc[params[i]].description; 132 | 133 | //HACK 134 | description = description.replace(/\*/g,'X') 135 | description = description.replace(/\n/g,'') 136 | 137 | jsDoc += '* @param {' + functionObj.paramDesc[params[i]].type + '} '+ params[i] + ' ' + description + '\n'; 138 | 139 | } 140 | 141 | 142 | } 143 | } 144 | 145 | if(functionObj.resource){ 146 | jsDoc += '* @return {object} The returned '+functionObj.resource+'Resource object\n'; 147 | } 148 | jsDoc += '*\/\n'; 149 | 150 | 151 | 152 | var newFunction = 'function '+functionId+'('+params.join()+'){'+ 153 | '\n var path = buildUrl_('+url+',options);'; 154 | if(postBody){ 155 | newFunction +='\n var callOptions = {method:"'+method+'",payload:JSON.stringify('+(postBody + 'Resource')+')};' 156 | }else{ 157 | newFunction +='\n var callOptions = {method:"'+method+'"};'; 158 | } 159 | 160 | if(fetchMethod === "CALL"){ 161 | newFunction += '\n var '+resourceName+'Resource = CALL_(path,callOptions);' 162 | newFunction += '\n return '+resourceName+'Resource;'; 163 | }else{ 164 | newFunction += '\n var '+resourceName+'Items = CALLPAGE_(path,callOptions,"items");' 165 | newFunction += '\n return '+resourceName+'Items;'; 166 | } 167 | newFunction += '\n}\n'; 168 | 169 | return {name:functionId, source:jsDoc+newFunction}; 170 | } 171 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Spencer-Easton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apps-Script-GoogleApis-Libraries 2 | A collection of libraries to access all public Google Apis from Apps Script 3 | -------------------------------------------------------------------------------- /appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "America/New_York", 3 | "dependencies": { 4 | "libraries": [{ 5 | "userSymbol": "ScriptAPILibrary", 6 | "libraryId": "1_Yv878TjVM-1QG3KnNYgLAm3QUJys-WQ89Pmjthyf5me_tJYXSmkiW8a", 7 | "version": "2" 8 | }] 9 | }, 10 | "exceptionLogging": "STACKDRIVER", 11 | "oauthScopes": ["https://www.googleapis.com/auth/script.projects", 12 | "https://www.googleapis.com/auth/drive", 13 | "https://www.googleapis.com/auth/script.external_request", 14 | "https://www.googleapis.com/auth/spreadsheets" 15 | ] 16 | } --------------------------------------------------------------------------------