async def test_appsync_subscription_iam_not_allowed(event_loop, server): from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from gql.transport.exceptions import TransportQueryError from botocore.credentials import Credentials path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" dummy_credentials = Credentials( access_key=DUMMY_ACCESS_KEY_ID_NOT_ALLOWED, secret_key=DUMMY_SECRET_ACCESS_KEY, token=DUMMY_SECRET_SESSION_TOKEN, ) auth = AppSyncIAMAuthentication(host=server.hostname, credentials=dummy_credentials, region_name=REGION_NAME) transport = AppSyncWebsocketsTransport(url=url, auth=auth) client = Client(transport=transport) async with client as session: subscription = gql(on_create_message_subscription_str) with pytest.raises(TransportQueryError) as exc_info: async for result in session.subscribe(subscription): pass assert "Permission denied" in str(exc_info)
async def main(): # Should look like: # https://XXXXXXXXXXXXXXXXXXXXXXXXXX.appsync-api.REGION.amazonaws.com/graphql url = os.environ.get("AWS_GRAPHQL_API_ENDPOINT") if url is None: print("Missing environment variables") sys.exit() # Using implicit auth (IAM) transport = AppSyncWebsocketsTransport(url=url) async with Client(transport=transport) as session: subscription = gql(""" subscription onCreateMessage { onCreateMessage { message } } """) print("Waiting for messages...") async for result in session.subscribe(subscription): print(result)
async def main(): # Should look like: # https://XXXXXXXXXXXXXXXXXXXXXXXXXX.appsync-api.REGION.amazonaws.com/graphql url = os.environ.get("AWS_GRAPHQL_API_ENDPOINT") api_key = os.environ.get("AWS_GRAPHQL_API_KEY") if url is None or api_key is None: print("Missing environment variables") sys.exit() # Extract host from url host = str(urlparse(url).netloc) print(f"Host: {host}") auth = AppSyncApiKeyAuthentication(host=host, api_key=api_key) transport = AppSyncWebsocketsTransport(url=url, auth=auth) async with Client(transport=transport) as session: subscription = gql(""" subscription onCreateMessage { onCreateMessage { message } } """) print("Waiting for messages...") async for result in session.subscribe(subscription): print(result)
def test_appsync_init_with_iam_auth_and_no_region(caplog, fake_credentials_factory, fake_session_factory): """ WARNING: this test will fail if: - you have a default region set in ~/.aws/config - you have the AWS_DEFAULT_REGION environment variable set """ from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from botocore.exceptions import NoRegionError import logging caplog.set_level(logging.WARNING) with pytest.raises(NoRegionError): session = fake_session_factory(credentials=fake_credentials_factory()) session._region_name = None session._credentials.region = None transport = AppSyncWebsocketsTransport(url=mock_transport_url, session=session) # prints the region name in case the test fails print(f"Region found: {transport.auth._region_name}") print(f"Captured: {caplog.text}") expected_error = ( "Region name not found. " "It was not possible to detect your region either from the host " "or from your default AWS configuration.") assert expected_error in caplog.text
def test_appsync_init_with_iam_auth_without_creds(fake_session_factory): import botocore.exceptions from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport auth = AppSyncIAMAuthentication( host=mock_transport_host, session=fake_session_factory(credentials=None), ) with pytest.raises(botocore.exceptions.NoCredentialsError): AppSyncWebsocketsTransport(url=mock_transport_url, auth=auth)
def test_appsync_init_with_iam_auth_with_creds(fake_credentials_factory): from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport auth = AppSyncIAMAuthentication( host=mock_transport_host, credentials=fake_credentials_factory(), region_name="us-east-1", ) sample_transport = AppSyncWebsocketsTransport(url=mock_transport_url, auth=auth) assert sample_transport.auth is auth
def test_appsync_init_with_minimal_args(fake_session_factory): from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport sample_transport = AppSyncWebsocketsTransport( url=mock_transport_url, session=fake_session_factory()) assert isinstance(sample_transport.auth, AppSyncIAMAuthentication) assert sample_transport.connect_timeout == 10 assert sample_transport.close_timeout == 10 assert sample_transport.ack_timeout == 10 assert sample_transport.ssl is False assert sample_transport.connect_args == {}
def test_appsync_init_with_jwt_auth(): from gql.transport.appsync_auth import AppSyncJWTAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport auth = AppSyncJWTAuthentication(host=mock_transport_host, jwt="some-jwt") sample_transport = AppSyncWebsocketsTransport(url=mock_transport_url, auth=auth) assert sample_transport.auth is auth assert auth.get_headers() == { "host": mock_transport_host, "Authorization": "some-jwt", }
def test_appsync_init_with_apikey_auth(): from gql.transport.appsync_auth import AppSyncApiKeyAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport auth = AppSyncApiKeyAuthentication(host=mock_transport_host, api_key="some-api-key") sample_transport = AppSyncWebsocketsTransport(url=mock_transport_url, auth=auth) assert sample_transport.auth is auth assert auth.get_headers() == { "host": mock_transport_host, "x-api-key": "some-api-key", }
async def test_appsync_subscription_api_key(event_loop, server): from gql.transport.appsync_auth import AppSyncApiKeyAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" auth = AppSyncApiKeyAuthentication(host=server.hostname, api_key=DUMMY_API_KEY) transport = AppSyncWebsocketsTransport( url=url, auth=auth, keep_alive_timeout=(5 * SEND_MESSAGE_DELAY)) await default_transport_test(transport)
def test_appsync_init_with_no_credentials(caplog, fake_session_factory): import botocore.exceptions from gql.transport.appsync_websockets import AppSyncWebsocketsTransport with pytest.raises(botocore.exceptions.NoCredentialsError): sample_transport = AppSyncWebsocketsTransport( url=mock_transport_url, session=fake_session_factory(credentials=None), ) assert sample_transport.auth is None expected_error = "Credentials not found" print(f"Captured log: {caplog.text}") assert expected_error in caplog.text
async def test_appsync_subscription_variable_values_and_operation_name( event_loop, server): from gql.transport.appsync_auth import AppSyncApiKeyAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" auth = AppSyncApiKeyAuthentication(host=server.hostname, api_key=DUMMY_API_KEY) transport = AppSyncWebsocketsTransport( url=url, auth=auth, keep_alive_timeout=(5 * SEND_MESSAGE_DELAY)) client = Client(transport=transport) expected_messages = [ f"Hello world {number}!" for number in range(NB_MESSAGES) ] received_messages = [] async with client as session: subscription = gql(on_create_message_subscription_str) async for execution_result in session.subscribe( subscription, operation_name="onCreateMessage", variable_values={"key1": "val1"}, get_execution_result=True, ): result = execution_result.data message = result["onCreateMessage"]["message"] print(f"Message received: '{message}'") received_messages.append(message) print(f"extensions received: {execution_result.extensions}") assert execution_result.extensions[ "operation_name"] == "onCreateMessage" variables = execution_result.extensions["variables"] assert variables["key1"] == "val1" assert expected_messages == received_messages
def test_munge_url(fake_signer_factory, fake_request_factory): from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport test_url = "https://appsync-api.aws.example.org/some-other-params" auth = AppSyncIAMAuthentication( host=test_url, signer=fake_signer_factory(), request_creator=fake_request_factory, ) sample_transport = AppSyncWebsocketsTransport(url=test_url, auth=auth) header_string = ("eyJGYWtlQXV0aG9yaXphdGlvbiI6ImEiLCJGYWtlVGltZSI6InRvZGF5" "IiwiaG9zdCI6Imh0dHBzOi8vYXBwc3luYy1hcGkuYXdzLmV4YW1wbGUu" "b3JnL3NvbWUtb3RoZXItcGFyYW1zIn0=") expected_url = ("wss://appsync-realtime-api.aws.example.org/" f"some-other-params?header={header_string}&payload=e30=") assert sample_transport.url == expected_url
async def test_appsync_subscription_api_key_unauthorized(event_loop, server): from gql.transport.appsync_auth import AppSyncApiKeyAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from gql.transport.exceptions import TransportServerError path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" auth = AppSyncApiKeyAuthentication(host=server.hostname, api_key="invalid") transport = AppSyncWebsocketsTransport(url=url, auth=auth) client = Client(transport=transport) with pytest.raises(TransportServerError) as exc_info: async with client as _: pass assert "You are not authorized to make this call." in str(exc_info)
async def test_appsync_execute_method_not_allowed(event_loop, server): from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from botocore.credentials import Credentials path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" dummy_credentials = Credentials( access_key=DUMMY_ACCESS_KEY_ID, secret_key=DUMMY_SECRET_ACCESS_KEY, ) auth = AppSyncIAMAuthentication(host=server.hostname, credentials=dummy_credentials, region_name=REGION_NAME) transport = AppSyncWebsocketsTransport(url=url, auth=auth) client = Client(transport=transport) async with client as session: query = gql(""" mutation createMessage($message: String!) { createMessage(input: {message: $message}) { id message createdAt } }""") variable_values = {"message": "Hello world!"} with pytest.raises(AssertionError) as exc_info: await session.execute(query, variable_values=variable_values) assert ( "execute method is not allowed for AppSyncWebsocketsTransport " "because only subscriptions are allowed on the realtime endpoint." ) in str(exc_info)
async def test_appsync_subscription_iam_without_token(event_loop, server): from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from botocore.credentials import Credentials path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" dummy_credentials = Credentials( access_key=DUMMY_ACCESS_KEY_ID, secret_key=DUMMY_SECRET_ACCESS_KEY, ) auth = AppSyncIAMAuthentication(host=server.hostname, credentials=dummy_credentials, region_name=REGION_NAME) transport = AppSyncWebsocketsTransport(url=url, auth=auth) await default_transport_test(transport)
async def test_appsync_subscription_server_sending_an_error_without_an_id( event_loop, server): from gql.transport.appsync_auth import AppSyncApiKeyAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from gql.transport.exceptions import TransportServerError path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" auth = AppSyncApiKeyAuthentication(host=server.hostname, api_key=DUMMY_API_KEY) transport = AppSyncWebsocketsTransport(url=url, auth=auth) client = Client(transport=transport) with pytest.raises(TransportServerError) as exc_info: async with client as _: pass assert "Sometimes AppSync will send you an error without an id" in str( exc_info)
async def test_appsync_subscription_server_sending_a_not_json_answer( event_loop, server): from gql.transport.appsync_auth import AppSyncApiKeyAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from gql.transport.exceptions import TransportProtocolError path = "/graphql" url = f"ws://{server.hostname}:{server.port}{path}" auth = AppSyncApiKeyAuthentication(host=server.hostname, api_key=DUMMY_API_KEY) transport = AppSyncWebsocketsTransport(url=url, auth=auth) client = Client(transport=transport) with pytest.raises(TransportProtocolError) as exc_info: async with client as _: pass assert "Server did not return a GraphQL result: Something not json" in str( exc_info)
async def test_appsync_fetch_schema_from_transport_not_allowed(event_loop): from gql.transport.appsync_auth import AppSyncIAMAuthentication from gql.transport.appsync_websockets import AppSyncWebsocketsTransport from botocore.credentials import Credentials dummy_credentials = Credentials( access_key=DUMMY_ACCESS_KEY_ID, secret_key=DUMMY_SECRET_ACCESS_KEY, ) auth = AppSyncIAMAuthentication(host="something", credentials=dummy_credentials, region_name=REGION_NAME) transport = AppSyncWebsocketsTransport(url="https://something", auth=auth) with pytest.raises(AssertionError) as exc_info: Client(transport=transport, fetch_schema_from_transport=True) assert ( "fetch_schema_from_transport=True is not allowed for AppSyncWebsocketsTransport" " because only subscriptions are allowed on the realtime endpoint." ) in str(exc_info)
def get_transport(args: Namespace) -> Optional[AsyncTransport]: """Instantiate a transport from the parsed command line arguments :param args: parsed command line arguments """ # Get the url scheme from server parameter url = URL(args.server) # Validate scheme if url.scheme not in ["http", "https", "ws", "wss"]: raise ValueError("URL protocol should be one of: http, https, ws, wss") # Get extra transport parameters from command line arguments # (headers) transport_args = get_transport_args(args) # Either use the requested transport or autodetect it if args.transport == "auto": transport_name = autodetect_transport(url) else: transport_name = args.transport # Import the correct transport class depending on the transport name if transport_name == "aiohttp": from gql.transport.aiohttp import AIOHTTPTransport return AIOHTTPTransport(url=args.server, **transport_args) elif transport_name == "phoenix": from gql.transport.phoenix_channel_websockets import ( PhoenixChannelWebsocketsTransport, ) return PhoenixChannelWebsocketsTransport(url=args.server, **transport_args) elif transport_name == "websockets": from gql.transport.websockets import WebsocketsTransport transport_args["ssl"] = url.scheme == "wss" return WebsocketsTransport(url=args.server, **transport_args) else: from gql.transport.appsync_auth import AppSyncAuthentication assert transport_name in ["appsync_http", "appsync_websockets"] assert url.host is not None auth: AppSyncAuthentication if args.api_key: from gql.transport.appsync_auth import AppSyncApiKeyAuthentication auth = AppSyncApiKeyAuthentication(host=url.host, api_key=args.api_key) elif args.jwt: from gql.transport.appsync_auth import AppSyncJWTAuthentication auth = AppSyncJWTAuthentication(host=url.host, jwt=args.jwt) else: from gql.transport.appsync_auth import AppSyncIAMAuthentication from botocore.exceptions import NoRegionError try: auth = AppSyncIAMAuthentication(host=url.host) except NoRegionError: # A warning message has been printed in the console return None transport_args["auth"] = auth if transport_name == "appsync_http": from gql.transport.aiohttp import AIOHTTPTransport return AIOHTTPTransport(url=args.server, **transport_args) else: from gql.transport.appsync_websockets import AppSyncWebsocketsTransport try: return AppSyncWebsocketsTransport(url=args.server, **transport_args) except Exception: # This is for the NoCredentialsError but we cannot import it here return None