Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/api/test_call b/api/test_call
new file mode 100755
index 0000000..5455241
--- /dev/null
+++ b/api/test_call
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+"""
+This is a helper script for making pRPC API calls during local development.
+
+Usage examples:
+
+To test an anonymous request to your own local monorail server:
+1. Run 'make serve' in another shell
+2. `./api/test_call monorail.Projects ListComponents
+ '{"project_name": "monorail", "include_admin_info": true}'`
+
+To test a signed in request to your own local monorail server:
+1. Run 'make serve' in another shell
+2. `./api/test_call monorail.Projects ListComponents
+ '{"project_name": "monorail", "include_admin_info": true}'
+ --test_account=test@example.com`
+Note that test account email address must always end in @example.com.
+
+To test an anonymous request to your monorail staging server:
+1. Deploy your staging server version, e.g., 12345-76697e9-tainted-jrobbins.
+2. Visit your staging server in a new incognito window and view source
+ to find the XSRF token for the anonymous user in JS var CS_env['token'].
+3. `./api/test_call monorail.Projects ListComponents
+ '{"project_name": "monorail", "include_admin_info": true}'
+ --host=12345-76697e9-tainted-jrobbins-dot-monorail-staging.appspot.com
+ --xsrf-token='THE_ANON_TOKEN'`
+
+To test a signed-in request to your monorail staging server using
+the client_id for monorail-staging and your own account:
+1. Make sure that you have a role in the monorail-staging project.
+2. Have your account allowlisted by email address.
+3. Download the monorail-staging app credientials via
+ `gcloud --project=monorail-staging auth login`.
+4. `./api/test_call monorail.Projects ListComponents
+ '{"project_name": "monorail", "include_admin_info": true}'
+ --host=12345-76697e9-tainted-jrobbins-dot-monorail-staging.appspot.com
+ --use-app-credentials`
+
+To test a signed-in request to your monorail staging server using
+a service account client secrets file that you download:
+(Note: This is not recommended for prod because downloading secrets
+is a bad practice.)
+1. Create a service account via the Cloud Console for any project.
+ Choose "IAM & Admin" > "Service accounts".
+ Press "+ Create Service Account".
+ Fill in the form and submit it to save a service account .json file
+ to your local disk. Keep this file private.
+2. File an issue on /p/monorail to allowlist your client_id and/or
+ client_email. Or, author a CL yourself to add it to the allowlist.
+3. `./api/test_call monorail.Projects ListComponents
+ '{"project_name": "monorail", "include_admin_info": true}'
+ --host=12345-76697e9-tainted-jrobbins-dot-monorail-staging.appspot.com
+ --service-account=FILENAME_OF_SERVICE_ACCOUNT_JSON_FILE`
+"""
+
+import argparse
+import errno
+import json
+import logging
+import os
+import sys
+
+
+monorail_dir = os.path.dirname(os.path.abspath(__file__ + '/..'))
+third_party_path = os.path.join(monorail_dir, 'third_party')
+if third_party_path not in sys.path:
+ sys.path.insert(0, third_party_path)
+
+import httplib2
+from oauth2client.client import GoogleCredentials
+
+
+URL_BASE = 'http://localhost:8080/prpc/'
+OAUTH_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
+
+def make_http(args):
+ """Return an httplib2.Http object, with or without oauth."""
+ http = httplib2.Http()
+ credentials = None
+ if args.use_app_credentials:
+ credentials = GoogleCredentials.get_application_default()
+ if args.service_account:
+ credentials = GoogleCredentials.from_stream(args.service_account)
+ logging.debug('Will request as user %r', credentials.service_account_email)
+
+ if credentials:
+ credentials = credentials.create_scoped([OAUTH_SCOPE])
+ logging.debug('Will request as client %r', credentials.client_id)
+ if not args.host:
+ print(('[ERROR] OAuth on localhost will always see user '
+ 'example@example.com, so we do not support that.\n'
+ 'Instead, add --server=YOUR_STAGING_SERVER, '
+ 'or use --test_account=USER@example.com.'))
+ sys.exit(1)
+
+ http = credentials.authorize(http)
+
+ return http
+
+def make_call(service, method, json_body, args):
+ """Call the server and print the response contents."""
+ body = json.loads(json_body)
+
+ url_base = URL_BASE
+ if args.host:
+ url_base = 'https://%s/prpc/' % args.host
+ url = '%s%s/%s' % (url_base, service, method)
+ logging.debug('Request URL: %s', url)
+
+ http = make_http(args)
+ headers = {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ }
+ if args.test_account:
+ headers['x-test-account'] = args.test_account
+ if args.xsrf_token:
+ headers['x-xsrf-token'] = args.xsrf_token
+ body = json.dumps(body)
+
+ logging.debug('Body: %r' % body)
+ try:
+ response, contents = http.request(
+ url, method='POST', body=body, headers=headers)
+ logging.info('Received response: %s', contents)
+ except httplib2.HttpLib2Error as e:
+ if hasattr(e.reason, 'errno') and e.reason.errno == errno.ECONNREFUSED:
+ print('[Error] Could not reach server. Is it running?')
+ else:
+ raise e
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Process some integers.')
+ parser.add_argument('service', help='pRPC service name.')
+ parser.add_argument('method', help='pRPC method name.')
+ parser.add_argument('json_body', help='pRPC HTTP body in valid JSON.')
+ parser.add_argument('--test-account',
+ help='Test account to use, in the form of an email.')
+ parser.add_argument('--xsrf-token', help='Custom XSRF token.')
+ parser.add_argument('--host', help='remote server FQDN.')
+ parser.add_argument(
+ '--use-app-credentials',
+ help='Use credentials of a GAE app that you are signed into via gcloud.',
+ action='store_true')
+ parser.add_argument(
+ '--service-account', help='Service account credentials JSON file name.')
+ parser.add_argument('-v', '--verbose', action='store_true')
+ args = parser.parse_args()
+
+ if args.verbose:
+ log_level = logging.DEBUG
+ else:
+ log_level = logging.INFO
+ logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
+
+ make_call(args.service, args.method, args.json_body, args)