From c61f08eaa3b6d4282ec866400bd8a47dfb6c8a07 Mon Sep 17 00:00:00 2001 From: Mihovil Ilakovac Date: Tue, 1 Oct 2024 13:54:57 +0200 Subject: [PATCH] Validation for @default attribute on userEntity (#2311) --- waspc/src/Wasp/AppSpec/Entity.hs | 19 +--------------- waspc/src/Wasp/AppSpec/Valid.hs | 15 ++++++++----- waspc/test/AppSpec/EntityTest.hs | 16 -------------- waspc/test/AppSpec/ValidTest.hs | 37 ++++++++++++++++++++++++++++---- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/waspc/src/Wasp/AppSpec/Entity.hs b/waspc/src/Wasp/AppSpec/Entity.hs index ca5ed93a63..d6642b49ab 100644 --- a/waspc/src/Wasp/AppSpec/Entity.hs +++ b/waspc/src/Wasp/AppSpec/Entity.hs @@ -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], @@ -54,18 +50,5 @@ getPslModelBody = pslModelBody getIdField :: Entity -> Maybe Psl.Model.Field getIdField = findIdField . getPslModelBody -isFieldUnique :: String -> Entity -> Maybe Bool -isFieldUnique fieldName = doesFieldHaveAttribute fieldName "unique" - -doesFieldHaveAttribute :: String -> String -> Entity -> Maybe Bool -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 diff --git a/waspc/src/Wasp/AppSpec/Valid.hs b/waspc/src/Wasp/AppSpec/Valid.hs index 264f35be04..6368b895ef 100644 --- a/waspc/src/Wasp/AppSpec/Valid.hs +++ b/waspc/src/Wasp/AppSpec/Valid.hs @@ -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 @@ -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] 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 diff --git a/waspc/test/AppSpec/EntityTest.hs b/waspc/test/AppSpec/EntityTest.hs index 99c5036ecd..a383956617 100644 --- a/waspc/test/AppSpec/EntityTest.hs +++ b/waspc/test/AppSpec/EntityTest.hs @@ -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 $ diff --git a/waspc/test/AppSpec/ValidTest.hs b/waspc/test/AppSpec/ValidTest.hs index d61908c6fc..140fa2ba9f 100644 --- a/waspc/test/AppSpec/ValidTest.hs +++ b/waspc/test/AppSpec/ValidTest.hs @@ -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 @@ -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 = @@ -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" []) + ] } ] }