Skip to content
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

Validation for @default attribute on userEntity #2311

Merged
merged 3 commits into from
Oct 1, 2024
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
19 changes: 1 addition & 18 deletions waspc/src/Wasp/AppSpec/Entity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,17 @@ module Wasp.AppSpec.Entity
getPslModelBody,
getIdField,
getIdBlockAttribute,
isFieldUnique,
-- only for testing:
doesFieldHaveAttribute,
)
where

import Data.Aeson (FromJSON (parseJSON))
import Data.Data (Data)
import Data.List (find)
import Wasp.AppSpec.Core.IsDecl (IsDecl)
import Wasp.AppSpec.Entity.Field (Field)
import qualified Wasp.AppSpec.Entity.Field as Field
import qualified Wasp.Psl.Ast.Attribute as Psl.Attribute
import qualified Wasp.Psl.Ast.Model as Psl.Model
import Wasp.Psl.Util (doesPslFieldHaveAttribute, findIdBlockAttribute, findIdField)
import Wasp.Psl.Util (findIdBlockAttribute, findIdField)

data Entity = Entity
{ fields :: ![Field],
Expand Down Expand Up @@ -54,18 +50,5 @@ getPslModelBody = pslModelBody
getIdField :: Entity -> Maybe Psl.Model.Field
getIdField = findIdField . getPslModelBody

isFieldUnique :: String -> Entity -> Maybe Bool
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was unused.

isFieldUnique fieldName = doesFieldHaveAttribute fieldName "unique"

doesFieldHaveAttribute :: String -> String -> Entity -> Maybe Bool
Copy link
Contributor Author

@infomiho infomiho Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a weirdly specific function which I tried to rewrite but in the end it was easier just to use the lower level helper doesPslFieldHaveAttribute directly in the one place where we needed to check if a field has an attribute.

doesFieldHaveAttribute fieldName attrName entity =
doesPslFieldHaveAttribute attrName <$> findPslFieldByName fieldName entity

findPslFieldByName :: String -> Entity -> Maybe Psl.Model.Field
findPslFieldByName fieldName Entity {pslModelBody = Psl.Model.Body elements} =
find isField [field | (Psl.Model.ElementField field) <- elements]
where
isField Psl.Model.Field {_name = name} = name == fieldName

getIdBlockAttribute :: Entity -> Maybe Psl.Attribute.Attribute
getIdBlockAttribute = findIdBlockAttribute . getPslModelBody
15 changes: 10 additions & 5 deletions waspc/src/Wasp/AppSpec/Valid.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import Wasp.Generator.Crud (crudDeclarationToOperationsList)
import Wasp.Node.Version (oldestWaspSupportedNodeVersion)
import qualified Wasp.Node.Version as V
import qualified Wasp.Psl.Ast.Model as Psl.Model
import qualified Wasp.Psl.Util as Psl.Util
import Wasp.Psl.Valid (getValidDbSystemFromPrismaSchema)
import qualified Wasp.SemanticVersion as SV
import qualified Wasp.SemanticVersion.VersionBound as SVB
Expand Down Expand Up @@ -128,14 +129,18 @@ validateUserEntity spec =
case App.auth (snd $ getApp spec) of
Nothing -> []
Just auth ->
[ GenericValidationError $ "Entity '" ++ userEntityName ++ "' (referenced by app.auth.userEntity) must have an ID field (specified with the '@id' attribute)"
| isNothing idFieldType
]
case Entity.getIdField userEntity of
Nothing -> [userEntityMissingIdFieldError]
Just idField ->
if Psl.Util.doesPslFieldHaveAttribute "default" idField
then []
else [userEntityIdFieldMissingDefaultAttrError]
Comment on lines +132 to +137
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Might be tempting to use Either here as a monad? Btw there is even this thing: https://hackage.haskell.org/package/validation .
But probably just not to complicate, it is simple enough for now, I am writing this just to give ideas for the future!

where
idFieldType = Entity.getIdField userEntity

(userEntityName, userEntity) = AS.resolveRef spec (Auth.userEntity auth)

userEntityMissingIdFieldError = GenericValidationError $ "Entity '" ++ userEntityName ++ "' (referenced by app.auth.userEntity) must have an ID field (specified with the '@id' attribute)"
userEntityIdFieldMissingDefaultAttrError = GenericValidationError $ "Entity '" ++ userEntityName ++ "' (referenced by app.auth.userEntity) must have an ID field (specified with the '@id' attribute) with a default value"

validateAppAuthIsSetIfAnyPageRequiresAuth :: AppSpec -> [ValidationError]
validateAppAuthIsSetIfAnyPageRequiresAuth spec =
[ GenericValidationError
Expand Down
16 changes: 0 additions & 16 deletions waspc/test/AppSpec/EntityTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,6 @@ spec_AppSpecEntityTest = do
getIdField entityWithIdField `shouldBe` Just idField
it "Returns Nothing if primary field doesn't exist" $ do
getIdField entityWithoutIdField `shouldBe` Nothing

describe "isFieldUnique" $ do
it "Returns Nothing if the field doesn't exist on the entity" $ do
Entity.isFieldUnique "nonExistingField" entityWithoutIdField `shouldBe` Nothing
it "Returns Just False if the field exists on the entity but isn't unique" $ do
Entity.isFieldUnique "description" entityWithIdField `shouldBe` Just False
it "Returns Just True if the field exists and is unique" $ do
Entity.isFieldUnique "id" entityWithIdField `shouldBe` Just True

describe "doesFieldHaveAttribute" $ do
it "Returns Nothing if the field doesn't exist on the entity" $ do
Entity.doesFieldHaveAttribute "nonExistingField" "unique" entityWithoutIdField `shouldBe` Nothing
it "Returns Just False if the field exists on the entity but doesn't have the required attribute" $ do
Entity.doesFieldHaveAttribute "description" "id" entityWithIdField `shouldBe` Just False
it "Returns Just True if the field exists on the entity and has the required attribute" $ do
Entity.doesFieldHaveAttribute "id" "id" entityWithIdField `shouldBe` Just True
where
entityWithIdField =
Entity.makeEntity $
Expand Down
37 changes: 33 additions & 4 deletions waspc/test/AppSpec/ValidTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import qualified Wasp.AppSpec.Query as AS.Query
import qualified Wasp.AppSpec.Route as AS.Route
import qualified Wasp.AppSpec.Valid as ASV
import qualified Wasp.ExternalConfig.PackageJson as EC.PackageJson
import qualified Wasp.Psl.Ast.Argument as Psl.Argument
import qualified Wasp.Psl.Ast.Attribute as Psl.Attribute
import qualified Wasp.Psl.Ast.Model as Psl.Model
import qualified Wasp.SemanticVersion as SV
Expand Down Expand Up @@ -260,22 +261,44 @@ spec_AppSpecValid = do
AS.Decl.makeDecl userEntityName (userEntity :: AS.Entity.Entity)
]
}
let invalidUserEntity =
let invalidUserEntityWithoutIdField =
AS.Entity.makeEntity
( Psl.Model.Body
[]
)
let invalidUserEntityWithoutDefaultAttr =
AS.Entity.makeEntity
( Psl.Model.Body
[ Psl.Model.ElementField $
Psl.Model.Field
{ Psl.Model._name = "id",
Psl.Model._type = Psl.Model.String,
Psl.Model._typeModifiers = [],
Psl.Model._attrs =
[ Psl.Attribute.Attribute
{ Psl.Attribute._attrName = "id",
Psl.Attribute._attrArgs = []
}
]
}
]
)

it "returns no error if app.auth is not set, regardless of shape of user entity" $ do
ASV.validateAppSpec (makeSpec Nothing invalidUserEntity) `shouldBe` []
ASV.validateAppSpec (makeSpec Nothing invalidUserEntityWithoutIdField) `shouldBe` []
ASV.validateAppSpec (makeSpec Nothing validUserEntity) `shouldBe` []
it "returns no error if app.auth is set and user entity is of valid shape" $ do
ASV.validateAppSpec (makeSpec (Just validAppAuth) validUserEntity) `shouldBe` []
it "returns an error if app.auth is set and user entity is of invalid shape" $ do
ASV.validateAppSpec (makeSpec (Just validAppAuth) invalidUserEntity)
it "returns an error if app.auth is set and user entity wihtout an ID field" $ do
ASV.validateAppSpec (makeSpec (Just validAppAuth) invalidUserEntityWithoutIdField)
`shouldBe` [ Valid.GenericValidationError
"Entity 'User' (referenced by app.auth.userEntity) must have an ID field (specified with the '@id' attribute)"
]
it "returns an error if app.auth is set and user entity with an ID field that is missing the default attr" $ do
ASV.validateAppSpec (makeSpec (Just validAppAuth) invalidUserEntityWithoutDefaultAttr)
`shouldBe` [ Valid.GenericValidationError
"Entity 'User' (referenced by app.auth.userEntity) must have an ID field (specified with the '@id' attribute) with a default value"
]

describe "should validate email sender setup." $ do
let emailAuthConfig =
Expand Down Expand Up @@ -414,6 +437,12 @@ spec_AppSpecValid = do
[ Psl.Attribute.Attribute
{ Psl.Attribute._attrName = "id",
Psl.Attribute._attrArgs = []
},
Psl.Attribute.Attribute
{ Psl.Attribute._attrName = "default",
Psl.Attribute._attrArgs =
[ Psl.Argument.ArgUnnamed (Psl.Argument.FuncExpr "autoincrement" [])
]
}
]
}
Expand Down
Loading