blob: e8c3320474b3991c5730924998cb318516998480 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001#!/usr/bin/env python
2"""
3This is a helper script for making pRPC API calls during local development.
4
5Usage examples:
6
7To test an anonymous request to your own local monorail server:
81. Run 'make serve' in another shell
92. `./api/test_call monorail.Projects ListComponents
10 '{"project_name": "monorail", "include_admin_info": true}'`
11
12To test a signed in request to your own local monorail server:
131. Run 'make serve' in another shell
142. `./api/test_call monorail.Projects ListComponents
15 '{"project_name": "monorail", "include_admin_info": true}'
16 --test_account=test@example.com`
17Note that test account email address must always end in @example.com.
18
19To test an anonymous request to your monorail staging server:
201. Deploy your staging server version, e.g., 12345-76697e9-tainted-jrobbins.
212. Visit your staging server in a new incognito window and view source
22 to find the XSRF token for the anonymous user in JS var CS_env['token'].
233. `./api/test_call monorail.Projects ListComponents
24 '{"project_name": "monorail", "include_admin_info": true}'
25 --host=12345-76697e9-tainted-jrobbins-dot-monorail-staging.appspot.com
26 --xsrf-token='THE_ANON_TOKEN'`
27
28To test a signed-in request to your monorail staging server using
29the client_id for monorail-staging and your own account:
301. Make sure that you have a role in the monorail-staging project.
312. Have your account allowlisted by email address.
323. Download the monorail-staging app credientials via
33 `gcloud --project=monorail-staging auth login`.
344. `./api/test_call monorail.Projects ListComponents
35 '{"project_name": "monorail", "include_admin_info": true}'
36 --host=12345-76697e9-tainted-jrobbins-dot-monorail-staging.appspot.com
37 --use-app-credentials`
38
39To test a signed-in request to your monorail staging server using
40a service account client secrets file that you download:
41(Note: This is not recommended for prod because downloading secrets
42is a bad practice.)
431. Create a service account via the Cloud Console for any project.
44 Choose "IAM & Admin" > "Service accounts".
45 Press "+ Create Service Account".
46 Fill in the form and submit it to save a service account .json file
47 to your local disk. Keep this file private.
482. File an issue on /p/monorail to allowlist your client_id and/or
49 client_email. Or, author a CL yourself to add it to the allowlist.
503. `./api/test_call monorail.Projects ListComponents
51 '{"project_name": "monorail", "include_admin_info": true}'
52 --host=12345-76697e9-tainted-jrobbins-dot-monorail-staging.appspot.com
53 --service-account=FILENAME_OF_SERVICE_ACCOUNT_JSON_FILE`
54"""
55
56import argparse
57import errno
58import json
59import logging
Copybara854996b2021-09-07 19:36:02 +000060import sys
61
62
Copybara854996b2021-09-07 19:36:02 +000063import httplib2
64from oauth2client.client import GoogleCredentials
65
66
67URL_BASE = 'http://localhost:8080/prpc/'
68OAUTH_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
69
70def make_http(args):
71 """Return an httplib2.Http object, with or without oauth."""
72 http = httplib2.Http()
73 credentials = None
74 if args.use_app_credentials:
75 credentials = GoogleCredentials.get_application_default()
76 if args.service_account:
77 credentials = GoogleCredentials.from_stream(args.service_account)
78 logging.debug('Will request as user %r', credentials.service_account_email)
79
80 if credentials:
81 credentials = credentials.create_scoped([OAUTH_SCOPE])
82 logging.debug('Will request as client %r', credentials.client_id)
83 if not args.host:
84 print(('[ERROR] OAuth on localhost will always see user '
85 'example@example.com, so we do not support that.\n'
86 'Instead, add --server=YOUR_STAGING_SERVER, '
87 'or use --test_account=USER@example.com.'))
88 sys.exit(1)
89
90 http = credentials.authorize(http)
91
92 return http
93
94def make_call(service, method, json_body, args):
95 """Call the server and print the response contents."""
96 body = json.loads(json_body)
97
98 url_base = URL_BASE
99 if args.host:
100 url_base = 'https://%s/prpc/' % args.host
101 url = '%s%s/%s' % (url_base, service, method)
102 logging.debug('Request URL: %s', url)
103
104 http = make_http(args)
105 headers = {
106 'Content-Type': 'application/json',
107 'Accept': 'application/json',
108 }
109 if args.test_account:
110 headers['x-test-account'] = args.test_account
111 if args.xsrf_token:
112 headers['x-xsrf-token'] = args.xsrf_token
113 body = json.dumps(body)
114
115 logging.debug('Body: %r' % body)
116 try:
117 response, contents = http.request(
118 url, method='POST', body=body, headers=headers)
119 logging.info('Received response: %s', contents)
120 except httplib2.HttpLib2Error as e:
121 if hasattr(e.reason, 'errno') and e.reason.errno == errno.ECONNREFUSED:
122 print('[Error] Could not reach server. Is it running?')
123 else:
124 raise e
125
126
127if __name__ == '__main__':
128 parser = argparse.ArgumentParser(description='Process some integers.')
129 parser.add_argument('service', help='pRPC service name.')
130 parser.add_argument('method', help='pRPC method name.')
131 parser.add_argument('json_body', help='pRPC HTTP body in valid JSON.')
132 parser.add_argument('--test-account',
133 help='Test account to use, in the form of an email.')
134 parser.add_argument('--xsrf-token', help='Custom XSRF token.')
135 parser.add_argument('--host', help='remote server FQDN.')
136 parser.add_argument(
137 '--use-app-credentials',
138 help='Use credentials of a GAE app that you are signed into via gcloud.',
139 action='store_true')
140 parser.add_argument(
141 '--service-account', help='Service account credentials JSON file name.')
142 parser.add_argument('-v', '--verbose', action='store_true')
143 args = parser.parse_args()
144
145 if args.verbose:
146 log_level = logging.DEBUG
147 else:
148 log_level = logging.INFO
149 logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
150
151 make_call(args.service, args.method, args.json_body, args)