google / clasp

🔗 Command Line Apps Script Projects
https://developers.google.com/apps-script/guides/clasp
Apache License 2.0
4.65k stars 432 forks source link

CLASP RUN PRODUCES ERRORS #204

Open jasonjurotich opened 6 years ago

jasonjurotich commented 6 years ago

(Please search existing issues before creating a new one.)

Expected Behavior

A simple function should run to add text to a cell in a Google Spreadsheet

Actual Behavior

errors

Steps to Reproduce the Problem

  1. clasp create [name]
  2. clasp clone [ScriptId] (This is to download the Code.js)
  3. add following code to Code.js by VIM editor:

function myFunction() { SpreadsheetApp.openById('1acJRcIGXIYa9YNgO8g3DTrgFRO6Iqk0wNJoxYpsust8').getRange('A1').setValue('set'); }

  1. save and close (in VIM = :x )
  2. clasp push (make sure everything is the same)
  3. clasp run myFunction

Specifications

I am using the GCP with a VM instance with Ubuntu 18 (up to date) with Node 10.2 and @google/clasp@1.3.1

The errors are in the document attached. I tried the script in a normal manner after clasp open and it worked, so something is off with the clasp run. Maybe something with permissions? errorclasp.txt

grant commented 6 years ago

@campionfellin Can you look at this? It looks like we need to document run a bit better.

campionfellin commented 6 years ago

Yep, I'll take a look in a little bit.

campionfellin commented 6 years ago

So after some investigation this is what I've found:

If you follow the instructions in this PR: https://github.com/google/clasp/pull/172 you should be able to use clasp run for simple functions that don't need extra scoping access.

More complex functions (like yours, requiring access to Sheets) are not yet supported by clasp (yet 😉). By following the instructions outlined in the above PR, you should get a response like:

  code: 401,
  errors:
   [ { message: 'Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.',
       domain: 'global',
       reason: 'unauthorized' } ] }

Which indicates that you don't have authorization. Of course this makes sense, because when we logged in we didn't give it the right scopes (for Sheets). Go here: https://github.com/google/clasp/blob/8e1c3fbeca11e06d22554b183b5d43d1c8490808/src/auth.ts#L20

and add this scope https://www.googleapis.com/auth/spreadsheets to the list. Then clasp logout and clasp login and then run your function. You should get a response like:

{ done: true,
  response:
   { '@type': 'type.googleapis.com/google.apps.script.v1.ExecutionResponse' } }

And if you open up the Sheet, you'll see the header has changed like you wanted to do.

Obviously, this is not ideal. There is some discussion to be had about how we want to go about making this easier for the user. I remember @grant had some ideas previously about doing that, so we'll have to discuss.

grant commented 6 years ago

Makes sense, good report.

campionfellin commented 6 years ago

Hey, actually, I don't think we even really need --ownkey anymore since we figured out how to recursively look for .clasprc.json files. Essentially --ownkey just saves it to the current directory, rather than to ~/. I did not realize that appsscript.json had an oauthScopes array. How do we plan to get the scopes in there? Once the scopes are there, it shouldn't be too bad to add those when we authorize.

grant commented 6 years ago

We need --ownkey to login non-globally to use script.run. Let's just manually add the scopes there for now.

Eventually we can have clasp automatically add OAuth scopes to appsscript.json the current way Apps Script's editor implicity adds scopes when you run functions.

How script.google.com Requests Scopes

For example, running this in the Apps Script editor:

function myFunction() {
  SlidesApp.create("test");
}

Will implicity request the https://www.googleapis.com/auth/presentations scope.

How the script.run API Call Requests Scopes

We manually requests scopes when auth'ing clasp (or your OAuth client).

Thus, we need to explicitly add OAuth scopes for clasp's OAuth client.

campionfellin commented 6 years ago

I don't think that we need --ownkey anymore, unless you can find where in the code it is actually needed.

screen shot 2018-05-31 at 10 52 54 pm

All I see it used for is here:

https://github.com/google/clasp/blob/8e1c3fbeca11e06d22554b183b5d43d1c8490808/src/auth.ts#L155

Where it calls authorize(...) with it.

authorize(...) then uses it to decide whether to save it in the workdir or homedir:

https://github.com/google/clasp/blob/8e1c3fbeca11e06d22554b183b5d43d1c8490808/src/auth.ts#L58

clasp run doesn't care, it just uses await loadAPICredentials https://github.com/google/clasp/blob/8e1c3fbeca11e06d22554b183b5d43d1c8490808/src/commands.ts#L270

jasonjurotich commented 6 years ago

Thanks guys. If it were possible, I think the idea would be to be able to stay in the terminal, even if that meant to type in more info at the beginning when beginning clasp. I am trying to get it to where we don't have to leave the terminal and go back to the GAS or the cloud project, which sort of defeats the purpose of using clasp in the first place, I would think.

It would be wonderful, even if it meant adding more info within the terminal at the beginning, if we could just add the scopes we need, when we begin clasp in the terminal, or, ... another suggestion is that you just add the scopes that most people need, like gmail, sheets, slides, docs, drive, scripts, etc. so we can avoid having to go out to specify what else we needed. I don't think most people who use this would have a problem giving permissions to more scopes, if it reduced the steps...

Either way, the goal would be not to have to leave the terminal so that we could run GAS anywhere (like on a cell phone using something like Termius), even if that meant putting in more info at the beginning.

grant commented 6 years ago

@campionfellin How did you create the .clasprc.json?

campionfellin commented 6 years ago

@grant I just used clasp login. Did you put in your own clientId and clientSecret here? https://github.com/google/clasp/blob/24c7d1309cde125b7a9db2a7d41f32bab4f5d7d1/src/auth.ts#L12

jasonjurotich commented 6 years ago

OK, saw that you guys updated the readme file. (Remotely executes an Apps Script function. This function runs your script in the cloud. You must supply the functionName params. For now, it can only run functions that do not require other authorization.)

Just a quick question, the sendMail doesn't need the Gmail API to send things?

I know you guys must be overworked, but would it be possible to check-in in about a month to see if you had time to add the scopes?

Thanks once again for all your hard work!!!

campionfellin commented 6 years ago

Hey @jasonjurotich I don't think it needs the Gmail API, but it does need this scope: https://www.googleapis.com/auth/script.send_mail

Definitely check in in about a month and we can update you!

@grant I think this would be a good flow for how it works:

clasp run myFunction .... runs function to find what scopes it needs (not sure if this is possible though), if none, then returns value. Otherwise, return something like:

> clasp run myFunction
You need to authorize x scopes, would you like to do that now? (Y/n) Y
Adding scopes and logging you out...
Please run `clasp login` now to re-login with these scopes.

It would add those scopes to the .appsscript.json file, (which /src/auth.ts should use to get scopes) and then log the user out. They could then login again to authorize those scopes.

grant commented 6 years ago

Setting up .clasprc.json

  1. @campionfellin For .clasprc.json, we probably should add more instructions on how to properly create/set up this file for clasp run. Ideally it would be automatic and you wouldn't modify any JSON.
  2. @jasonjurotich If sendMail requires authorizations (Gmail), then you'll need to login to clasp with those scopes enabled (the Gmail API send scope). Running other APIs isn't currently supported (without hacking the clasp source code).
    1. To be honest, this feature is still experimental in clasp and will be ready at a later state.

If your function is this:

function myFunction() {
  MailApp.sendEmail({ ... });
}

Running on script.google.com will prompt you for a Gmail API scope. ("Send email as you" OAuth dialog.)

Developer Flow

@campionfellin

Ideally, a developer shouldn't modify source code (and build clasp) to use clasp run. The command was added here: #104 as a good first step but I think I missed the bigger picture for the design of clasp run.

I realized that we're missing a way for a developer to use their own OAuth Client Settings (client ID/secret) when creating their own key. A user should really be able to (1) login with their own project credentials as well as (2) read from their own credentials depending on which folder they are in. I believe (2) is implemented (you can read local credentials if you somehow created them), but you must manually change the client settings in order to create your local credentials.

Ideally a user has this flow:

npm i -g @google/clasp
# Download OAuth Credentials to the Apps Script API into creds.json
clasp login --creds creds.json

--creds creds.json would imply --ownkey (so we can remove --ownkey in place of --creds creds.json

This way you get a creds.json would be through https://console.developers.google.com/ and using your script's Project ID (script.google.com/<project> > Resources > Cloud Platform project...). Ideally we have instructions on how to do this. This ensures you meet the requirements (like common Cloud Project) listed here. From there you create an OAuth Client ID/Secret that you can download to creds.json

I am working on an easier way (within Google) to create this creds.json file rather than the above method.

@campionfellin As for the run flow, detecting scopes would be ideal for a v2 run function. I can explain how Apps Script detects scopes in a different discussion, but it's pretty complicated.

Ref: https://github.com/google/clasp/issues/11#issuecomment-370682855


To detect existing auth'd scopes: https://developers.google.com/apis-explorer/#search/tokeninfo/m/oauth2/v2/oauth2.tokeninfo

To detect scopes that are needed:

IanWhalen commented 6 years ago

Hey all, two things:

1) Just wanted to vote for this with my specific use case. As I'm developing locally and testing it would be awesome if I could basically save what I'm doing and hit clasp push && clasp run main to see how my changes are doing. Then I could effectively never open the online editor to run my code.

2) Is it the case that this should work out of the box as long as you don't need any additional authorizations? I tried with a fresh project but was never able to get it to work. Just getting the same Requested entity was not found. error as originally reported.

jasonjurotich commented 6 years ago

Thanks IanWhalen, I was actually going to send a message to see how this was going. If there were a way to add the scopes and get them approved, that would be awesome, to solve this problem.

I will be more than happy to make a YouTube video on this as soon as we can run other things to help other.

dustinmichels commented 6 years ago

Hi! Thanks for all your great work on this. Any updates? What is the smoothest way to use clasp run?

jasonjurotich commented 6 years ago

Yes, are there any updates in regards to being able to use other scopes? That would really help a ton to run things from the terminal. Thanks!!

grant commented 6 years ago

I hope to get some progress on this feature at some point. However, it is complicated to implement and not the highest upvoted feature request (proxy support is high).

Pull requests or design ideas for this feature are also welcome.

jasonjurotich commented 6 years ago

Perfectly understood. I have the same problem on a lower level when people ask me for GAS scripts. Thanks for keeping an eye on it.

grant commented 6 years ago

So theres actually another way in which we can add scopes to clasp run.

If you run a script but it doesn't have permissions, the error actually states the permissions required to run the script in an interesting string within the API response:

{
  "done": true,
  "error": {
    "code": 3,
    "message": "ScriptError",
    "details": [
      {
        "@type": "type.googleapis.com/google.apps.script.v1.ExecutionError",
        "scriptStackTraceElements": [
          {
            "function": "sendMail",
            "lineNumber": 7
          }
        ],
        "errorMessage": "The script does not have permission to perform that action. Required permissions: (https://www.googleapis.com/auth/gmail.send || https://www.googleapis.com/auth/gmail.compose || https://www.googleapis.com/auth/gmail.modify || https://mail.google.com/ || https://www.googleapis.com/auth/gmail.addons.current.action.compose)",
        "errorType": "ScriptError"
      }
    ]
  }
}

Unfortunately it only reports one scope at a time.

We could possibly have the flow:

grant commented 5 years ago

Note: This issue could probably be closed with #411.

paxperscientiam commented 5 years ago

Qualitatively, I have to say this process is a huge pain in the butt. More substatively, and maybe this should be a separate issue, but the whole oauth process is only working with the Chrome browser: on both Firefox and Safari, I'm getting error messages when redirected to localhost.