2
2
import logging
3
3
import sys
4
4
from argparse import ArgumentParser , Namespace , RawDescriptionHelpFormatter
5
- from typing import Any , Dict
5
+ from typing import Any , Dict , Optional
6
6
7
7
from graphql import GraphQLError , print_schema
8
8
from yarl import URL
@@ -101,6 +101,43 @@ def get_parser(with_examples: bool = False) -> ArgumentParser:
101
101
action = "store_true" ,
102
102
dest = "print_schema" ,
103
103
)
104
+ parser .add_argument (
105
+ "--transport" ,
106
+ default = "auto" ,
107
+ choices = [
108
+ "auto" ,
109
+ "aiohttp" ,
110
+ "phoenix" ,
111
+ "websockets" ,
112
+ "appsync_http" ,
113
+ "appsync_websockets" ,
114
+ ],
115
+ help = (
116
+ "select the transport. 'auto' by default: "
117
+ "aiohttp or websockets depending on url scheme"
118
+ ),
119
+ dest = "transport" ,
120
+ )
121
+
122
+ appsync_description = """
123
+ By default, for an AppSync backend, the IAM authentication is chosen.
124
+
125
+ If you want API key or JWT authentication, you can provide one of the
126
+ following arguments:"""
127
+
128
+ appsync_group = parser .add_argument_group (
129
+ "AWS AppSync options" , description = appsync_description
130
+ )
131
+
132
+ appsync_auth_group = appsync_group .add_mutually_exclusive_group ()
133
+
134
+ appsync_auth_group .add_argument (
135
+ "--api-key" , help = "Provide an API key for authentication" , dest = "api_key" ,
136
+ )
137
+
138
+ appsync_auth_group .add_argument (
139
+ "--jwt" , help = "Provide an JSON Web token for authentication" , dest = "jwt" ,
140
+ )
104
141
105
142
return parser
106
143
@@ -191,36 +228,106 @@ def get_execute_args(args: Namespace) -> Dict[str, Any]:
191
228
return execute_args
192
229
193
230
194
- def get_transport (args : Namespace ) -> AsyncTransport :
231
+ def autodetect_transport (url : URL ) -> str :
232
+ """Detects which transport should be used depending on url."""
233
+
234
+ if url .scheme in ["ws" , "wss" ]:
235
+ transport_name = "websockets"
236
+
237
+ else :
238
+ assert url .scheme in ["http" , "https" ]
239
+ transport_name = "aiohttp"
240
+
241
+ return transport_name
242
+
243
+
244
+ def get_transport (args : Namespace ) -> Optional [AsyncTransport ]:
195
245
"""Instantiate a transport from the parsed command line arguments
196
246
197
247
:param args: parsed command line arguments
198
248
"""
199
249
200
250
# Get the url scheme from server parameter
201
251
url = URL (args .server )
202
- scheme = url .scheme
252
+
253
+ # Validate scheme
254
+ if url .scheme not in ["http" , "https" , "ws" , "wss" ]:
255
+ raise ValueError ("URL protocol should be one of: http, https, ws, wss" )
203
256
204
257
# Get extra transport parameters from command line arguments
205
258
# (headers)
206
259
transport_args = get_transport_args (args )
207
260
208
- # Instantiate transport depending on url scheme
209
- transport : AsyncTransport
210
- if scheme in ["ws" , "wss" ]:
211
- from gql .transport .websockets import WebsocketsTransport
261
+ # Either use the requested transport or autodetect it
262
+ if args .transport == "auto" :
263
+ transport_name = autodetect_transport (url )
264
+ else :
265
+ transport_name = args .transport
212
266
213
- transport = WebsocketsTransport (
214
- url = args .server , ssl = (scheme == "wss" ), ** transport_args
215
- )
216
- elif scheme in ["http" , "https" ]:
267
+ # Import the correct transport class depending on the transport name
268
+ if transport_name == "aiohttp" :
217
269
from gql .transport .aiohttp import AIOHTTPTransport
218
270
219
- transport = AIOHTTPTransport (url = args .server , ** transport_args )
271
+ return AIOHTTPTransport (url = args .server , ** transport_args )
272
+
273
+ elif transport_name == "phoenix" :
274
+ from gql .transport .phoenix_channel_websockets import (
275
+ PhoenixChannelWebsocketsTransport ,
276
+ )
277
+
278
+ return PhoenixChannelWebsocketsTransport (url = args .server , ** transport_args )
279
+
280
+ elif transport_name == "websockets" :
281
+ from gql .transport .websockets import WebsocketsTransport
282
+
283
+ transport_args ["ssl" ] = url .scheme == "wss"
284
+
285
+ return WebsocketsTransport (url = args .server , ** transport_args )
286
+
220
287
else :
221
- raise ValueError ("URL protocol should be one of: http, https, ws, wss" )
222
288
223
- return transport
289
+ from gql .transport .appsync_auth import AppSyncAuthentication
290
+
291
+ assert transport_name in ["appsync_http" , "appsync_websockets" ]
292
+ assert url .host is not None
293
+
294
+ auth : AppSyncAuthentication
295
+
296
+ if args .api_key :
297
+ from gql .transport .appsync_auth import AppSyncApiKeyAuthentication
298
+
299
+ auth = AppSyncApiKeyAuthentication (host = url .host , api_key = args .api_key )
300
+
301
+ elif args .jwt :
302
+ from gql .transport .appsync_auth import AppSyncJWTAuthentication
303
+
304
+ auth = AppSyncJWTAuthentication (host = url .host , jwt = args .jwt )
305
+
306
+ else :
307
+ from gql .transport .appsync_auth import AppSyncIAMAuthentication
308
+ from botocore .exceptions import NoRegionError
309
+
310
+ try :
311
+ auth = AppSyncIAMAuthentication (host = url .host )
312
+ except NoRegionError :
313
+ # A warning message has been printed in the console
314
+ return None
315
+
316
+ transport_args ["auth" ] = auth
317
+
318
+ if transport_name == "appsync_http" :
319
+ from gql .transport .aiohttp import AIOHTTPTransport
320
+
321
+ return AIOHTTPTransport (url = args .server , ** transport_args )
322
+
323
+ else :
324
+ from gql .transport .appsync_websockets import AppSyncWebsocketsTransport
325
+
326
+ try :
327
+ return AppSyncWebsocketsTransport (url = args .server , ** transport_args )
328
+ except Exception :
329
+ # This is for the NoCredentialsError but we cannot import it here
330
+ return None
224
331
225
332
226
333
async def main (args : Namespace ) -> int :
@@ -238,13 +345,16 @@ async def main(args: Namespace) -> int:
238
345
# Instantiate transport from command line arguments
239
346
transport = get_transport (args )
240
347
348
+ if transport is None :
349
+ return 1
350
+
241
351
# Get extra execute parameters from command line arguments
242
352
# (variables, operation_name)
243
353
execute_args = get_execute_args (args )
244
354
245
355
except ValueError as e :
246
356
print (f"Error: { e } " , file = sys .stderr )
247
- sys . exit ( 1 )
357
+ return 1
248
358
249
359
# By default, the exit_code is 0 (everything is ok)
250
360
exit_code = 0
0 commit comments