diff --git a/CHANGELOG.md b/CHANGELOG.md index 213a5867046..25046d0aad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - console: read-only modify page for mssql - console: filter out partitions from track table list and display partition info - console: fixes an issue where no schemas are listed on an MSSQL source +- server: REST endpoint bugfix for UUID url params ## v2.0.0-alpha.10 diff --git a/server/src-lib/Hasura/Server/Rest.hs b/server/src-lib/Hasura/Server/Rest.hs index c0216149e82..26c8f44459d 100644 --- a/server/src-lib/Hasura/Server/Rest.hs +++ b/server/src-lib/Hasura/Server/Rest.hs @@ -54,25 +54,31 @@ resolveVar varName (That _providedVar) = Left $ "Unexpected variable " <> toTxt resolveVar varName (These expectedVar providedVar) = -- TODO: See CustomTypes.hs for SCALAR types case G._vdType expectedVar of - G.TypeNamed (G.Nullability _) typeName -> case providedVar of + G.TypeNamed (G.Nullability nullable) typeName -> case providedVar of Right r -> Right (Just r) Left l - | typeName == boolScalar && T.null l -> Right $ Just $ J.Bool True -- Key present but value missing for bools defaults to True. - | typeName == stringScalar -> Right $ Just $ J.String l -- Note: Strings don't need to be decoded since the format already matches. - | typeName == $$(G.litName "UUID") -> Right $ Just $ J.String l -- TODO: Handle UUIDs - Is this litName correct? - | typeName == idScalar -> Right $ Just $ J.String l - | otherwise -> case J.decodeStrict (T.encodeUtf8 l) of - (Just J.Null ) | typeName == $$(G.litName "Null") -> pure $ Just J.Null - | otherwise -> Left $ "Expected " <> toTxt typeName <> " got Null" - (Just x@(J.Bool _)) | typeName == boolScalar -> pure $ Just x - | typeName == $$(G.litName "Bool") -> pure $ Just x - | otherwise -> Left $ "Expected " <> toTxt typeName <> " got Bool" - (Just x@(J.Number _)) | typeName == $$(G.litName "Number") -> pure $ Just x -- TODO: Check other numeric types - | typeName == intScalar -> pure $ Just x - | typeName == floatScalar -> pure $ Just x - | typeName == $$(G.litName "Double") -> pure $ Just x - | otherwise -> Left $ "Expected " <> toTxt typeName <> " got Number" - _ -> Left ("Type of URL parameter not supported - Consider putting it in the request body: " <> tshow l) + | typeName == stringScalar -> Right $ Just $ J.String l -- "String" -- Note: Strings don't need to be decoded since the format already matches. + | otherwise -> + case (J.decodeStrict (T.encodeUtf8 l), nullable) + of (Just J.Null, True) -> pure Nothing + (decoded, _) + | typeName == boolScalar && T.null l -> Right $ Just $ J.Bool True -- Key present but value missing for bools defaults to True. + | typeName == $$(G.litName "UUID") -> Right $ Just $ J.String l + | typeName == $$(G.litName "uuid") -> Right $ Just $ J.String l + | typeName == idScalar -> Right $ Just $ J.String l -- "ID" -- Note: Console doesn't expose this as a column type. + | otherwise -> case decoded of + (Just (J.Null )) -> Left $ "Null or missing value for non-nullable variable: " <> G.unName varName + (Just x@(J.Bool _)) | typeName == boolScalar -> pure $ Just x -- "Boolean" + | typeName == $$(G.litName "Bool") -> pure $ Just x + | otherwise -> Left $ "Expected " <> toTxt typeName <> " for variable " <> G.unName varName <> " got Bool" + (Just x@(J.Number _)) | typeName == intScalar -> pure $ Just x -- "Int" + | typeName == floatScalar -> pure $ Just x -- "Float" + | typeName == $$(G.litName "Number") -> pure $ Just x + | typeName == $$(G.litName "Double") -> pure $ Just x + | typeName == $$(G.litName "float8") -> pure $ Just x + | typeName == $$(G.litName "numeric") -> pure $ Just x + | otherwise -> Left $ "Expected " <> toTxt typeName <> " for variable " <> G.unName varName <> " got Number" + _ -> Left ("Type of URL parameter for variable " <> G.unName varName <> " not supported - Consider putting it in the request body: " <> tshow l) -- TODO: This is a fallthrough case and is still required -- but we can move checks for template variables being diff --git a/server/tests-py/queries/endpoints/endpoint_with_uuid_arg.yaml b/server/tests-py/queries/endpoints/endpoint_with_uuid_arg.yaml new file mode 100644 index 00000000000..a70801744b2 --- /dev/null +++ b/server/tests-py/queries/endpoints/endpoint_with_uuid_arg.yaml @@ -0,0 +1,13 @@ +description: Successful call of an endpoint with uuid arg +url: /api/rest/with_uuid/81F1BFEA-C169-44B5-B4C8-37E9A071ABD8 +method: GET +status: 200 +query: +response: + test_table: + - first_name: Foo + last_name: Bar + - first_name: Baz + last_name: Qux + - first_name: X%20Y + last_name: Test \ No newline at end of file diff --git a/server/tests-py/queries/endpoints/setup.yaml b/server/tests-py/queries/endpoints/setup.yaml index 515a6782381..1cbcddc3b7c 100644 --- a/server/tests-py/queries/endpoints/setup.yaml +++ b/server/tests-py/queries/endpoints/setup.yaml @@ -13,6 +13,8 @@ args: query: "query ($first_name:String!) { test_table(where: {first_name: { _eq: $first_name } }) { first_name last_name } }" - name: query_with_args query: "query ($first_name: String!, $last_name:String!) { test_table(where: {first_name: { _eq: $first_name } last_name: { _eq: $last_name }}) { first_name last_name } }" + - name: query_with_uuid_arg + query: "query ($id: uuid!) { test_table(where: {id: { _neq: $id }}) { first_name last_name } }" - name: simple_subscription query: "subscription { test_table { first_name last_name } }" @@ -72,6 +74,17 @@ args: collection_name: test_collection query_name: query_with_arg +- type: create_rest_endpoint + args: + url: with_uuid/:id + name: with_uuid + methods: + - GET + definition: + query: + collection_name: test_collection + query_name: query_with_uuid_arg + - type: create_rest_endpoint args: url: to_be_dropped @@ -92,7 +105,8 @@ args: sql: | create table test_table( first_name text, - last_name text + last_name text, + id UUID NOT NULL DEFAULT gen_random_uuid() ); - type: track_table diff --git a/server/tests-py/queries/endpoints/teardown.yaml b/server/tests-py/queries/endpoints/teardown.yaml index 686e95efe2f..c68192bbe03 100644 --- a/server/tests-py/queries/endpoints/teardown.yaml +++ b/server/tests-py/queries/endpoints/teardown.yaml @@ -12,6 +12,9 @@ args: - type: drop_rest_endpoint args: name: with_args +- type: drop_rest_endpoint + args: + name: with_uuid - type: drop_rest_endpoint args: name: with_template diff --git a/server/tests-py/test_endpoints.py b/server/tests-py/test_endpoints.py index 954468faff1..df867fb9eab 100644 --- a/server/tests-py/test_endpoints.py +++ b/server/tests-py/test_endpoints.py @@ -73,5 +73,8 @@ class TestCustomEndpoints: def test_endpoint_empty_path_param(self, hge_ctx, transport): check_query_f(hge_ctx, self.dir() + '/endpoint_empty_path_param.yaml', transport) + def test_endpoint_uuid_arg(self, hge_ctx, transport): + check_query_f(hge_ctx, self.dir() + '/endpoint_with_uuid_arg.yaml', transport) + def test_endpoint_subscription(self, hge_ctx, transport): check_query_f(hge_ctx, self.dir() + '/endpoint_subscription.yaml', transport)