Radek
Radek

Reputation: 11091

How to migrate Google Application Script project to different a account?

Is there any - ideally simple and quick - way how to migrate Google Application Script project to another account?

Let's say that I work on a project for a client and once it is ready I want to migrate it. But the script connects to Google Sheets and there is also a time trigger. Also the project acts as a web application.

I thought that if the customer creates a project and shares that with me and authorises the script to access the Google Sheet by running the script by themselves it would solve "the migration". But it does not look like. When I run the script it asks me to authorise the access to Sheets too. And I have no idea how it would work with the trigger and web application.

Could someone explain? How to make sure that at the end the project runs with end customer privileges, uses their quota etc?

Upvotes: 1

Views: 1745

Answers (1)

Martí
Martí

Reputation: 2851

There are multiple points to talk about:

  1. When I set up a trigger in a shared project, what happens?
  2. When I deploy in a shared project, whose quota is being used?
  3. How to set it up more easily?

The documentation has a nice table that is a good overview on what permissions execute when.

1. When I set up a trigger in a shared project, what happens?

Quoting the documentation:

When you collaborate on a project, any installable triggers that you create are not shared with those who have access to your project. […]

So basically if you make an installable trigger, that trigger executes as you and it uses your quota.

2. When I deploy in a shared project, whose quota is being used?

The documentation states:

You can collaborate on web apps using shared drive. When a web app in a shared is deployed, choosing to "execute as you" causes the web app to execute under the authority of the user that deployed it (since there is no script owner).

3. How to set it up more easily?

You can use ScriptApp to set the triggers and use the Apps Scripts API to deploy. This will recur configuration of a GCP and manually setup the project manifest (see manifest’s structure reference). Here is a code I’ve done that works when everything is set up properly:

//
// This file is for easy setup of the project.
//
 
 
/**
* Remove any installed trigger and makes the ones on the list
*/
function remakeTriggers() {
 // Remove all triggers for this project for this user
 ScriptApp.getProjectTriggers().forEach(trigger => ScriptApp.deleteTrigger(trigger))
 
  // Remake triggers
  // Change as desired to set the triggers you want
 ScriptApp.newTrigger('myTimer')
   .timeBased()
   .atHour(2)
   .everyDays(1)
   .create()
  ScriptApp.newTrigger('myTimer')
   .forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
   .onEdit()
   .create()
}
 
const DEPLOYMENT_ID_KEY = 'deploymentId'
 
/**
* Deploys this script as a WebApp.
*/
function deploy() {
 const properties = PropertiesService.getScriptProperties()
 const version = newVersion_()
  const deploymentId = properties.getProperty(DEPLOYMENT_ID_KEY)
 if (!deploymentId) {
   console.log(`Creating new deployment to version ${version.versionNumber}`)
   const deployment = newDeploy_(version.versionNumber)
   properties.setProperty(DEPLOYMENT_ID_KEY, deployment.deploymentId)
 } else {
   console.log(`Updating deployment to version ${version.versionNumber}`)
   updateDeploy_(deploymentId, version.versionNumber)
 }
}
 
/**
* Makes a new version.
*/
function newVersion_() {
 const response = UrlFetchApp.fetch(
   `https://script.googleapis.com/v1/projects/${ScriptApp.getScriptId()}/versions`,
   {
     method: 'POST',
     muteHttpExceptions: true,
     headers: {
       'Authorization': `Bearer ${ScriptApp.getOAuthToken()}`
     },
   },
 )
 return parseResponse_(response)
}
 
/**
* Makes a new deployment
*/
function newDeploy_(versionNumber) {
 const response = UrlFetchApp.fetch(
   `https://script.googleapis.com/v1/projects/${ScriptApp.getScriptId()}/deployments`,
   {
     method: 'POST',
     payload: JSON.stringify({ versionNumber }),
     contentType: 'application/json; charset=utf-8',
     headers: {
       'Authorization': `Bearer ${ScriptApp.getOAuthToken()}`
     },
     muteHttpExceptions: true,
   },
 )
 return parseResponse_(response)
}
 
/**
* Redeploys
*/
function updateDeploy_(deploymentId, versionNumber) {
 const response = UrlFetchApp.fetch(
   `https://script.googleapis.com/v1/projects/${ScriptApp.getScriptId()}/deployments/${deploymentId}`,
   {
     method: 'PUT',
     payload: JSON.stringify({
       deploymentConfig: {
         versionNumber
       }
     }),
     contentType: 'application/json; charset=utf-8',
     headers: {
       'Authorization': `Bearer ${ScriptApp.getOAuthToken()}`
     },
     muteHttpExceptions: true,
   },
 )
 return parseResponse_(response)
}
 
/**
* Helper function to parse a fetch's JSON response. Handles errors.
*
* @param {UrlFetchApp.HTTPResponse} response to parse
* @returns {Object} Parsed JSON response
*/
function parseResponse_(response) {
 const result = response.getContentText()
 const code = response.getResponseCode()
 if (code < 200 || code >= 400) {
   throw result
 }
 return JSON.parse(result)
}

Remember to change remakeTriggers so it creates the triggers that you want.

And my manifest looks like this:

{
 […]
 "oauthScopes": [
   "https://www.googleapis.com/auth/spreadsheets.currentonly",
   "https://www.googleapis.com/auth/script.external_request",
   "https://www.googleapis.com/auth/script.projects",
   "https://www.googleapis.com/auth/script.deployments"
 ],
 "webapp": {
   "access": "ANYONE",
   "executeAs": "USER_DEPLOYING"
 }
}

Notice that we are configuring the webapp here (cannot do it via API) and that the OAuth2 scopes need to be defined manually. Once everything is set up, the final user would only need to go the script file and execute the functions as required (remakeTriggers to remake the triggers and deploy to do so).

You could also make this two function private (add _ at the end of their name) and make a function that calls them both, so the user only needs to go to the Deploy.gs file a click Run.

References

Upvotes: 3

Related Questions