From c0d76038e7b4eade6381023b8232e905cd229190 Mon Sep 17 00:00:00 2001 From: Christian Schalk Date: Thu, 16 Dec 2021 15:50:39 -0800 Subject: [PATCH 1/4] Initial checkin of Forms API Apps Script webapp --- .../demos/AppsScriptFormsAPIWebApp/Code.gs | 17 + .../AppsScriptFormsAPIWebApp/FormsAPI.gs | 280 ++++++++++++++ .../demos/AppsScriptFormsAPIWebApp/Main.html | 353 ++++++++++++++++++ .../demos/AppsScriptFormsAPIWebApp/Readme | 36 ++ .../AppsScriptFormsAPIWebApp/appsscript.json | 19 + 5 files changed, 705 insertions(+) create mode 100644 forms-api/demos/AppsScriptFormsAPIWebApp/Code.gs create mode 100644 forms-api/demos/AppsScriptFormsAPIWebApp/FormsAPI.gs create mode 100644 forms-api/demos/AppsScriptFormsAPIWebApp/Main.html create mode 100644 forms-api/demos/AppsScriptFormsAPIWebApp/Readme create mode 100644 forms-api/demos/AppsScriptFormsAPIWebApp/appsscript.json diff --git a/forms-api/demos/AppsScriptFormsAPIWebApp/Code.gs b/forms-api/demos/AppsScriptFormsAPIWebApp/Code.gs new file mode 100644 index 000000000..aa0a5f9e2 --- /dev/null +++ b/forms-api/demos/AppsScriptFormsAPIWebApp/Code.gs @@ -0,0 +1,17 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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. + +function doGet() { + return HtmlService.createTemplateFromFile('Main').evaluate(); +} \ No newline at end of file diff --git a/forms-api/demos/AppsScriptFormsAPIWebApp/FormsAPI.gs b/forms-api/demos/AppsScriptFormsAPIWebApp/FormsAPI.gs new file mode 100644 index 000000000..87c170caf --- /dev/null +++ b/forms-api/demos/AppsScriptFormsAPIWebApp/FormsAPI.gs @@ -0,0 +1,280 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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. + +// Global constants. Customize as needed. +const formsAPIUrl = 'https://forms.googleapis.com/v1beta/forms/'; +const formId = ''; +const topicName = 'projects/'; + +// To setup pub/sub topics, see: +// https://cloud.google.com/pubsub/docs/building-pubsub-messaging-system + +/** + * Forms API Method: forms.create + * POST https://forms.googleapis.com/v1beta/forms + */ +function create(title) { + const accessToken = ScriptApp.getOAuthToken(); + const jsonTitle = JSON.stringify({ + info: { + title: title + } + }); + + const options = { + 'headers': { + Authorization: 'Bearer ' + accessToken + }, + 'method': 'post', + 'contentType': 'application/json', + 'payload': jsonTitle + }; + + Logger.log('Forms API POST options was: ' + JSON.stringify(options)); + let response = UrlFetchApp.fetch(formsAPIUrl, options); + Logger.log('Response from Forms API was: ' + JSON.stringify(response)); + + return ('' + response); +} + +/** + * Forms API Method: forms.get + * GET https://forms.googleapis.com/v1beta/forms/{formId}/responses/{responseId} + */ +function get(formId) { + const accessToken = ScriptApp.getOAuthToken(); + + const options = { + 'headers': { + Authorization: 'Bearer ' + accessToken, + Accept: 'application/json' + }, + 'method': 'get' + }; + + try { + let response = UrlFetchApp.fetch(formsAPIUrl + formId, options); + Logger.log('Response from Forms API was: ' + response); + return ('' + response); + } catch (e) { + Logger.log(JSON.stringify(e)); + return ('Error:' + JSON.stringify(e) + + '

Unable to find Form with formId:
' + formId); + } +} + +/** + * Forms API Method: forms.batchUpdate + * POST https://forms.googleapis.com/v1beta/forms/{formId}:batchUpdate + */ +function batchUpdate(formId) { + const accessToken = ScriptApp.getOAuthToken(); + + // Request body to add a description to a Form + const update = { + 'requests': [{ + 'updateFormInfo': { + 'info': { + 'description': 'Please complete this quiz based on this week\'s readings for class.' + }, + 'updateMask': 'description' + } + }] + } + + const options = { + 'headers': { + Authorization: 'Bearer ' + accessToken + }, + 'method': 'post', + 'contentType': 'application/json', + 'payload': JSON.stringify(update), + 'muteHttpExceptions': true, + }; + + let response = UrlFetchApp.fetch(formsAPIUrl + formId + ':batchUpdate', + options); + Logger.log('Response code from API: ' + response.getResponseCode()); + return (response.getResponseCode()); +} + +/** + * Forms API Method: forms.responses.get + * GET https://forms.googleapis.com/v1beta/forms/{formId}/responses/{responseId} + */ +function responsesGet(formId, responseId) { + const accessToken = ScriptApp.getOAuthToken(); + + var options = { + 'headers': { + Authorization: 'Bearer ' + accessToken, + Accept: 'application/json' + }, + 'method': 'get' + }; + + try { + var response = UrlFetchApp.fetch(formsAPIUrl + formId + '/' + 'responses/' + + responseId, options); + Logger.log('Response from Forms.responses.get was: ' + response); + return ('' + response); + } catch (e) { + Logger.log(JSON.stringify(e)); + return ('Error:' + JSON.stringify(e)) + } +} + +/** + * Forms API Method: forms.responses.list + * GET https://forms.googleapis.com/v1beta/forms/{formId}/responses + */ +function responsesList(formId) { + const accessToken = ScriptApp.getOAuthToken(); + + var options = { + 'headers': { + Authorization: 'Bearer ' + accessToken, + Accept: 'application/json' + }, + 'method': 'get' + }; + + try { + var response = UrlFetchApp.fetch(formsAPIUrl + formId + '/' + 'responses', + options); + Logger.log('Response from Forms.responses was: ' + response); + return ('' + response); + } catch (e) { + Logger.log(JSON.stringify(e)); + return ('Error:' + JSON.stringify(e)) + } +} + +/** + * Forms API Method: forms.watches.create + * POST https://forms.googleapis.com/v1beta/forms/{formId}/watches + */ +function createWatch(formId) { + let accessToken = ScriptApp.getOAuthToken(); + + var myWatch = { + 'watch': { + 'target': { + 'topic': { + 'topicName': topicName + } + }, + 'eventType': 'RESPONSES', + } + }; + Logger.log('myWatch is: ' + JSON.stringify(myWatch)); + + var options = { + 'headers': { + Authorization: 'Bearer ' + accessToken + }, + 'method': 'post', + 'contentType': 'application/json', + 'payload': JSON.stringify(myWatch), + 'muteHttpExceptions': false, + }; + Logger.log('options are: ' + JSON.stringify(options)); + Logger.log('formsAPIURL was: ' + formsAPIUrl); + + var response = UrlFetchApp.fetch(formsAPIUrl + formId + '/' + 'watches', + options); + Logger.log(response); + return ('' + response); +} + +/** + * Forms API Method: forms.watches.delete + * DELETE https://forms.googleapis.com/v1beta/forms/{formId}/watches/{watchId} + */ +function deleteWatch(formId, watchId) { + let accessToken = ScriptApp.getOAuthToken(); + + Logger.log('formsAPIUrl is: ' + formsAPIUrl); + + var options = { + 'headers': { + Authorization: 'Bearer ' + accessToken, + Accept: 'application/json' + }, + 'method': 'delete', + 'muteHttpExceptions': false, + }; + + try { + var response = UrlFetchApp.fetch(formsAPIUrl + formId + '/' + 'watches/' + + watchId, options); + Logger.log(response); + return ('' + response); + } catch (e) { + Logger.log('API Error: ' + JSON.stringify(e)); + return (JSON.stringify(e)); + } + +} + +/** + * Forms API Method: forms.watches.list + * GET https://forms.googleapis.com/v1beta/forms/{formId}/watches + */ +function watchesList(formId) { + Logger.log('formId is: ' + formId); + let accessToken = ScriptApp.getOAuthToken(); + var options = { + 'headers': { + Authorization: 'Bearer ' + accessToken, + Accept: 'application/json' + }, + 'method': 'get' + }; + try { + var response = UrlFetchApp.fetch(formsAPIUrl + formId + '/' + 'watches', + options); + Logger.log(response); + return ('' + response); + } catch (e) { + Logger.log('API Error: ' + JSON.stringify(e)); + return (JSON.stringify(e)); + } +} + +/** + * Forms API Method: forms.watches.renew + * POST https://forms.googleapis.com/v1beta/forms/{formId}/watches/{watchId}:renew + */ +function renewWatch(formId, watchId) { + let accessToken = ScriptApp.getOAuthToken(); + + var options = { + 'headers': { + Authorization: 'Bearer ' + accessToken, + Accept: 'application/json' + }, + 'method': 'post' + }; + + try { + var response = UrlFetchApp.fetch(formsAPIUrl + formId + '/' + 'watches/' + + watchId + ':renew', options); + Logger.log(response); + return ('' + response); + } catch (e) { + Logger.log('API Error: ' + JSON.stringify(e)); + return (JSON.stringify(e)); + } +} \ No newline at end of file diff --git a/forms-api/demos/AppsScriptFormsAPIWebApp/Main.html b/forms-api/demos/AppsScriptFormsAPIWebApp/Main.html new file mode 100644 index 000000000..4cdd0c966 --- /dev/null +++ b/forms-api/demos/AppsScriptFormsAPIWebApp/Main.html @@ -0,0 +1,353 @@ + + + + + + + + + + +

Forms API & Apps Script Testing + Application - v.1

+
+ Form Id: + + +
+ + + + + + + + + +
MethodsStatus
+
+ + +
+ forms.create + (spec)
+ Form title: + +
+ + + +
+ forms.get + (spec)
+ +
+ + + +
+ forms.batchUpdate + (spec)
+ + +
+ + + +
+ forms.responses.list + (spec)
+ +
+
+ + +
+ forms.responses.get + (spec)
+ Response id:
+ +
+
+ + +
+ forms.watches.create + (spec)
+ +
+
+ + +
+ forms.watches.delete + (spec)
+ Watch id:
+ +
+
+ + +
+ forms.watches.list + (spec)
+ +
+
+ + +
+ forms.watches.renew + (spec)
+ Watch id:
+ + +
+ +
+
+
+
+
+
+ Forms API Reference +
+ + + \ No newline at end of file diff --git a/forms-api/demos/AppsScriptFormsAPIWebApp/Readme b/forms-api/demos/AppsScriptFormsAPIWebApp/Readme new file mode 100644 index 000000000..e35267d5a --- /dev/null +++ b/forms-api/demos/AppsScriptFormsAPIWebApp/Readme @@ -0,0 +1,36 @@ +--- +title: Google Forms API Apps Script Webapp +description: Serves as an example of how to use the Google Forms API via REST calls from Apps Script. +labels: Apps Script, Google Forms API, Forms +material_icon: Feed +create_time: 2021-10-10 +update_time: 2021-12-21 +--- + +This solution demonstrates how to interact with the new Google Forms API directly from Apps Script using REST calls, and not +the native Apps Script Forms Service service. + +## General Setup + + +### Join the Forms API EAP! + +1. Required: Your account and GCP project must be allowlisted as per the EAP program in order to make requests to the API. +See: https://developers.google.com/forms/api + + +Apps Script project customization + +1. After creating a new blank Apps Script project, click Project Settings and: + * Check 'Show "appsscript.json" manifest file in editor' + * Enter the Project Number of the project that was allowlisted for the Forms API and click 'Change project'. + +1. Copy the contents of the Apps Script, HTML and JSON files into your Apps Script project. + +1. Edit the FormsAPI.gs file and customize the constants. + * formId (Select one of your existing Forms id); + * topicName (Optional, if using Watches(pub/sub). Further project setup required) + Note: To setup pub/sub topics, see: https://cloud.google.com/pubsub/docs/building-pubsub-messaging-system + +1. Deploy the project as a Web app, Authorize access and click on the deployment URL. + diff --git a/forms-api/demos/AppsScriptFormsAPIWebApp/appsscript.json b/forms-api/demos/AppsScriptFormsAPIWebApp/appsscript.json new file mode 100644 index 000000000..796a53b3e --- /dev/null +++ b/forms-api/demos/AppsScriptFormsAPIWebApp/appsscript.json @@ -0,0 +1,19 @@ +{ + "timeZone": "America/New_York", + "exceptionLogging": "STACKDRIVER", + "runtimeVersion": "V8", + "dependencies": {}, + "webapp": { + "executeAs": "USER_DEPLOYING", + "access": "MYSELF" + }, + "oauthScopes": [ + "https://www.googleapis.com/auth/script.external_request", + "https://www.googleapis.com/auth/drive", + "https://www.googleapis.com/auth/drive.readonly", + "https://www.googleapis.com/auth/forms.body", + "https://www.googleapis.com/auth/forms.body.readonly", + "https://www.googleapis.com/auth/forms.responses.readonly", + "https://www.googleapis.com/auth/userinfo.email" + ] +} \ No newline at end of file From ac4b35ee20f66d478d930664f8d7b720a0fd2edd Mon Sep 17 00:00:00 2001 From: Christian Schalk Date: Thu, 16 Dec 2021 16:47:21 -0800 Subject: [PATCH 2/4] Updated checkin of Forms API Apps Script webapp --- .../demos/AppsScriptFormsAPIWebApp/Main.html | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/forms-api/demos/AppsScriptFormsAPIWebApp/Main.html b/forms-api/demos/AppsScriptFormsAPIWebApp/Main.html index 4cdd0c966..f53ff9a8a 100644 --- a/forms-api/demos/AppsScriptFormsAPIWebApp/Main.html +++ b/forms-api/demos/AppsScriptFormsAPIWebApp/Main.html @@ -2,6 +2,7 @@ + Main Web Page