Change tests to use postgres-json-schema instead of hjsonschema.
Embeds https://github.com/gavinwahl/postgres-json-schema.
This commit is contained in:
committed by
Steve Chávez
parent
e34669b137
commit
65fc672417
@@ -232,6 +232,7 @@ jobs:
|
||||
psql "postgres:///postgrest_test" -f test/fixtures/roles.sql
|
||||
psql "postgres:///postgrest_test" -f test/fixtures/schema.sql
|
||||
psql "postgres:///postgrest_test" -f test/fixtures/jwt.sql
|
||||
psql "postgres:///postgrest_test" -f test/fixtures/jsonschema.sql
|
||||
psql "postgres:///postgrest_test" -f test/fixtures/privileges.sql
|
||||
test/memory-tests.sh
|
||||
- save_cache:
|
||||
|
||||
+1
-1
@@ -158,7 +158,6 @@ Test-Suite spec
|
||||
, hasql-pool >= 0.5 && < 0.6
|
||||
, hasql-transaction >= 0.7 && < 0.8
|
||||
, heredoc
|
||||
, hjsonschema == 1.5.0.1
|
||||
, hspec
|
||||
, hspec-wai >= 0.7.0
|
||||
, hspec-wai-json
|
||||
@@ -170,6 +169,7 @@ Test-Suite spec
|
||||
, process
|
||||
, protolude == 0.2.2
|
||||
, regex-tdfa
|
||||
, text
|
||||
, time
|
||||
, transformers-base
|
||||
, wai
|
||||
|
||||
@@ -2,8 +2,6 @@ resolver: lts-9.6
|
||||
extra-deps:
|
||||
- configurator-ng-0.0.0.1
|
||||
- critbit-0.2.0.0
|
||||
- hjsonpointer-1.1.1
|
||||
- hjsonschema-1.5.0.1
|
||||
- Ranged-sets-0.3.0
|
||||
- protolude-0.2.2
|
||||
- hasql-1.3
|
||||
@@ -18,6 +16,3 @@ ghc-options:
|
||||
postgrest: -O2 -Werror -Wall -fwarn-identities -fno-warn-redundant-constraints
|
||||
nix:
|
||||
shell-file: shell.nix
|
||||
# only added because of hjsonschema conflict with http-types
|
||||
# once hjsonschema upper bounding on http-types is solved it can be removed
|
||||
allow-newer: true
|
||||
|
||||
@@ -3,14 +3,11 @@ module Feature.ProxySpec where
|
||||
import Network.Wai (Application)
|
||||
import Test.Hspec hiding (pendingWith)
|
||||
|
||||
import Test.Hspec.Wai
|
||||
|
||||
import Protolude
|
||||
import SpecHelper
|
||||
|
||||
spec :: SpecWith Application
|
||||
spec =
|
||||
describe "GET / with proxy" $
|
||||
it "returns a valid openapi spec with proxy" $ do
|
||||
pendingWith "Test timing out frequently on CI, please run locally"
|
||||
it "returns a valid openapi spec with proxy" $
|
||||
validateOpenApiResponse [("Accept", "application/openapi+json")]
|
||||
|
||||
@@ -21,8 +21,7 @@ spec :: SpecWith Application
|
||||
spec = do
|
||||
|
||||
describe "OpenAPI" $ do
|
||||
it "root path returns a valid openapi spec" $ do
|
||||
pendingWith "Test timing out frequently on CI, please run locally"
|
||||
it "root path returns a valid openapi spec" $
|
||||
validateOpenApiResponse [("Accept", "application/openapi+json")]
|
||||
|
||||
it "should respond to openapi request on none root path with 415" $
|
||||
|
||||
+15
-14
@@ -5,14 +5,12 @@ import qualified Data.ByteString.Char8 as BS
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.Map.Strict as M
|
||||
import qualified Data.Set as S
|
||||
import qualified JSONSchema.Draft4 as D4
|
||||
import qualified System.IO.Error as E
|
||||
|
||||
import Control.Monad (void)
|
||||
import Data.Aeson (Value (..), decode)
|
||||
import Data.Aeson (Value (..), decode, encode)
|
||||
import Data.CaseInsensitive (CI (..))
|
||||
import Data.List (lookup)
|
||||
import Data.Maybe (fromJust)
|
||||
import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus))
|
||||
import System.Environment (getEnv)
|
||||
import System.Process (readProcess)
|
||||
@@ -45,17 +43,19 @@ validateOpenApiResponse headers = do
|
||||
let respHeaders = simpleHeaders r in
|
||||
respHeaders `shouldSatisfy`
|
||||
\hs -> ("Content-Type", "application/openapi+json; charset=utf-8") `elem` hs
|
||||
liftIO $
|
||||
let respBody = simpleBody r
|
||||
schema :: D4.Schema
|
||||
schema = D4.emptySchema { D4._schemaRef = Just "openapi.json" }
|
||||
schemaContext :: D4.SchemaWithURI D4.Schema
|
||||
schemaContext = D4.SchemaWithURI
|
||||
{ D4._swSchema = schema
|
||||
, D4._swURI = Just "test/fixtures/openapi.json"
|
||||
}
|
||||
in
|
||||
D4.fetchFilesystemAndValidate schemaContext ((fromJust . decode) respBody) `shouldReturn` Right ()
|
||||
let Just body = decode (simpleBody r)
|
||||
Just schema <- liftIO $ decode <$> BL.readFile "test/fixtures/openapi.json"
|
||||
let args :: M.Map Text Value
|
||||
args = M.fromList
|
||||
[ ( "schema", schema )
|
||||
, ( "data", body ) ]
|
||||
hdrs = acceptHdrs "application/json"
|
||||
request methodPost "/rpc/validate_json_schema" hdrs (encode args)
|
||||
`shouldRespondWith` "true"
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = []
|
||||
}
|
||||
|
||||
|
||||
getEnvVarWithDefault :: Text -> Text -> IO Text
|
||||
getEnvVarWithDefault var def = toS <$>
|
||||
@@ -130,6 +130,7 @@ setupDb dbConn = do
|
||||
loadFixture dbConn "roles"
|
||||
loadFixture dbConn "schema"
|
||||
loadFixture dbConn "jwt"
|
||||
loadFixture dbConn "jsonschema"
|
||||
loadFixture dbConn "privileges"
|
||||
resetDb dbConn
|
||||
|
||||
|
||||
Vendored
+280
@@ -0,0 +1,280 @@
|
||||
-- from gavinwahl/postgres-json-schema commit 5a257e19a1569a77b82e9182b0b7d9fc8b6f6382
|
||||
|
||||
/*
|
||||
Copyright (c) 2016, Gavin Wahl
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose, without fee, and without a written agreement is
|
||||
hereby granted, provided that the above copyright notice and this paragraph and
|
||||
the following two paragraphs appear in all copies.
|
||||
|
||||
IN NO EVENT SHALL GAVIN WAHL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
||||
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
|
||||
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF GAVIN WAHL HAS
|
||||
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
GAVIN WAHL SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND GAVIN WAHL
|
||||
HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
||||
MODIFICATIONS.
|
||||
*/
|
||||
|
||||
CREATE OR REPLACE FUNCTION _validate_json_schema_type(type text, data jsonb) RETURNS boolean AS $f$
|
||||
BEGIN
|
||||
IF type = 'integer' THEN
|
||||
IF jsonb_typeof(data) != 'number' THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
IF trunc(data::text::numeric) != data::text::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
ELSE
|
||||
IF type != jsonb_typeof(data) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
RETURN true;
|
||||
END;
|
||||
$f$ LANGUAGE 'plpgsql' IMMUTABLE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION test.validate_json_schema(schema jsonb, data jsonb, root_schema jsonb DEFAULT NULL) RETURNS boolean AS $f$
|
||||
DECLARE
|
||||
prop text;
|
||||
item jsonb;
|
||||
path text[];
|
||||
types text[];
|
||||
pattern text;
|
||||
props text[];
|
||||
BEGIN
|
||||
IF root_schema IS NULL THEN
|
||||
root_schema = schema;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'type' THEN
|
||||
IF jsonb_typeof(schema->'type') = 'array' THEN
|
||||
types = ARRAY(SELECT jsonb_array_elements_text(schema->'type'));
|
||||
ELSE
|
||||
types = ARRAY[schema->>'type'];
|
||||
END IF;
|
||||
IF (SELECT NOT bool_or(public._validate_json_schema_type(type, data)) FROM unnest(types) type) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'properties' THEN
|
||||
FOR prop IN SELECT jsonb_object_keys(schema->'properties') LOOP
|
||||
IF data ? prop AND NOT validate_json_schema(schema->'properties'->prop, data->prop, root_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'required' AND jsonb_typeof(data) = 'object' THEN
|
||||
IF NOT ARRAY(SELECT jsonb_object_keys(data)) @>
|
||||
ARRAY(SELECT jsonb_array_elements_text(schema->'required')) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'items' AND jsonb_typeof(data) = 'array' THEN
|
||||
IF jsonb_typeof(schema->'items') = 'object' THEN
|
||||
FOR item IN SELECT jsonb_array_elements(data) LOOP
|
||||
IF NOT validate_json_schema(schema->'items', item, root_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END LOOP;
|
||||
ELSE
|
||||
IF NOT (
|
||||
SELECT bool_and(i > jsonb_array_length(schema->'items') OR validate_json_schema(schema->'items'->(i::int - 1), elem, root_schema))
|
||||
FROM jsonb_array_elements(data) WITH ORDINALITY AS t(elem, i)
|
||||
) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF jsonb_typeof(schema->'additionalItems') = 'boolean' and NOT (schema->'additionalItems')::text::boolean AND jsonb_typeof(schema->'items') = 'array' THEN
|
||||
IF jsonb_array_length(data) > jsonb_array_length(schema->'items') THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF jsonb_typeof(schema->'additionalItems') = 'object' THEN
|
||||
IF NOT (
|
||||
SELECT bool_and(validate_json_schema(schema->'additionalItems', elem, root_schema))
|
||||
FROM jsonb_array_elements(data) WITH ORDINALITY AS t(elem, i)
|
||||
WHERE i > jsonb_array_length(schema->'items')
|
||||
) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'minimum' AND jsonb_typeof(data) = 'number' THEN
|
||||
IF data::text::numeric < (schema->>'minimum')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'maximum' AND jsonb_typeof(data) = 'number' THEN
|
||||
IF data::text::numeric > (schema->>'maximum')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF COALESCE((schema->'exclusiveMinimum')::text::bool, FALSE) THEN
|
||||
IF data::text::numeric = (schema->>'minimum')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF COALESCE((schema->'exclusiveMaximum')::text::bool, FALSE) THEN
|
||||
IF data::text::numeric = (schema->>'maximum')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'anyOf' THEN
|
||||
IF NOT (SELECT bool_or(validate_json_schema(sub_schema, data, root_schema)) FROM jsonb_array_elements(schema->'anyOf') sub_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'allOf' THEN
|
||||
IF NOT (SELECT bool_and(validate_json_schema(sub_schema, data, root_schema)) FROM jsonb_array_elements(schema->'allOf') sub_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'oneOf' THEN
|
||||
IF 1 != (SELECT COUNT(*) FROM jsonb_array_elements(schema->'oneOf') sub_schema WHERE validate_json_schema(sub_schema, data, root_schema)) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF COALESCE((schema->'uniqueItems')::text::boolean, false) THEN
|
||||
IF (SELECT COUNT(*) FROM jsonb_array_elements(data)) != (SELECT count(DISTINCT val) FROM jsonb_array_elements(data) val) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'additionalProperties' AND jsonb_typeof(data) = 'object' THEN
|
||||
props := ARRAY(
|
||||
SELECT key
|
||||
FROM jsonb_object_keys(data) key
|
||||
WHERE key NOT IN (SELECT jsonb_object_keys(schema->'properties'))
|
||||
AND NOT EXISTS (SELECT * FROM jsonb_object_keys(schema->'patternProperties') pat WHERE key ~ pat)
|
||||
);
|
||||
IF jsonb_typeof(schema->'additionalProperties') = 'boolean' THEN
|
||||
IF NOT (schema->'additionalProperties')::text::boolean AND jsonb_typeof(data) = 'object' AND NOT props <@ ARRAY(SELECT jsonb_object_keys(schema->'properties')) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
ELSEIF NOT (
|
||||
SELECT bool_and(validate_json_schema(schema->'additionalProperties', data->key, root_schema))
|
||||
FROM unnest(props) key
|
||||
) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? '$ref' THEN
|
||||
path := ARRAY(
|
||||
SELECT regexp_replace(regexp_replace(path_part, '~1', '/'), '~0', '~')
|
||||
FROM UNNEST(regexp_split_to_array(schema->>'$ref', '/')) path_part
|
||||
);
|
||||
-- ASSERT path[1] = '#', 'only refs anchored at the root are supported';
|
||||
IF NOT validate_json_schema(root_schema #> path[2:array_length(path, 1)], data, root_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'enum' THEN
|
||||
IF NOT EXISTS (SELECT * FROM jsonb_array_elements(schema->'enum') val WHERE val = data) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'minLength' AND jsonb_typeof(data) = 'string' THEN
|
||||
IF char_length(data #>> '{}') < (schema->>'minLength')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'maxLength' AND jsonb_typeof(data) = 'string' THEN
|
||||
IF char_length(data #>> '{}') > (schema->>'maxLength')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'not' THEN
|
||||
IF validate_json_schema(schema->'not', data, root_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'maxProperties' AND jsonb_typeof(data) = 'object' THEN
|
||||
IF (SELECT count(*) FROM jsonb_object_keys(data)) > (schema->>'maxProperties')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'minProperties' AND jsonb_typeof(data) = 'object' THEN
|
||||
IF (SELECT count(*) FROM jsonb_object_keys(data)) < (schema->>'minProperties')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'maxItems' AND jsonb_typeof(data) = 'array' THEN
|
||||
IF (SELECT count(*) FROM jsonb_array_elements(data)) > (schema->>'maxItems')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'minItems' AND jsonb_typeof(data) = 'array' THEN
|
||||
IF (SELECT count(*) FROM jsonb_array_elements(data)) < (schema->>'minItems')::numeric THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'dependencies' THEN
|
||||
FOR prop IN SELECT jsonb_object_keys(schema->'dependencies') LOOP
|
||||
IF data ? prop THEN
|
||||
IF jsonb_typeof(schema->'dependencies'->prop) = 'array' THEN
|
||||
IF NOT (SELECT bool_and(data ? dep) FROM jsonb_array_elements_text(schema->'dependencies'->prop) dep) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
ELSE
|
||||
IF NOT validate_json_schema(schema->'dependencies'->prop, data, root_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'pattern' AND jsonb_typeof(data) = 'string' THEN
|
||||
IF (data #>> '{}') !~ (schema->>'pattern') THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'patternProperties' AND jsonb_typeof(data) = 'object' THEN
|
||||
FOR prop IN SELECT jsonb_object_keys(data) LOOP
|
||||
FOR pattern IN SELECT jsonb_object_keys(schema->'patternProperties') LOOP
|
||||
RAISE NOTICE 'prop %s, pattern %, schema %', prop, pattern, schema->'patternProperties'->pattern;
|
||||
IF prop ~ pattern AND NOT validate_json_schema(schema->'patternProperties'->pattern, data->prop, root_schema) THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
IF schema ? 'multipleOf' AND jsonb_typeof(data) = 'number' THEN
|
||||
IF data::text::numeric % (schema->>'multipleOf')::numeric != 0 THEN
|
||||
RETURN false;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN true;
|
||||
END;
|
||||
$f$ LANGUAGE 'plpgsql' IMMUTABLE;
|
||||
Reference in New Issue
Block a user