AppSync - initial implementation (#4761)
This commit is contained in:
parent
d118d592ca
commit
f3313be991
@ -162,6 +162,61 @@
|
||||
- [X] register_scalable_target
|
||||
</details>
|
||||
|
||||
## appsync
|
||||
<details>
|
||||
<summary>30% implemented</summary>
|
||||
|
||||
- [ ] associate_api
|
||||
- [ ] create_api_cache
|
||||
- [X] create_api_key
|
||||
- [ ] create_data_source
|
||||
- [ ] create_domain_name
|
||||
- [ ] create_function
|
||||
- [X] create_graphql_api
|
||||
- [ ] create_resolver
|
||||
- [ ] create_type
|
||||
- [ ] delete_api_cache
|
||||
- [X] delete_api_key
|
||||
- [ ] delete_data_source
|
||||
- [ ] delete_domain_name
|
||||
- [ ] delete_function
|
||||
- [X] delete_graphql_api
|
||||
- [ ] delete_resolver
|
||||
- [ ] delete_type
|
||||
- [ ] disassociate_api
|
||||
- [ ] flush_api_cache
|
||||
- [ ] get_api_association
|
||||
- [ ] get_api_cache
|
||||
- [ ] get_data_source
|
||||
- [ ] get_domain_name
|
||||
- [ ] get_function
|
||||
- [X] get_graphql_api
|
||||
- [ ] get_introspection_schema
|
||||
- [ ] get_resolver
|
||||
- [X] get_schema_creation_status
|
||||
- [X] get_type
|
||||
- [X] list_api_keys
|
||||
- [ ] list_data_sources
|
||||
- [ ] list_domain_names
|
||||
- [ ] list_functions
|
||||
- [X] list_graphql_apis
|
||||
- [ ] list_resolvers
|
||||
- [ ] list_resolvers_by_function
|
||||
- [X] list_tags_for_resource
|
||||
- [ ] list_types
|
||||
- [X] start_schema_creation
|
||||
- [X] tag_resource
|
||||
- [X] untag_resource
|
||||
- [ ] update_api_cache
|
||||
- [X] update_api_key
|
||||
- [ ] update_data_source
|
||||
- [ ] update_domain_name
|
||||
- [ ] update_function
|
||||
- [X] update_graphql_api
|
||||
- [ ] update_resolver
|
||||
- [ ] update_type
|
||||
</details>
|
||||
|
||||
## athena
|
||||
<details>
|
||||
<summary>20% implemented</summary>
|
||||
@ -5132,7 +5187,6 @@
|
||||
- appmesh
|
||||
- apprunner
|
||||
- appstream
|
||||
- appsync
|
||||
- auditmanager
|
||||
- autoscaling-plans
|
||||
- backup
|
||||
|
87
docs/docs/services/appsync.rst
Normal file
87
docs/docs/services/appsync.rst
Normal file
@ -0,0 +1,87 @@
|
||||
.. _implementedservice_appsync:
|
||||
|
||||
.. |start-h3| raw:: html
|
||||
|
||||
<h3>
|
||||
|
||||
.. |end-h3| raw:: html
|
||||
|
||||
</h3>
|
||||
|
||||
=======
|
||||
appsync
|
||||
=======
|
||||
|
||||
.. autoclass:: moto.appsync.models.AppSyncBackend
|
||||
|
||||
|start-h3| Example usage |end-h3|
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
@mock_appsync
|
||||
def test_appsync_behaviour:
|
||||
boto3.client("appsync")
|
||||
...
|
||||
|
||||
|
||||
|
||||
|start-h3| Implemented features for this service |end-h3|
|
||||
|
||||
- [ ] associate_api
|
||||
- [ ] create_api_cache
|
||||
- [X] create_api_key
|
||||
- [ ] create_data_source
|
||||
- [ ] create_domain_name
|
||||
- [ ] create_function
|
||||
- [X] create_graphql_api
|
||||
- [ ] create_resolver
|
||||
- [ ] create_type
|
||||
- [ ] delete_api_cache
|
||||
- [X] delete_api_key
|
||||
- [ ] delete_data_source
|
||||
- [ ] delete_domain_name
|
||||
- [ ] delete_function
|
||||
- [X] delete_graphql_api
|
||||
- [ ] delete_resolver
|
||||
- [ ] delete_type
|
||||
- [ ] disassociate_api
|
||||
- [ ] flush_api_cache
|
||||
- [ ] get_api_association
|
||||
- [ ] get_api_cache
|
||||
- [ ] get_data_source
|
||||
- [ ] get_domain_name
|
||||
- [ ] get_function
|
||||
- [X] get_graphql_api
|
||||
- [ ] get_introspection_schema
|
||||
- [ ] get_resolver
|
||||
- [X] get_schema_creation_status
|
||||
- [X] get_type
|
||||
- [X] list_api_keys
|
||||
|
||||
Pagination or the maxResults-parameter have not yet been implemented.
|
||||
|
||||
|
||||
- [ ] list_data_sources
|
||||
- [ ] list_domain_names
|
||||
- [ ] list_functions
|
||||
- [X] list_graphql_apis
|
||||
|
||||
Pagination or the maxResults-parameter have not yet been implemented.
|
||||
|
||||
|
||||
- [ ] list_resolvers
|
||||
- [ ] list_resolvers_by_function
|
||||
- [X] list_tags_for_resource
|
||||
- [ ] list_types
|
||||
- [X] start_schema_creation
|
||||
- [X] tag_resource
|
||||
- [X] untag_resource
|
||||
- [ ] update_api_cache
|
||||
- [X] update_api_key
|
||||
- [ ] update_data_source
|
||||
- [ ] update_domain_name
|
||||
- [ ] update_function
|
||||
- [X] update_graphql_api
|
||||
- [ ] update_resolver
|
||||
- [ ] update_type
|
||||
|
@ -27,6 +27,7 @@ def lazy_load(
|
||||
mock_acm = lazy_load(".acm", "mock_acm")
|
||||
mock_apigateway = lazy_load(".apigateway", "mock_apigateway")
|
||||
mock_apigateway_deprecated = lazy_load(".apigateway", "mock_apigateway_deprecated")
|
||||
mock_appsync = lazy_load(".appsync", "mock_appsync", boto3_name="appsync")
|
||||
mock_athena = lazy_load(".athena", "mock_athena")
|
||||
mock_applicationautoscaling = lazy_load(
|
||||
".applicationautoscaling", "mock_applicationautoscaling"
|
||||
|
5
moto/appsync/__init__.py
Normal file
5
moto/appsync/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""appsync module initialization; sets value for base decorator."""
|
||||
from .models import appsync_backends
|
||||
from ..core.models import base_decorator
|
||||
|
||||
mock_appsync = base_decorator(appsync_backends)
|
16
moto/appsync/exceptions.py
Normal file
16
moto/appsync/exceptions.py
Normal file
@ -0,0 +1,16 @@
|
||||
import json
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
|
||||
|
||||
class AppSyncExceptions(JsonRESTError):
|
||||
pass
|
||||
|
||||
|
||||
class GraphqlAPINotFound(AppSyncExceptions):
|
||||
code = 404
|
||||
|
||||
def __init__(self, api_id):
|
||||
super().__init__(
|
||||
"NotFoundException", f"GraphQL API {api_id} not found.",
|
||||
)
|
||||
self.description = json.dumps({"message": self.message})
|
309
moto/appsync/models.py
Normal file
309
moto/appsync/models.py
Normal file
@ -0,0 +1,309 @@
|
||||
import base64
|
||||
from datetime import timedelta, datetime, timezone
|
||||
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
|
||||
from moto.core.utils import BackendDict, unix_time
|
||||
from moto.utilities.tagging_service import TaggingService
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from .exceptions import GraphqlAPINotFound
|
||||
|
||||
|
||||
class GraphqlSchema(BaseModel):
|
||||
def __init__(self, definition):
|
||||
self.definition = definition
|
||||
# [graphql.language.ast.ObjectTypeDefinitionNode, ..]
|
||||
self.types = []
|
||||
|
||||
self.status = "PROCESSING"
|
||||
self.parse_error = None
|
||||
self._parse_graphql_definition()
|
||||
|
||||
def get_type(self, name):
|
||||
for graphql_type in self.types:
|
||||
if graphql_type.name.value == name:
|
||||
return {
|
||||
"name": name,
|
||||
"description": graphql_type.description.value
|
||||
if graphql_type.description
|
||||
else None,
|
||||
"arn": f"arn:aws:appsync:graphql_type/{name}",
|
||||
"definition": "NotYetImplemented",
|
||||
}
|
||||
|
||||
def get_status(self):
|
||||
return self.status, self.parse_error
|
||||
|
||||
def _parse_graphql_definition(self):
|
||||
try:
|
||||
from graphql import parse
|
||||
from graphql.language.ast import ObjectTypeDefinitionNode
|
||||
from graphql.error.graphql_error import GraphQLError
|
||||
|
||||
res = parse(self.definition)
|
||||
for definition in res.definitions:
|
||||
if isinstance(definition, ObjectTypeDefinitionNode):
|
||||
self.types.append(definition)
|
||||
self.status = "SUCCESS"
|
||||
except GraphQLError as e:
|
||||
self.status = "FAILED"
|
||||
self.parse_error = str(e)
|
||||
|
||||
|
||||
class GraphqlAPI(BaseModel):
|
||||
def __init__(
|
||||
self,
|
||||
region,
|
||||
name,
|
||||
authentication_type,
|
||||
additional_authentication_providers,
|
||||
log_config,
|
||||
xray_enabled,
|
||||
user_pool_config,
|
||||
open_id_connect_config,
|
||||
lambda_authorizer_config,
|
||||
):
|
||||
self.region = region
|
||||
self.name = name
|
||||
self.api_id = str(uuid4())
|
||||
self.authentication_type = authentication_type
|
||||
self.additional_authentication_providers = additional_authentication_providers
|
||||
self.lambda_authorizer_config = lambda_authorizer_config
|
||||
self.log_config = log_config
|
||||
self.open_id_connect_config = open_id_connect_config
|
||||
self.user_pool_config = user_pool_config
|
||||
self.xray_enabled = xray_enabled
|
||||
|
||||
self.arn = f"arn:aws:appsync:{self.region}:{ACCOUNT_ID}:apis/{self.api_id}"
|
||||
self.graphql_schema = None
|
||||
|
||||
self.api_keys = dict()
|
||||
|
||||
def update(
|
||||
self,
|
||||
name,
|
||||
additional_authentication_providers,
|
||||
authentication_type,
|
||||
lambda_authorizer_config,
|
||||
log_config,
|
||||
open_id_connect_config,
|
||||
user_pool_config,
|
||||
xray_enabled,
|
||||
):
|
||||
if name:
|
||||
self.name = name
|
||||
if additional_authentication_providers:
|
||||
self.additional_authentication_providers = (
|
||||
additional_authentication_providers
|
||||
)
|
||||
if authentication_type:
|
||||
self.authentication_type = authentication_type
|
||||
if lambda_authorizer_config:
|
||||
self.lambda_authorizer_config = lambda_authorizer_config
|
||||
if log_config:
|
||||
self.log_config = log_config
|
||||
if open_id_connect_config:
|
||||
self.open_id_connect_config = open_id_connect_config
|
||||
if user_pool_config:
|
||||
self.user_pool_config = user_pool_config
|
||||
if xray_enabled is not None:
|
||||
self.xray_enabled = xray_enabled
|
||||
|
||||
def create_api_key(self, description, expires):
|
||||
api_key = GraphqlAPIKey(description, expires)
|
||||
self.api_keys[api_key.key_id] = api_key
|
||||
return api_key
|
||||
|
||||
def list_api_keys(self):
|
||||
return self.api_keys.values()
|
||||
|
||||
def delete_api_key(self, api_key_id):
|
||||
self.api_keys.pop(api_key_id)
|
||||
|
||||
def update_api_key(self, api_key_id, description, expires):
|
||||
api_key = self.api_keys[api_key_id]
|
||||
api_key.update(description, expires)
|
||||
return api_key
|
||||
|
||||
def start_schema_creation(self, definition):
|
||||
graphql_definition = base64.b64decode(definition).decode("utf-8")
|
||||
|
||||
self.graphql_schema = GraphqlSchema(graphql_definition)
|
||||
|
||||
def get_schema_status(self):
|
||||
return self.graphql_schema.get_status()
|
||||
|
||||
def get_type(self, type_name, type_format):
|
||||
graphql_type = self.graphql_schema.get_type(type_name)
|
||||
graphql_type["format"] = type_format
|
||||
return graphql_type
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
"apiId": self.api_id,
|
||||
"authenticationType": self.authentication_type,
|
||||
"arn": self.arn,
|
||||
"uris": {"GRAPHQL": "http://graphql.uri"},
|
||||
"additionalAuthenticationProviders": self.additional_authentication_providers,
|
||||
"lambdaAuthorizerConfig": self.lambda_authorizer_config,
|
||||
"logConfig": self.log_config,
|
||||
"openIDConnectConfig": self.open_id_connect_config,
|
||||
"userPoolConfig": self.user_pool_config,
|
||||
"xrayEnabled": self.xray_enabled,
|
||||
}
|
||||
|
||||
|
||||
class GraphqlAPIKey(BaseModel):
|
||||
def __init__(self, description, expires):
|
||||
self.key_id = str(uuid4())[0:6]
|
||||
self.description = description
|
||||
self.expires = expires
|
||||
if not self.expires:
|
||||
default_expiry = datetime.now(timezone.utc)
|
||||
default_expiry = default_expiry.replace(
|
||||
minute=0, second=0, microsecond=0, tzinfo=None
|
||||
)
|
||||
default_expiry = default_expiry + timedelta(days=7)
|
||||
self.expires = unix_time(default_expiry)
|
||||
|
||||
def update(self, description, expires):
|
||||
if description:
|
||||
self.description = description
|
||||
if expires:
|
||||
self.expires = expires
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"id": self.key_id,
|
||||
"description": self.description,
|
||||
"expires": self.expires,
|
||||
"deletes": self.expires,
|
||||
}
|
||||
|
||||
|
||||
class AppSyncBackend(BaseBackend):
|
||||
"""Implementation of AppSync APIs."""
|
||||
|
||||
def __init__(self, region_name=None):
|
||||
self.region_name = region_name
|
||||
self.graphql_apis = dict()
|
||||
self.tagger = TaggingService()
|
||||
|
||||
def reset(self):
|
||||
"""Re-initialize all attributes for this instance."""
|
||||
region_name = self.region_name
|
||||
self.__dict__ = {}
|
||||
self.__init__(region_name)
|
||||
|
||||
def create_graphql_api(
|
||||
self,
|
||||
name,
|
||||
log_config,
|
||||
authentication_type,
|
||||
user_pool_config,
|
||||
open_id_connect_config,
|
||||
additional_authentication_providers,
|
||||
xray_enabled,
|
||||
lambda_authorizer_config,
|
||||
tags,
|
||||
):
|
||||
graphql_api = GraphqlAPI(
|
||||
region=self.region_name,
|
||||
name=name,
|
||||
authentication_type=authentication_type,
|
||||
additional_authentication_providers=additional_authentication_providers,
|
||||
log_config=log_config,
|
||||
xray_enabled=xray_enabled,
|
||||
user_pool_config=user_pool_config,
|
||||
open_id_connect_config=open_id_connect_config,
|
||||
lambda_authorizer_config=lambda_authorizer_config,
|
||||
)
|
||||
self.graphql_apis[graphql_api.api_id] = graphql_api
|
||||
self.tagger.tag_resource(
|
||||
graphql_api.arn, TaggingService.convert_dict_to_tags_input(tags)
|
||||
)
|
||||
return graphql_api
|
||||
|
||||
def update_graphql_api(
|
||||
self,
|
||||
api_id,
|
||||
name,
|
||||
log_config,
|
||||
authentication_type,
|
||||
user_pool_config,
|
||||
open_id_connect_config,
|
||||
additional_authentication_providers,
|
||||
xray_enabled,
|
||||
lambda_authorizer_config,
|
||||
):
|
||||
graphql_api = self.graphql_apis[api_id]
|
||||
graphql_api.update(
|
||||
name,
|
||||
additional_authentication_providers,
|
||||
authentication_type,
|
||||
lambda_authorizer_config,
|
||||
log_config,
|
||||
open_id_connect_config,
|
||||
user_pool_config,
|
||||
xray_enabled,
|
||||
)
|
||||
return graphql_api
|
||||
|
||||
def get_graphql_api(self, api_id):
|
||||
if api_id not in self.graphql_apis:
|
||||
raise GraphqlAPINotFound(api_id)
|
||||
return self.graphql_apis[api_id]
|
||||
|
||||
def delete_graphql_api(self, api_id):
|
||||
self.graphql_apis.pop(api_id)
|
||||
|
||||
def list_graphql_apis(self):
|
||||
"""
|
||||
Pagination or the maxResults-parameter have not yet been implemented.
|
||||
"""
|
||||
return self.graphql_apis.values()
|
||||
|
||||
def create_api_key(self, api_id, description, expires):
|
||||
return self.graphql_apis[api_id].create_api_key(description, expires)
|
||||
|
||||
def delete_api_key(self, api_id, api_key_id):
|
||||
self.graphql_apis[api_id].delete_api_key(api_key_id)
|
||||
|
||||
def list_api_keys(self, api_id):
|
||||
"""
|
||||
Pagination or the maxResults-parameter have not yet been implemented.
|
||||
"""
|
||||
if api_id in self.graphql_apis:
|
||||
return self.graphql_apis[api_id].list_api_keys()
|
||||
else:
|
||||
return []
|
||||
|
||||
def update_api_key(self, api_id, api_key_id, description, expires):
|
||||
return self.graphql_apis[api_id].update_api_key(
|
||||
api_key_id, description, expires
|
||||
)
|
||||
|
||||
def start_schema_creation(self, api_id, definition):
|
||||
self.graphql_apis[api_id].start_schema_creation(definition)
|
||||
return "PROCESSING"
|
||||
|
||||
def get_schema_creation_status(self, api_id):
|
||||
return self.graphql_apis[api_id].get_schema_status()
|
||||
|
||||
def tag_resource(self, resource_arn, tags):
|
||||
self.tagger.tag_resource(
|
||||
resource_arn, TaggingService.convert_dict_to_tags_input(tags)
|
||||
)
|
||||
|
||||
def untag_resource(self, resource_arn, tag_keys):
|
||||
self.tagger.untag_resource_using_names(resource_arn, tag_keys)
|
||||
|
||||
def list_tags_for_resource(self, resource_arn):
|
||||
return self.tagger.get_tag_dict_for_resource(resource_arn)
|
||||
|
||||
def get_type(self, api_id, type_name, type_format):
|
||||
return self.graphql_apis[api_id].get_type(type_name, type_format)
|
||||
|
||||
|
||||
appsync_backends = BackendDict(AppSyncBackend, "appsync")
|
250
moto/appsync/responses.py
Normal file
250
moto/appsync/responses.py
Normal file
@ -0,0 +1,250 @@
|
||||
"""Handles incoming appsync requests, invokes methods, returns responses."""
|
||||
import json
|
||||
|
||||
from functools import wraps
|
||||
from moto.core.responses import BaseResponse
|
||||
from urllib.parse import unquote
|
||||
from .exceptions import AppSyncExceptions
|
||||
from .models import appsync_backends
|
||||
|
||||
|
||||
def error_handler(f):
|
||||
@wraps(f)
|
||||
def _wrapper(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except AppSyncExceptions as e:
|
||||
return e.code, e.get_headers(), e.get_body()
|
||||
|
||||
return _wrapper
|
||||
|
||||
|
||||
class AppSyncResponse(BaseResponse):
|
||||
"""Handler for AppSync requests and responses."""
|
||||
|
||||
@property
|
||||
def appsync_backend(self):
|
||||
"""Return backend instance specific for this region."""
|
||||
return appsync_backends[self.region]
|
||||
|
||||
def graph_ql(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "POST":
|
||||
return self.create_graphql_api()
|
||||
if request.method == "GET":
|
||||
return self.list_graphql_apis()
|
||||
|
||||
@error_handler
|
||||
def graph_ql_individual(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "GET":
|
||||
return self.get_graphql_api()
|
||||
if request.method == "DELETE":
|
||||
return self.delete_graphql_api()
|
||||
if request.method == "POST":
|
||||
return self.update_graphql_api()
|
||||
|
||||
def api_key(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "POST":
|
||||
return self.create_api_key()
|
||||
if request.method == "GET":
|
||||
return self.list_api_keys()
|
||||
|
||||
def schemacreation(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "POST":
|
||||
return self.start_schema_creation()
|
||||
if request.method == "GET":
|
||||
return self.get_schema_creation_status()
|
||||
|
||||
def api_key_individual(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "DELETE":
|
||||
return self.delete_api_key()
|
||||
if request.method == "POST":
|
||||
return self.update_api_key()
|
||||
|
||||
def tags(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "POST":
|
||||
return self.tag_resource()
|
||||
if request.method == "DELETE":
|
||||
return self.untag_resource()
|
||||
if request.method == "GET":
|
||||
return self.list_tags_for_resource()
|
||||
|
||||
def types(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "GET":
|
||||
return self.get_type()
|
||||
|
||||
def create_graphql_api(self):
|
||||
params = json.loads(self.body)
|
||||
name = params.get("name")
|
||||
log_config = params.get("logConfig")
|
||||
authentication_type = params.get("authenticationType")
|
||||
user_pool_config = params.get("userPoolConfig")
|
||||
open_id_connect_config = params.get("openIDConnectConfig")
|
||||
tags = params.get("tags")
|
||||
additional_authentication_providers = params.get(
|
||||
"additionalAuthenticationProviders"
|
||||
)
|
||||
xray_enabled = params.get("xrayEnabled", False)
|
||||
lambda_authorizer_config = params.get("lambdaAuthorizerConfig")
|
||||
graphql_api = self.appsync_backend.create_graphql_api(
|
||||
name=name,
|
||||
log_config=log_config,
|
||||
authentication_type=authentication_type,
|
||||
user_pool_config=user_pool_config,
|
||||
open_id_connect_config=open_id_connect_config,
|
||||
additional_authentication_providers=additional_authentication_providers,
|
||||
xray_enabled=xray_enabled,
|
||||
lambda_authorizer_config=lambda_authorizer_config,
|
||||
tags=tags,
|
||||
)
|
||||
response = graphql_api.to_json()
|
||||
response["tags"] = self.appsync_backend.list_tags_for_resource(graphql_api.arn)
|
||||
return 200, {}, json.dumps(dict(graphqlApi=response))
|
||||
|
||||
def get_graphql_api(self):
|
||||
api_id = self.path.split("/")[-1]
|
||||
|
||||
graphql_api = self.appsync_backend.get_graphql_api(api_id=api_id)
|
||||
response = graphql_api.to_json()
|
||||
response["tags"] = self.appsync_backend.list_tags_for_resource(graphql_api.arn)
|
||||
return 200, {}, json.dumps(dict(graphqlApi=response))
|
||||
|
||||
def delete_graphql_api(self):
|
||||
api_id = self.path.split("/")[-1]
|
||||
self.appsync_backend.delete_graphql_api(api_id=api_id)
|
||||
return 200, {}, json.dumps(dict())
|
||||
|
||||
def update_graphql_api(self):
|
||||
api_id = self.path.split("/")[-1]
|
||||
|
||||
params = json.loads(self.body)
|
||||
name = params.get("name")
|
||||
log_config = params.get("logConfig")
|
||||
authentication_type = params.get("authenticationType")
|
||||
user_pool_config = params.get("userPoolConfig")
|
||||
print(user_pool_config)
|
||||
open_id_connect_config = params.get("openIDConnectConfig")
|
||||
additional_authentication_providers = params.get(
|
||||
"additionalAuthenticationProviders"
|
||||
)
|
||||
xray_enabled = params.get("xrayEnabled", False)
|
||||
lambda_authorizer_config = params.get("lambdaAuthorizerConfig")
|
||||
|
||||
api = self.appsync_backend.update_graphql_api(
|
||||
api_id=api_id,
|
||||
name=name,
|
||||
log_config=log_config,
|
||||
authentication_type=authentication_type,
|
||||
user_pool_config=user_pool_config,
|
||||
open_id_connect_config=open_id_connect_config,
|
||||
additional_authentication_providers=additional_authentication_providers,
|
||||
xray_enabled=xray_enabled,
|
||||
lambda_authorizer_config=lambda_authorizer_config,
|
||||
)
|
||||
return 200, {}, json.dumps(dict(graphqlApi=api.to_json()))
|
||||
|
||||
def list_graphql_apis(self):
|
||||
graphql_apis = self.appsync_backend.list_graphql_apis()
|
||||
return (
|
||||
200,
|
||||
{},
|
||||
json.dumps(dict(graphqlApis=[api.to_json() for api in graphql_apis])),
|
||||
)
|
||||
|
||||
def create_api_key(self):
|
||||
params = json.loads(self.body)
|
||||
# /v1/apis/[api_id]/apikeys
|
||||
api_id = self.path.split("/")[-2]
|
||||
description = params.get("description")
|
||||
expires = params.get("expires")
|
||||
api_key = self.appsync_backend.create_api_key(
|
||||
api_id=api_id, description=description, expires=expires,
|
||||
)
|
||||
print(api_key.to_json())
|
||||
return 200, {}, json.dumps(dict(apiKey=api_key.to_json()))
|
||||
|
||||
def delete_api_key(self):
|
||||
api_id = self.path.split("/")[-3]
|
||||
api_key_id = self.path.split("/")[-1]
|
||||
self.appsync_backend.delete_api_key(
|
||||
api_id=api_id, api_key_id=api_key_id,
|
||||
)
|
||||
return 200, {}, json.dumps(dict())
|
||||
|
||||
def list_api_keys(self):
|
||||
# /v1/apis/[api_id]/apikeys
|
||||
api_id = self.path.split("/")[-2]
|
||||
api_keys = self.appsync_backend.list_api_keys(api_id=api_id)
|
||||
return 200, {}, json.dumps(dict(apiKeys=[key.to_json() for key in api_keys]))
|
||||
|
||||
def update_api_key(self):
|
||||
api_id = self.path.split("/")[-3]
|
||||
api_key_id = self.path.split("/")[-1]
|
||||
params = json.loads(self.body)
|
||||
description = params.get("description")
|
||||
expires = params.get("expires")
|
||||
api_key = self.appsync_backend.update_api_key(
|
||||
api_id=api_id,
|
||||
api_key_id=api_key_id,
|
||||
description=description,
|
||||
expires=expires,
|
||||
)
|
||||
return 200, {}, json.dumps(dict(apiKey=api_key.to_json()))
|
||||
|
||||
def start_schema_creation(self):
|
||||
params = json.loads(self.body)
|
||||
api_id = self.path.split("/")[-2]
|
||||
definition = params.get("definition")
|
||||
status = self.appsync_backend.start_schema_creation(
|
||||
api_id=api_id, definition=definition,
|
||||
)
|
||||
return 200, {}, json.dumps({"status": status})
|
||||
|
||||
def get_schema_creation_status(self):
|
||||
api_id = self.path.split("/")[-2]
|
||||
status, details = self.appsync_backend.get_schema_creation_status(
|
||||
api_id=api_id,
|
||||
)
|
||||
return 200, {}, json.dumps(dict(status=status, details=details))
|
||||
|
||||
def tag_resource(self):
|
||||
resource_arn = self._extract_arn_from_path()
|
||||
params = json.loads(self.body)
|
||||
tags = params.get("tags")
|
||||
self.appsync_backend.tag_resource(
|
||||
resource_arn=resource_arn, tags=tags,
|
||||
)
|
||||
return 200, {}, json.dumps(dict())
|
||||
|
||||
def untag_resource(self):
|
||||
resource_arn = self._extract_arn_from_path()
|
||||
tag_keys = self.querystring.get("tagKeys", [])
|
||||
self.appsync_backend.untag_resource(
|
||||
resource_arn=resource_arn, tag_keys=tag_keys,
|
||||
)
|
||||
return 200, {}, json.dumps(dict())
|
||||
|
||||
def list_tags_for_resource(self):
|
||||
resource_arn = self._extract_arn_from_path()
|
||||
tags = self.appsync_backend.list_tags_for_resource(resource_arn=resource_arn,)
|
||||
return 200, {}, json.dumps(dict(tags=tags))
|
||||
|
||||
def _extract_arn_from_path(self):
|
||||
# /v1/tags/arn_that_may_contain_a_slash
|
||||
path = unquote(self.path)
|
||||
return "/".join(path.split("/")[3:])
|
||||
|
||||
def get_type(self):
|
||||
api_id = unquote(self.path.split("/")[-3])
|
||||
type_name = self.path.split("/")[-1]
|
||||
type_format = self.querystring.get("format")[0]
|
||||
graphql_type = self.appsync_backend.get_type(
|
||||
api_id=api_id, type_name=type_name, type_format=type_format,
|
||||
)
|
||||
return 200, {}, json.dumps(dict(type=graphql_type))
|
20
moto/appsync/urls.py
Normal file
20
moto/appsync/urls.py
Normal file
@ -0,0 +1,20 @@
|
||||
"""appsync base URL and path."""
|
||||
from .responses import AppSyncResponse
|
||||
|
||||
url_bases = [
|
||||
r"https?://appsync\.(.+)\.amazonaws\.com",
|
||||
]
|
||||
|
||||
|
||||
response = AppSyncResponse()
|
||||
|
||||
|
||||
url_paths = {
|
||||
"{0}/v1/apis$": response.graph_ql,
|
||||
"{0}/v1/apis/(?P<api_id>[^/]+)$": response.graph_ql_individual,
|
||||
"{0}/v1/apis/(?P<api_id>[^/]+)/apikeys$": response.api_key,
|
||||
"{0}/v1/apis/(?P<api_id>[^/]+)/apikeys/(?P<api_key_id>[^/]+)$": response.api_key_individual,
|
||||
"{0}/v1/apis/(?P<api_id>[^/]+)/schemacreation$": response.schemacreation,
|
||||
"{0}/v1/tags/(?P<resource_arn>.+)$": response.tags,
|
||||
"{0}/v1/apis/(?P<api_id>[^/]+)/types/(?P<type_name>.+)$": response.types,
|
||||
}
|
@ -8,6 +8,7 @@ backend_url_patterns = [
|
||||
"applicationautoscaling",
|
||||
re.compile("https?://application-autoscaling\\.(.+)\\.amazonaws.com"),
|
||||
),
|
||||
("appsync", re.compile("https?://appsync\\.(.+)\\.amazonaws\\.com")),
|
||||
("athena", re.compile("https?://athena\\.(.+)\\.amazonaws\\.com")),
|
||||
("autoscaling", re.compile("https?://autoscaling\\.(.+)\\.amazonaws\\.com")),
|
||||
("batch", re.compile("https?://batch\\.(.+)\\.amazonaws.com")),
|
||||
|
@ -168,4 +168,6 @@ class TaggingService:
|
||||
@staticmethod
|
||||
def convert_dict_to_tags_input(tags):
|
||||
""" Given a dictionary, return generic boto params for tags """
|
||||
if not tags:
|
||||
return []
|
||||
return [{"Key": k, "Value": v} for (k, v) in tags.items()]
|
||||
|
3
setup.py
3
setup.py
@ -49,6 +49,7 @@ _dep_python_jose_ecdsa_pin = (
|
||||
)
|
||||
_dep_dataclasses = "dataclasses; python_version < '3.7'"
|
||||
_dep_docker = "docker>=2.5.1"
|
||||
_dep_graphql = "graphql-core"
|
||||
_dep_jsondiff = "jsondiff>=1.1.2"
|
||||
_dep_aws_xray_sdk = "aws-xray-sdk!=0.96,>=0.93"
|
||||
_dep_idna = "idna<4,>=2.5"
|
||||
@ -61,6 +62,7 @@ all_extra_deps = [
|
||||
_dep_python_jose,
|
||||
_dep_python_jose_ecdsa_pin,
|
||||
_dep_docker,
|
||||
_dep_graphql,
|
||||
_dep_jsondiff,
|
||||
_dep_aws_xray_sdk,
|
||||
_dep_idna,
|
||||
@ -80,6 +82,7 @@ for service_name in [
|
||||
extras_per_service.update(
|
||||
{
|
||||
"apigateway": [_dep_python_jose, _dep_python_jose_ecdsa_pin],
|
||||
"appsync": [_dep_graphql],
|
||||
"awslambda": [_dep_docker],
|
||||
"batch": [_dep_docker],
|
||||
"cloudformation": [_dep_docker, _dep_PyYAML, _dep_cfn_lint],
|
||||
|
@ -1,5 +1,7 @@
|
||||
TestAccAWSAccessKey
|
||||
TestAccAWSAvailabilityZones
|
||||
TestAccAWSAppsyncApiKey
|
||||
TestAccAWSAppsyncGraphqlApi
|
||||
TestAccAWSBillingServiceAccount
|
||||
TestAccAWSCallerIdentity
|
||||
TestAccAWSCloudTrailServiceAccount
|
||||
|
0
tests/test_appsync/__init__.py
Normal file
0
tests/test_appsync/__init__.py
Normal file
164
tests/test_appsync/test_appsync.py
Normal file
164
tests/test_appsync/test_appsync.py
Normal file
@ -0,0 +1,164 @@
|
||||
import boto3
|
||||
import pytest
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from moto import mock_appsync
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
# See our Development Tips on writing tests for hints on how to write good tests:
|
||||
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_create_graphql_api():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
resp = client.create_graphql_api(name="api1", authenticationType="API_KEY")
|
||||
|
||||
resp.should.have.key("graphqlApi")
|
||||
|
||||
api = resp["graphqlApi"]
|
||||
api.should.have.key("name").equals("api1")
|
||||
api.should.have.key("apiId")
|
||||
api.should.have.key("authenticationType").equals("API_KEY")
|
||||
api.should.have.key("arn").equals(
|
||||
f"arn:aws:appsync:ap-southeast-1:{ACCOUNT_ID}:apis/{api['apiId']}"
|
||||
)
|
||||
api.should.have.key("uris").equals({"GRAPHQL": "http://graphql.uri"})
|
||||
api.should.have.key("xrayEnabled").equals(False)
|
||||
api.shouldnt.have.key("additionalAuthenticationProviders")
|
||||
api.shouldnt.have.key("logConfig")
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_create_graphql_api_advanced():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
resp = client.create_graphql_api(
|
||||
name="api1",
|
||||
authenticationType="API_KEY",
|
||||
additionalAuthenticationProviders=[{"authenticationType": "API_KEY"}],
|
||||
logConfig={
|
||||
"fieldLogLevel": "ERROR",
|
||||
"cloudWatchLogsRoleArn": "arn:aws:cloudwatch:role",
|
||||
},
|
||||
xrayEnabled=True,
|
||||
)
|
||||
|
||||
resp.should.have.key("graphqlApi")
|
||||
|
||||
api = resp["graphqlApi"]
|
||||
api.should.have.key("name").equals("api1")
|
||||
api.should.have.key("apiId")
|
||||
api.should.have.key("authenticationType").equals("API_KEY")
|
||||
api.should.have.key("arn").equals(
|
||||
f"arn:aws:appsync:ap-southeast-1:{ACCOUNT_ID}:apis/{api['apiId']}"
|
||||
)
|
||||
api.should.have.key("uris").equals({"GRAPHQL": "http://graphql.uri"})
|
||||
api.should.have.key("additionalAuthenticationProviders").equals(
|
||||
[{"authenticationType": "API_KEY"}]
|
||||
)
|
||||
api.should.have.key("logConfig").equals(
|
||||
{"cloudWatchLogsRoleArn": "arn:aws:cloudwatch:role", "fieldLogLevel": "ERROR"}
|
||||
)
|
||||
api.should.have.key("xrayEnabled").equals(True)
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_get_graphql_api():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
resp = client.get_graphql_api(apiId=api_id)
|
||||
resp.should.have.key("graphqlApi")
|
||||
|
||||
api = resp["graphqlApi"]
|
||||
api.should.have.key("name").equals("api1")
|
||||
api.should.have.key("apiId")
|
||||
api.should.have.key("authenticationType").equals("API_KEY")
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_update_graphql_api():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
client.update_graphql_api(
|
||||
apiId=api_id,
|
||||
name="api2",
|
||||
authenticationType="AWS_IAM",
|
||||
logConfig={
|
||||
"cloudWatchLogsRoleArn": "arn:aws:cloudwatch:role",
|
||||
"fieldLogLevel": "ERROR",
|
||||
},
|
||||
userPoolConfig={
|
||||
"awsRegion": "us-east-1",
|
||||
"defaultAction": "DENY",
|
||||
"userPoolId": "us-east-1_391729ed4a2d430a9d2abadecfc1ab86",
|
||||
},
|
||||
xrayEnabled=True,
|
||||
)
|
||||
|
||||
graphql_api = client.get_graphql_api(apiId=api_id)["graphqlApi"]
|
||||
|
||||
graphql_api.should.have.key("name").equals("api2")
|
||||
graphql_api.should.have.key("authenticationType").equals("AWS_IAM")
|
||||
graphql_api.should.have.key("arn").equals(
|
||||
f"arn:aws:appsync:ap-southeast-1:{ACCOUNT_ID}:apis/{graphql_api['apiId']}"
|
||||
)
|
||||
graphql_api.should.have.key("logConfig").equals(
|
||||
{"cloudWatchLogsRoleArn": "arn:aws:cloudwatch:role", "fieldLogLevel": "ERROR"}
|
||||
)
|
||||
graphql_api.should.have.key("userPoolConfig").equals(
|
||||
{
|
||||
"awsRegion": "us-east-1",
|
||||
"defaultAction": "DENY",
|
||||
"userPoolId": "us-east-1_391729ed4a2d430a9d2abadecfc1ab86",
|
||||
}
|
||||
)
|
||||
graphql_api.should.have.key("xrayEnabled").equals(True)
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_get_graphql_api_unknown():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.get_graphql_api(apiId="unknown")
|
||||
err = exc.value.response["Error"]
|
||||
|
||||
err["Code"].should.equal("NotFoundException")
|
||||
err["Message"].should.equal("GraphQL API unknown not found.")
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_delete_graphql_api():
|
||||
client = boto3.client("appsync", region_name="eu-west-1")
|
||||
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
resp = client.list_graphql_apis()
|
||||
resp.should.have.key("graphqlApis").length_of(1)
|
||||
|
||||
client.delete_graphql_api(apiId=api_id)
|
||||
|
||||
resp = client.list_graphql_apis()
|
||||
resp.should.have.key("graphqlApis").length_of(0)
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_list_graphql_apis():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
resp = client.list_graphql_apis()
|
||||
resp.should.have.key("graphqlApis").equals([])
|
||||
|
||||
for _ in range(3):
|
||||
client.create_graphql_api(name="api1", authenticationType="API_KEY")
|
||||
|
||||
resp = client.list_graphql_apis()
|
||||
resp.should.have.key("graphqlApis").length_of(3)
|
112
tests/test_appsync/test_appsync_apikeys.py
Normal file
112
tests/test_appsync/test_appsync_apikeys.py
Normal file
@ -0,0 +1,112 @@
|
||||
import boto3
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
from moto import mock_appsync
|
||||
from moto.core.utils import unix_time
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_create_api_key_simple():
|
||||
client = boto3.client("appsync", region_name="eu-west-1")
|
||||
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
resp = client.create_api_key(apiId=api_id)
|
||||
|
||||
resp.should.have.key("apiKey")
|
||||
api_key = resp["apiKey"]
|
||||
|
||||
api_key.should.have.key("id")
|
||||
api_key.shouldnt.have.key("description")
|
||||
api_key.should.have.key("expires")
|
||||
api_key.should.have.key("deletes")
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_create_api_key():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
tomorrow = datetime.now() + timedelta(days=1)
|
||||
tomorrow_in_secs = int(unix_time(tomorrow))
|
||||
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
resp = client.create_api_key(
|
||||
apiId=api_id, description="my first api key", expires=tomorrow_in_secs
|
||||
)
|
||||
|
||||
resp.should.have.key("apiKey")
|
||||
api_key = resp["apiKey"]
|
||||
|
||||
api_key.should.have.key("id")
|
||||
api_key.should.have.key("description").equals("my first api key")
|
||||
api_key.should.have.key("expires").equals(tomorrow_in_secs)
|
||||
api_key.should.have.key("deletes").equals(tomorrow_in_secs)
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_delete_api_key():
|
||||
client = boto3.client("appsync", region_name="us-east-1")
|
||||
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
api_key_id = client.create_api_key(apiId=api_id)["apiKey"]["id"]
|
||||
|
||||
client.delete_api_key(apiId=api_id, id=api_key_id)
|
||||
|
||||
resp = client.list_api_keys(apiId=api_id)
|
||||
resp.should.have.key("apiKeys").length_of(0)
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_list_api_keys_unknown_api():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
resp = client.list_api_keys(apiId="unknown")
|
||||
resp.should.have.key("apiKeys").equals([])
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_list_api_keys_empty():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
resp = client.list_api_keys(apiId=api_id)
|
||||
resp.should.have.key("apiKeys").equals([])
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_list_api_keys():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
client.create_api_key(apiId=api_id)
|
||||
client.create_api_key(apiId=api_id, description="my first api key")
|
||||
resp = client.list_api_keys(apiId=api_id)
|
||||
resp.should.have.key("apiKeys").length_of(2)
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_update_api_key():
|
||||
client = boto3.client("appsync", region_name="eu-west-1")
|
||||
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
original = client.create_api_key(apiId=api_id, description="my first api key")[
|
||||
"apiKey"
|
||||
]
|
||||
|
||||
updated = client.update_api_key(
|
||||
apiId=api_id, id=original["id"], description="my second api key"
|
||||
)["apiKey"]
|
||||
|
||||
updated.should.have.key("id").equals(original["id"])
|
||||
updated.should.have.key("description").equals("my second api key")
|
||||
updated.should.have.key("expires").equals(original["expires"])
|
||||
updated.should.have.key("deletes").equals(original["deletes"])
|
88
tests/test_appsync/test_appsync_schema.py
Normal file
88
tests/test_appsync/test_appsync_schema.py
Normal file
@ -0,0 +1,88 @@
|
||||
import boto3
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
from moto import mock_appsync
|
||||
|
||||
schema = """type Mutation {
|
||||
putPost(id: ID!, title: String!): Post
|
||||
}
|
||||
|
||||
"My custom post type"
|
||||
type Post {
|
||||
id: ID!
|
||||
title: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
singlePost(id: ID!): Post
|
||||
}
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
mutation: Mutation
|
||||
|
||||
}"""
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_start_schema_creation():
|
||||
client = boto3.client("appsync", region_name="us-east-2")
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
resp = client.start_schema_creation(apiId=api_id, definition=b"sth")
|
||||
|
||||
resp.should.have.key("status").equals("PROCESSING")
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_get_schema_creation_status():
|
||||
client = boto3.client("appsync", region_name="eu-west-1")
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
client.start_schema_creation(apiId=api_id, definition=schema.encode("utf-8"))
|
||||
resp = client.get_schema_creation_status(apiId=api_id)
|
||||
|
||||
resp.should.have.key("status").equals("SUCCESS")
|
||||
resp.shouldnt.have.key("details")
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_get_schema_creation_status_invalid():
|
||||
client = boto3.client("appsync", region_name="eu-west-1")
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
client.start_schema_creation(apiId=api_id, definition=b"sth")
|
||||
resp = client.get_schema_creation_status(apiId=api_id)
|
||||
|
||||
resp.should.have.key("status").equals("FAILED")
|
||||
resp.should.have.key("details").match("Syntax Error")
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_get_type_from_schema():
|
||||
client = boto3.client("appsync", region_name="us-east-2")
|
||||
|
||||
api_id = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]["apiId"]
|
||||
|
||||
client.start_schema_creation(apiId=api_id, definition=schema.encode("utf-8"))
|
||||
resp = client.get_type(apiId=api_id, typeName="Post", format="SDL")
|
||||
|
||||
resp.should.have.key("type")
|
||||
graphql_type = resp["type"]
|
||||
graphql_type.should.have.key("name").equals("Post")
|
||||
graphql_type.should.have.key("description").equals("My custom post type")
|
||||
graphql_type.should.have.key("arn").equals("arn:aws:appsync:graphql_type/Post")
|
||||
graphql_type.should.have.key("definition").equals("NotYetImplemented")
|
||||
graphql_type.should.have.key("format").equals("SDL")
|
||||
|
||||
query_type = client.get_type(apiId=api_id, typeName="Query", format="SDL")["type"]
|
||||
query_type.should.have.key("name").equals("Query")
|
||||
query_type.shouldnt.have.key("description")
|
86
tests/test_appsync/test_appsync_tags.py
Normal file
86
tests/test_appsync/test_appsync_tags.py
Normal file
@ -0,0 +1,86 @@
|
||||
import boto3
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
from moto import mock_appsync
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_create_graphql_api_with_tags():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
api = client.create_graphql_api(
|
||||
name="api1", authenticationType="API_KEY", tags={"key": "val", "key2": "val2"}
|
||||
)["graphqlApi"]
|
||||
|
||||
api.should.have.key("tags").equals({"key": "val", "key2": "val2"})
|
||||
|
||||
api = client.get_graphql_api(apiId=api["apiId"])["graphqlApi"]
|
||||
|
||||
api.should.have.key("tags").equals({"key": "val", "key2": "val2"})
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_tag_resource():
|
||||
client = boto3.client("appsync", region_name="us-east-2")
|
||||
api = client.create_graphql_api(name="api1", authenticationType="API_KEY")[
|
||||
"graphqlApi"
|
||||
]
|
||||
|
||||
client.tag_resource(resourceArn=(api["arn"]), tags={"key1": "val1"})
|
||||
|
||||
api = client.get_graphql_api(apiId=api["apiId"])["graphqlApi"]
|
||||
api.should.have.key("tags").equals({"key1": "val1"})
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_tag_resource_with_existing_tags():
|
||||
client = boto3.client("appsync", region_name="us-east-2")
|
||||
api = client.create_graphql_api(
|
||||
name="api1", authenticationType="API_KEY", tags={"key": "val", "key2": "val2"}
|
||||
)["graphqlApi"]
|
||||
|
||||
client.untag_resource(resourceArn=api["arn"], tagKeys=["key"])
|
||||
|
||||
client.tag_resource(
|
||||
resourceArn=(api["arn"]), tags={"key2": "new value", "key3": "val3"}
|
||||
)
|
||||
|
||||
api = client.get_graphql_api(apiId=api["apiId"])["graphqlApi"]
|
||||
api.should.have.key("tags").equals({"key2": "new value", "key3": "val3"})
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_untag_resource():
|
||||
client = boto3.client("appsync", region_name="eu-west-1")
|
||||
api = client.create_graphql_api(
|
||||
name="api1", authenticationType="API_KEY", tags={"key": "val", "key2": "val2"}
|
||||
)["graphqlApi"]
|
||||
|
||||
client.untag_resource(resourceArn=api["arn"], tagKeys=["key"])
|
||||
|
||||
api = client.get_graphql_api(apiId=api["apiId"])["graphqlApi"]
|
||||
api.should.have.key("tags").equals({"key2": "val2"})
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_untag_resource_all():
|
||||
client = boto3.client("appsync", region_name="eu-west-1")
|
||||
api = client.create_graphql_api(
|
||||
name="api1", authenticationType="API_KEY", tags={"key": "val", "key2": "val2"}
|
||||
)["graphqlApi"]
|
||||
|
||||
client.untag_resource(resourceArn=api["arn"], tagKeys=["key", "key2"])
|
||||
|
||||
api = client.get_graphql_api(apiId=api["apiId"])["graphqlApi"]
|
||||
api.should.have.key("tags").equals({})
|
||||
|
||||
|
||||
@mock_appsync
|
||||
def test_list_tags_for_resource():
|
||||
client = boto3.client("appsync", region_name="ap-southeast-1")
|
||||
api = client.create_graphql_api(
|
||||
name="api1", authenticationType="API_KEY", tags={"key": "val", "key2": "val2"}
|
||||
)["graphqlApi"]
|
||||
|
||||
resp = client.list_tags_for_resource(resourceArn=api["arn"])
|
||||
|
||||
resp.should.have.key("tags").equals({"key": "val", "key2": "val2"})
|
15
tests/test_appsync/test_server.py
Normal file
15
tests/test_appsync/test_server.py
Normal file
@ -0,0 +1,15 @@
|
||||
import json
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
import moto.server as server
|
||||
|
||||
|
||||
def test_appsync_list_tags_for_resource():
|
||||
backend = server.create_backend_app("appsync")
|
||||
test_client = backend.test_client()
|
||||
|
||||
resp = test_client.get(
|
||||
"/v1/tags/arn%3Aaws%3Aappsync%3Aus-east-1%3A123456789012%3Aapis%2Ff405dd93-855e-451d-ab00-7325b8e439c6?tagKeys=Description"
|
||||
)
|
||||
resp.status_code.should.equal(200)
|
||||
json.loads(resp.data).should.equals({"tags": {}})
|
Loading…
x
Reference in New Issue
Block a user