1

I'm trying to run a script using google-apps-script RESTful API. When the script is being executed, I always get an error saying Script function not found: function_name. Where is it that I'm going wrong?

This is for a new project where there is a need for me to run the script from a Django application. So I'm trying to call the script run function once the user has authenticated (using Google OAuth2). Just before trying to run the script, I'm updating the script which is working without any problem at all.

I've taken the flask application from https://developers.google.com/api-client-library/python/auth/web-app and converted it into DRF

# Default imports
import base64
import google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery

from django.shortcuts import redirect, reverse
from rest_framework import generics, status
from rest_framework.response import Response

CLIENT_SECRETS_FILE = "credentials.json"

SCOPES = [
    "https://www.googleapis.com/auth/script.projects",
    "https://www.googleapis.com/auth/script.deployments",
    "https://www.googleapis.com/auth/forms",
    "https://www.googleapis.com/auth/forms.currentonly",
    "https://www.googleapis.com/auth/script.processes",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/script.scriptapp",
    "https://www.googleapis.com/auth/drive",
]


def get_form_template(param1, param2):
    # Google Script code template being generated
    # using param1 and param2
    return (
        """
        // executionScript function
        function executionScript() {
            var param1 = '""" + param1 + """'
            var param2 = '""" + param2 + """'

            Logger.log("param1 is : " + param1)
            Logger.log("param2 is : " + param2)

            // And some extra functionality
            // that is why all the extra scopes in
            // the manifest
        }
        """.strip()
    )


def get_manifest():
    return """
        {
            "timeZone": "Asia/Kolkata",
            "exceptionLogging": "STACKDRIVER",
            "oauthScopes": [
                "https://www.googleapis.com/auth/script.external_request",
                "https://www.googleapis.com/auth/script.scriptapp",
                "https://www.googleapis.com/auth/script.projects",
                "https://www.googleapis.com/auth/script.deployments",
                "https://www.googleapis.com/auth/forms",
                "https://www.googleapis.com/auth/forms.currentonly",
                "https://www.googleapis.com/auth/script.processes",
                "https://www.googleapis.com/auth/drive"
            ]
        }
    """.strip()


class TestAPIPage(generics.ListAPIView):
    def get(self, request, *args, **kwargs):

        #################################################################
        # Code to make sure param1 and param2 are not null or undefined
        #################################################################

        param1 = self.request.query_params.get("param1")
        param2 = self.request.query_params.get("param2")

        if "credentials" not in self.request.session:
            return redirect(
                reverse("oauth2_google:authorize")
                + "?param1={}&param2={}".format(param1, param2)
            )

        # Load credentials from the session.
        credentials = google.oauth2.credentials.Credentials(
            **self.request.session["credentials"]
        )

        # Save credentials back to session in case access token was refreshed.
        # ACTION ITEM: In a production app, you likely want to save these
        #              credentials in a persistent database instead.
        self.request.session["credentials"] = credentials_to_dict(credentials)

        service = googleapiclient.discovery.build(
            "script", "v1", credentials=credentials
        )

        SCRIPT_ID = " ################## SCRIPT_ID ################## "

        # Checking if project exists
        project_get_response = service.projects().get(scriptId=SCRIPT_ID).execute()
        print("Project get response : ", project_get_response)

        update_request = {
            "files": [
                {
                    "name": "Code",
                    "type": "SERVER_JS",
                    "source": get_form_template(param1, param2),
                },
                {"name": "appsscript", "type": "JSON", "source": get_manifest()},
            ]
        }

        project_update_response = service.projects().updateContent(body=update_request, scriptId=SCRIPT_ID).execute()
        print("Project Update Response : ", project_update_response)

        ##########################################
        ## Till here code runs without a problem
        ##########################################

        execute_request = {"function": "executionScript"}

        try:
            ###############################################
            ## Exception raised below, whose response is
            # {
            #     'done': True, 
            #     'error': {
            #         'code': 3, 
            #         'message': 'ScriptError', 
            #         'details': [
            #             {
            #                 '@type': 'type.googleapis.com/google.apps.script.v1.ExecutionError', 
            #                 'errorMessage': 'Script function not found: executionScript', 
            #                 'errorType': 'ScriptError'
            #             }
            #         ]
            #     }
            # }
            ###############################################
            execute_response = (
                service.scripts()
                .run(body=execute_request, scriptId=SCRIPT_ID)
                .execute()
            )

            print("Response when exexuting script is : ", execute_response)

            return Response(
                {"status": 200, "content": "Successfully executed the script"},
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            # The API encountered a problem before the script started executing.
            print("Exception raised is : ", e)


class AuthorizePage(generics.ListAPIView):
    def get(self, request, *args, **kwargs):

        param1 = self.request.query_params.get("param1")
        param2 = self.request.query_params.get("param2")

        # Store the state so the callback can verify the auth server response.
        bytes_string = "?param1={}&param2={}".format(param1, param2)
        bytes_string = bytes_string.encode()
        encoded_state = base64.b64encode(bytes_string)
        encoded_state_string = encoded_state.decode("UTF-8")

        self.request.session["state"] = encoded_state_string

        # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
        flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
            CLIENT_SECRETS_FILE, scopes=SCOPES, state=encoded_state_string
        )

        flow.redirect_uri = request.build_absolute_uri(
            reverse("oauth2_google:oauth2callback")
        )

        authorization_url, _ = flow.authorization_url(
            # Enable offline access so that you can refresh an access token without
            # re-prompting the user for permission. Recommended for web server apps.
            access_type="offline",
            # Enable incremental authorization. Recommended as the best practice.
            include_granted_scopes="false",
        )

        return redirect(authorization_url)


class OAuth2CallbackPage(generics.ListAPIView):
    def get(self, request, *ars, **kwargs):
        # Specify the state when creating the flow in the callback so that it can
        # verified in the authorization server response.
        state = self.request.session["state"]

        decoded_state_bytes = state.encode()
        decoded_bytes = base64.b64decode(decoded_state_bytes)
        decoded_string = decoded_bytes.decode("UTF-8")

        flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
            CLIENT_SECRETS_FILE, scopes=SCOPES, state=state
        )
        flow.redirect_uri = request.build_absolute_uri(
            reverse("oauth2_google:oauth2callback")
        )

        # Use the authorization server's response to fetch the OAuth 2.0 tokens.
        authorization_response = request.build_absolute_uri()
        flow.fetch_token(authorization_response=authorization_response)

        # Store credentials in the session.
        # ACTION ITEM: In a production app, you likely want to save these
        #              credentials in a persistent database instead.
        credentials = flow.credentials
        self.request.session["credentials"] = credentials_to_dict(credentials)

        return redirect(reverse("oauth2_google:test_api_request") + decoded_string)


def credentials_to_dict(credentials):
    return {
        "token": credentials.token,
        "refresh_token": credentials.refresh_token,
        "token_uri": credentials.token_uri,
        "client_id": credentials.client_id,
        "client_secret": credentials.client_secret,
        "scopes": credentials.scopes,
    }

All I can see in the Apps Script Web Page is the errors that code isn't being executed

google-apps-script-image

Stackdriver is also not showing what the error is, and why the code is not being executed

Since I'm able to access the Script API and update the code successfully without errors, I think using the same Auth token, I should be able to execute the script as well. Please help out pointing the error.

I even tried to execute the script independently in a separate python file using Google App Script QuickStart Example but even that gave same error

0 Answers0