这是indexloc提供的服务,不要输入任何密码
Skip to content

fix error code for when insertion check constraint fails (fix #257) #267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions server/graphql-engine.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -250,5 +250,7 @@ test-suite graphql-engine-test
, yaml
, http-client
, http-client-tls
, unordered-containers >= 0.2
, case-insensitive

other-modules: Spec
29 changes: 14 additions & 15 deletions server/src-lib/Hasura/RQL/DML/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ dmlTxErrorHandler :: Q.PGTxErr -> QErr
dmlTxErrorHandler p2Res =
case err of
Nothing -> defaultTxErrorHandler p2Res
Just msg -> err400 PostgresError msg
Just (code, msg) -> err400 code msg
where err = simplifyError p2Res

-- | col_name as col_name
Expand All @@ -236,7 +236,7 @@ mkColExtrAl :: (IsIden a) => Maybe a -> (PGCol, PGColType) -> S.Extractor
mkColExtrAl alM (c, pct) =
if pct == PGGeometry || pct == PGGeography
then S.mkAliasedExtrFromExp
((S.SEFnApp "ST_AsGeoJSON" [S.mkSIdenExp c] Nothing) `S.SETyAnn` S.jsonType) alM
(S.SEFnApp "ST_AsGeoJSON" [S.mkSIdenExp c] Nothing `S.SETyAnn` S.jsonType) alM
else S.mkAliasedExtr c alM

-- validate headers
Expand All @@ -247,7 +247,7 @@ validateHeaders depHeaders = do
unless (hdr `elem` map T.toLower headers) $
throw400 NotFound $ hdr <<> " header is expected but not found"

simplifyError :: Q.PGTxErr -> Maybe T.Text
simplifyError :: Q.PGTxErr -> Maybe (Code, T.Text)
simplifyError txErr = do
stmtErr <- Q.getPGStmtErr txErr
codeMsg <- getPGCodeMsg stmtErr
Expand All @@ -257,32 +257,31 @@ simplifyError txErr = do
(,) <$> Q.edStatusCode pged <*> Q.edMessage pged
extractError = \case
-- restrict violation
("23501", msg) ->
return $ "Can not delete or update due to data being referred. " <> msg
("23001", msg) ->
return (ConstraintViolation, "Can not delete or update due to data being referred. " <> msg)
-- not null violation
("23502", msg) ->
return $ "Not-NULL violation. " <> msg
return (ConstraintViolation, "Not-NULL violation. " <> msg)
-- foreign key violation
("23503", msg) ->
return $ "Foreign key violation. " <> msg
return (ConstraintViolation, "Foreign key violation. " <> msg)
-- unique violation
("23505", msg) ->
return $ "Uniqueness violation. " <> msg
return (ConstraintViolation, "Uniqueness violation. " <> msg)
-- check violation
("23514", msg) ->
return $ "Check constraint violation. " <> msg
return (PermissionError, "Check constraint violation. " <> msg)
-- invalid text representation
("22P02", msg) -> return msg
("22P02", msg) -> return (DataException, msg)
-- invalid parameter value
("22023", msg) -> return (DataException, msg)
-- no unique constraint on the columns
("42P10", _) ->
return "there is no unique or exclusion constraint on target column(s)"
return (ConstraintError, "there is no unique or exclusion constraint on target column(s)")
-- no constraint
("42704", msg) -> return msg
-- invalid parameter value
("22023", msg) -> return msg
("42704", msg) -> return (ConstraintError, msg)
_ -> Nothing


-- validate limit and offset int values
onlyPositiveInt :: MonadError QErr m => Int -> m ()
onlyPositiveInt i = when (i < 0) $ throw400 NotSupported
Expand Down
50 changes: 27 additions & 23 deletions server/src-lib/Hasura/RQL/Types/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -67,35 +67,39 @@ data Code
| AlreadyUntracked
| InvalidParams
| AlreadyInit
| ConstraintViolation
| DataException
-- Graphql error
| NoTables
| ValidationFailed
deriving (Eq)

instance Show Code where
show NotNullViolation = "not-null-violation"
show PermissionDenied = "permission-denied"
show NotExists = "not-exists"
show AlreadyExists = "already-exists"
show AlreadyTracked = "already-tracked"
show AlreadyUntracked = "already-untracked"
show PostgresError = "postgres-error"
show NotSupported = "not-supported"
show DependencyError = "dependency-error"
show InvalidHeaders = "invalid-headers"
show InvalidJSON = "invalid-json"
show AccessDenied = "access-denied"
show ParseFailed = "parse-failed"
show ConstraintError = "constraint-error"
show PermissionError = "permission-error"
show NotFound = "not-found"
show Unexpected = "unexpected"
show UnexpectedPayload = "unexpected-payload"
show NoUpdate = "no-update"
show InvalidParams = "invalid-params"
show AlreadyInit = "already-initialised"
show NoTables = "no-tables"
show ValidationFailed = "validation-failed"
show NotNullViolation = "not-null-violation"
show DataException = "data-exception"
show ConstraintViolation = "constraint-violation"
show PermissionDenied = "permission-denied"
show NotExists = "not-exists"
show AlreadyExists = "already-exists"
show AlreadyTracked = "already-tracked"
show AlreadyUntracked = "already-untracked"
show PostgresError = "postgres-error"
show NotSupported = "not-supported"
show DependencyError = "dependency-error"
show InvalidHeaders = "invalid-headers"
show InvalidJSON = "invalid-json"
show AccessDenied = "access-denied"
show ParseFailed = "parse-failed"
show ConstraintError = "constraint-error"
show PermissionError = "permission-error"
show NotFound = "not-found"
show Unexpected = "unexpected"
show UnexpectedPayload = "unexpected-payload"
show NoUpdate = "no-update"
show InvalidParams = "invalid-params"
show AlreadyInit = "already-initialised"
show NoTables = "no-tables"
show ValidationFailed = "validation-failed"

data QErr
= QErr
Expand Down
39 changes: 29 additions & 10 deletions server/test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,31 @@

module Spec (mkSpecs) where

import Hasura.Prelude hiding (get)
import Network.Wai (Application)
import Hasura.Prelude hiding (get)
import Network.Wai (Application)
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.Matcher

import qualified Data.Aeson as J
import qualified Data.Aeson.Casing as J
import qualified Data.Aeson.TH as J
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Yaml as Y
import qualified Data.Aeson as J
import qualified Data.Aeson.Casing as J
import qualified Data.Aeson.TH as J
import qualified Data.CaseInsensitive as CI
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Yaml as Y

type Headers = HM.HashMap T.Text T.Text

data TestCase
= TestCase
{ tcDescription :: !T.Text
, tcQuery :: !J.Value
, tcUrl :: !T.Text
, tcHeaders :: !(Maybe Headers)
, tcStatus :: !Int
, tcResponse :: !(Maybe J.Value)
-- , tcDependsOn :: !(Maybe TestCase)
} deriving (Show)

Expand All @@ -35,6 +41,7 @@ querySpecFiles =
, "create_author_article_relationship.yaml"
, "create_author_article_permissions.yaml"
, "create_address_resident_relationship_error.yaml"
, "create_user_permission_address.yaml"
]

gqlSpecFiles :: [FilePath]
Expand All @@ -52,6 +59,9 @@ gqlSpecFiles =
, "insert_mutation/person.yaml"
, "insert_mutation/person_array.yaml"
, "insert_mutation/order.yaml"
, "insert_mutation/address_check_constraint_error.yaml"
, "insert_mutation/address_not_null_constraint_error.yaml"
, "insert_mutation/author_unique_constraint_error.yaml"
, "nested_select_query_article.yaml"
, "select_query_article_limit_offset.yaml"
, "select_query_article_limit_offset_error_01.yaml"
Expand All @@ -67,6 +77,7 @@ gqlSpecFiles =
, "update_mutation/person_error_01.yaml"
, "delete_mutation/article.yaml"
, "delete_mutation/article_returning.yaml"
, "delete_mutation/author_foreign_key_violation.yaml"
]

readTestCase :: FilePath -> IO TestCase
Expand All @@ -83,9 +94,17 @@ mkSpec tc = do
let desc = tcDescription tc
url = tcUrl tc
q = tcQuery tc
respStatus = (fromIntegral $ tcStatus tc) :: ResponseMatcher
mHeaders = tcHeaders tc
statusCode = tcStatus tc
mRespBody = tcResponse tc
headers = maybe [] (map toHeader . HM.toList) mHeaders
body = maybe matchAny bodyEquals $ fmap J.encode mRespBody
resp = ResponseMatcher statusCode [] body
it (T.unpack desc) $
post (T.encodeUtf8 url) (J.encode q) `shouldRespondWith` respStatus
request "POST" (T.encodeUtf8 url) headers (J.encode q) `shouldRespondWith` resp
where
matchAny = MatchBody (\_ _ -> Nothing)
toHeader (k, v) = (CI.mk $ T.encodeUtf8 k, T.encodeUtf8 v)


mkSpecs :: IO (SpecWith Application)
Expand Down
2 changes: 1 addition & 1 deletion server/test/testcases/create_tables.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ query:
args:
- type: run_sql
args:
sql: "CREATE TABLE author (id SERIAL PRIMARY KEY, name TEXT)"
sql: "CREATE TABLE author (id SERIAL PRIMARY KEY, name TEXT UNIQUE)"
- type: run_sql
args:
sql: |
Expand Down
11 changes: 11 additions & 0 deletions server/test/testcases/create_user_permission_address.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Create a insert permission on address table for user role
url: /v1/query
status: 200
query:
type: create_insert_permission
args:
table: address
role: merchant
permission:
check:
city: bengaluru
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
description: delete from author table (Foreign Key Violation Error)
url: /v1alpha1/graphql
status: 400
query:
query: |
mutation {
delete_author(where: {id: {_eq: 2}}){
affected_rows
}
}
response:
errors:
- path: $
error: "Foreign key violation. update or delete on table \"author\" violates foreign key constraint \"article_author_id_fkey\" on table \"article\""
code: constraint-violation
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
description: Insert into order table as user role (Check Constraint Error)
url: /v1alpha1/graphql
status: 400
headers:
X-Hasura-Role: merchant
query:
query: |
mutation {
insert_address(objects: [{door_no: "12-21", street: "Madhapur", city: "Hyderabad", resident_id: 1}]){
affected_rows
}
}
response:
errors:
- path: $
error: Check constraint violation. insert check constraint failed
code: permission-error
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
description: Insert into order table as user role (Not Null Constraint Error)
url: /v1alpha1/graphql
status: 400
query:
query: |
mutation {
insert_address(objects: [{street: "koramangala"}]){
returning{
id
street
}
affected_rows
}
}
response:
errors:
- path: $
error: "Not-NULL violation. null value in column \"door_no\" violates not-null constraint"
code: constraint-violation
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
description: Insert into author table as user role (Unique Constraint Error)
url: /v1alpha1/graphql
status: 400
query:
query: |
mutation {
insert_author(objects: [{name: "Author 2"}]){
returning{
id
name
}
affected_rows
}
}
response:
errors:
- path: $
error: "Uniqueness violation. duplicate key value violates unique constraint \"author_name_key\""
code: constraint-violation