mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
b84db36ebb
* basic doc for actions * custom_types, sync and async actions * switch to graphql-parser-hs on github * update docs * metadata import/export * webhook calls are now supported * relationships in sync actions * initialise.sql is now in sync with the migration file * fix metadata tests * allow specifying arguments of actions * fix blacklist check on check_build_worthiness job * track custom_types and actions related tables * handlers are now triggered on async actions * default to pgjson unless a field is involved in relationships, for generating definition list * use 'true' for action filter for non admin role * fix create_action_permission sql query * drop permissions when dropping an action * add a hdb_role view (and relationships) to fetch all roles in the system * rename 'webhook' key in action definition to 'handler' * allow templating actions wehook URLs with env vars * add 'update_action' /v1/query type * allow forwarding client headers by setting `forward_client_headers` in action definition * add 'headers' configuration in action definition * handle webhook error response based on status codes * support array relationships for custom types * implement single row mutation, see https://github.com/hasura/graphql-engine/issues/3731 * single row mutation: rename 'pk_columns' -> 'columns' and no-op refactor * use top level primary key inputs for delete_by_pk & account select permissions for single row mutations * use only REST semantics to resolve the webhook response * use 'pk_columns' instead of 'columns' for update_by_pk input * add python basic tests for single row mutations * add action context (name) in webhook payload * Async action response is accessible for non admin roles only if the request session vars equals to action's * clean nulls, empty arrays for actions, custom types in export metadata * async action mutation returns only the UUID of the action * unit tests for URL template parser * Basic sync actions python tests * fix output in async query & add async tests * add admin secret header in async actions python test * document async action architecture in Resolve/Action.hs file * support actions returning array of objects * tests for list type response actions * update docs with actions and custom types metadata API reference * update actions python tests as per #f8e1330 Co-authored-by: Tirumarai Selvan <tirumarai.selvan@gmail.com> Co-authored-by: Aravind Shankar <face11301@gmail.com> Co-authored-by: Rakesh Emmadi <12475069+rakeshkky@users.noreply.github.com>
248 lines
6.9 KiB
Python
248 lines
6.9 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import pytest
|
|
import time
|
|
|
|
from validate import check_query_f, check_query
|
|
|
|
"""
|
|
TODO:- Test Actions metadata
|
|
"""
|
|
|
|
use_action_fixtures = pytest.mark.usefixtures(
|
|
"actions_webhook",
|
|
'per_class_db_schema_for_mutation_tests',
|
|
'per_method_db_data_for_mutation_tests'
|
|
)
|
|
|
|
@pytest.mark.parametrize("transport", ['http', 'websocket'])
|
|
@use_action_fixtures
|
|
class TestActionsSync:
|
|
|
|
@classmethod
|
|
def dir(cls):
|
|
return 'queries/actions/sync'
|
|
|
|
def test_create_user_fail(self, hge_ctx, transport):
|
|
check_query_f(hge_ctx, self.dir() + '/create_user_fail.yaml', transport)
|
|
|
|
def test_create_user_success(self, hge_ctx, transport):
|
|
check_query_f(hge_ctx, self.dir() + '/create_user_success.yaml', transport)
|
|
|
|
def test_create_users_fail(self, hge_ctx, transport):
|
|
check_query_f(hge_ctx, self.dir() + '/create_users_fail.yaml', transport)
|
|
|
|
def test_create_users_success(self, hge_ctx, transport):
|
|
check_query_f(hge_ctx, self.dir() + '/create_users_success.yaml', transport)
|
|
|
|
def test_invalid_webhook_response(self, hge_ctx, transport):
|
|
check_query_f(hge_ctx, self.dir() + '/invalid_webhook_response.yaml')
|
|
|
|
def test_expecting_object_response(self, hge_ctx, transport):
|
|
check_query_f(hge_ctx, self.dir() + '/expecting_object_response.yaml')
|
|
|
|
def test_expecting_array_response(self, hge_ctx, transport):
|
|
check_query_f(hge_ctx, self.dir() + '/expecting_array_response.yaml')
|
|
|
|
@use_action_fixtures
|
|
class TestActionsAsync:
|
|
@classmethod
|
|
def dir(cls):
|
|
return 'queries/actions/async'
|
|
|
|
def mk_headers_with_secret(self, hge_ctx, headers={}):
|
|
admin_secret = hge_ctx.hge_key
|
|
if admin_secret:
|
|
headers['X-Hasura-Admin-Secret'] = admin_secret
|
|
return headers
|
|
|
|
|
|
def test_create_user_fail(self, hge_ctx):
|
|
graphql_mutation = '''
|
|
mutation {
|
|
create_user(email: "random-email", name: "Clarke")
|
|
}
|
|
'''
|
|
query = {
|
|
'query': graphql_mutation,
|
|
'variables': {}
|
|
}
|
|
status, resp, _ = hge_ctx.anyq('/v1/graphql', query, self.mk_headers_with_secret(hge_ctx))
|
|
assert status == 200, resp
|
|
assert 'data' in resp
|
|
action_id = resp['data']['create_user']
|
|
time.sleep(2)
|
|
|
|
query_async = '''
|
|
query ($action_id: uuid!){
|
|
create_user(id: $action_id){
|
|
id
|
|
errors
|
|
}
|
|
}
|
|
'''
|
|
query = {
|
|
'query': query_async,
|
|
'variables': {
|
|
'action_id': action_id
|
|
}
|
|
}
|
|
response = {
|
|
'data': {
|
|
'create_user': {
|
|
'id': action_id,
|
|
'errors': {
|
|
'code': 'invalid-email',
|
|
'path': '$',
|
|
'error': 'Given email address is not valid'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
conf = {
|
|
'url': '/v1/graphql',
|
|
'headers': {},
|
|
'query': query,
|
|
'status': 200,
|
|
'response': response
|
|
}
|
|
check_query(hge_ctx, conf)
|
|
|
|
def test_create_user_success(self, hge_ctx):
|
|
graphql_mutation = '''
|
|
mutation {
|
|
create_user(email: "clarke@hasura.io", name: "Clarke")
|
|
}
|
|
'''
|
|
query = {
|
|
'query': graphql_mutation,
|
|
'variables': {}
|
|
}
|
|
status, resp, _ = hge_ctx.anyq('/v1/graphql', query, self.mk_headers_with_secret(hge_ctx))
|
|
assert status == 200, resp
|
|
assert 'data' in resp
|
|
action_id = resp['data']['create_user']
|
|
time.sleep(2)
|
|
|
|
query_async = '''
|
|
query ($action_id: uuid!){
|
|
create_user(id: $action_id){
|
|
id
|
|
output {
|
|
id
|
|
user {
|
|
name
|
|
email
|
|
is_admin
|
|
}
|
|
}
|
|
}
|
|
}
|
|
'''
|
|
query = {
|
|
'query': query_async,
|
|
'variables': {
|
|
'action_id': action_id
|
|
}
|
|
}
|
|
response = {
|
|
'data': {
|
|
'create_user': {
|
|
'id': action_id,
|
|
'output': {
|
|
'id': 1,
|
|
'user': {
|
|
'name': 'Clarke',
|
|
'email': 'clarke@hasura.io',
|
|
'is_admin': False
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
conf = {
|
|
'url': '/v1/graphql',
|
|
'headers': {},
|
|
'query': query,
|
|
'status': 200,
|
|
'response': response
|
|
}
|
|
check_query(hge_ctx, conf)
|
|
|
|
def test_create_user_roles(self, hge_ctx):
|
|
graphql_mutation = '''
|
|
mutation {
|
|
create_user(email: "blake@hasura.io", name: "Blake")
|
|
}
|
|
'''
|
|
query = {
|
|
'query': graphql_mutation,
|
|
'variables': {}
|
|
}
|
|
headers_user_1 = self.mk_headers_with_secret(hge_ctx, {
|
|
'X-Hasura-Role': 'user',
|
|
'X-Hasura-User-Id': '1'
|
|
})
|
|
# create action with user-id 1
|
|
status, resp, headers = hge_ctx.anyq('/v1/graphql', query, headers_user_1)
|
|
assert status == 200, resp
|
|
assert 'data' in resp
|
|
action_id = resp['data']['create_user']
|
|
time.sleep(2)
|
|
|
|
query_async = '''
|
|
query ($action_id: uuid!){
|
|
create_user(id: $action_id){
|
|
id
|
|
output {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
'''
|
|
query = {
|
|
'query': query_async,
|
|
'variables': {
|
|
'action_id': action_id
|
|
}
|
|
}
|
|
|
|
headers_user_2 = self.mk_headers_with_secret(hge_ctx, {
|
|
'X-Hasura-Role': 'user',
|
|
'X-Hasura-User-Id': '2'
|
|
})
|
|
conf_user_2 = {
|
|
'url': '/v1/graphql',
|
|
'headers': headers_user_2,
|
|
'query': query,
|
|
'status': 200,
|
|
'response': {
|
|
'data': {
|
|
'create_user': None # User 2 shouldn't able to access the action
|
|
}
|
|
}
|
|
}
|
|
# Query the action as user-id 2
|
|
# Make request without auth using admin_secret
|
|
check_query(hge_ctx, conf_user_2, add_auth = False)
|
|
|
|
conf_user_1 = {
|
|
'url': '/v1/graphql',
|
|
'headers': headers_user_1,
|
|
'query': query,
|
|
'status': 200,
|
|
'response': {
|
|
'data': {
|
|
'create_user': {
|
|
'id': action_id,
|
|
'output': {
|
|
'id': 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
# Query the action as user-id 1
|
|
# Make request without auth using admin_secret
|
|
check_query(hge_ctx, conf_user_1, add_auth = False)
|