Make sure entity declaration is not used in the Wasp file (#2152)

This commit is contained in:
Mihovil Ilakovac 2024-07-09 23:44:56 +02:00 committed by GitHub
parent 0eef00d5d9
commit 8f136fb5cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 136 additions and 8 deletions

View File

@ -127,6 +127,7 @@ import Wasp.Analyzer.AnalyzeError
)
import Wasp.Analyzer.Evaluator (Decl, evaluate, takeDecls)
import Wasp.Analyzer.Parser (parseStatements)
import Wasp.Analyzer.Parser.Valid (validateAst)
import Wasp.Analyzer.Prisma (injectEntitiesFromPrismaSchema)
import Wasp.Analyzer.StdTypeDefinitions (stdTypes)
import Wasp.Analyzer.TypeChecker (typeCheck)
@ -138,6 +139,25 @@ import qualified Wasp.Psl.Ast.Schema as Psl.Schema
analyze :: Psl.Schema.Schema -> String -> Either [AnalyzeError] [Decl]
analyze prismaSchemaAst =
(left (map ParseError) . parseStatements)
{--
Why introduce AST validation and not just throw a ParseError from the parser?
We want to support the `entity` declaration in the AST but not in the Wasp source
file.
This was the fastest and cleanest (e.g. not having to hack the type checker) way
to allow users to define entities in the Prisma schema file. We are parsing
the `schema.prisma` file and injecting the models into the Wasp AST as entity
statements.
We validate the AST to prevent users from defining entities in the Wasp source
file since we don't want to allow defining entities in two places.
Wasp file -(parse)-> AST -(validate)-> AST -(injectEntities)-> AST (...)
^ disallow entities here
^ inject entities here
--}
>=> (left ((: []) . ValidationError) . validateAst)
>=> injectEntitiesFromPrismaSchema prismaSchemaAst
>=> (left ((: []) . TypeError) . typeCheck stdTypes)
>=> (left ((: []) . EvaluationError) . evaluate stdTypes)

View File

@ -16,10 +16,12 @@ data AnalyzeError
= ParseError PE.ParseError
| TypeError TE.TypeError
| EvaluationError EE.EvaluationError
| ValidationError (String, Ctx)
deriving (Show, Eq)
getErrorMessageAndCtx :: AnalyzeError -> (String, Ctx)
getErrorMessageAndCtx = \case
ParseError e -> first (("Parse error:\n" ++) . indent 2) $ PE.getErrorMessageAndCtx e
ValidationError (msg, ctx) -> ("Validation error:\n" ++ indent 2 msg, ctx)
TypeError e -> first (("Type error:\n" ++) . indent 2) $ TE.getErrorMessageAndCtx e
EvaluationError e -> first (("Evaluation error:\n" ++) . indent 2) $ EE.getErrorMessageAndCtx e

View File

@ -0,0 +1,26 @@
module Wasp.Analyzer.Parser.Valid
( validateAst,
)
where
import Data.List (find)
import qualified Wasp.Analyzer.Parser as P
import Wasp.Analyzer.StdTypeDefinitions.Entity (entityDeclTypeName)
validateAst :: P.AST -> Either (String, P.Ctx) P.AST
validateAst = validateNoEntityDeclInWaspFile
validateNoEntityDeclInWaspFile :: P.AST -> Either (String, P.Ctx) P.AST
validateNoEntityDeclInWaspFile ast@(P.AST stmts) = case findEntityStmt stmts of
Just (P.WithCtx ctx _) -> Left (entitiesNoLongerSupportedError, ctx)
Nothing -> Right ast
where
findEntityStmt :: [P.WithCtx P.Stmt] -> Maybe (P.WithCtx P.Stmt)
findEntityStmt =
find
( \(P.WithCtx _ (P.Decl declTypeName _ _)) -> declTypeName == entityDeclTypeName
)
entitiesNoLongerSupportedError :: String
entitiesNoLongerSupportedError =
"Entities can no longer be defined in the .wasp file. You should migrate your entities to the schema.prisma file. Read more: https://wasp-lang.dev/docs/migrate-from-0-13-to-0-14#migrate-to-the-new-schemaprisma-file"

View File

@ -1,7 +1,10 @@
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Wasp.Analyzer.StdTypeDefinitions.Entity () where
module Wasp.Analyzer.StdTypeDefinitions.Entity
( entityDeclTypeName,
)
where
import Control.Arrow (left)
import Wasp.Analyzer.Evaluator.EvaluationError (mkEvaluationError)
@ -16,7 +19,7 @@ import qualified Wasp.Psl.Parser.Model
instance IsDeclType Entity where
declType =
DeclType
{ dtName = "entity",
{ dtName = entityDeclTypeName,
dtBodyType = Type.QuoterType "psl",
dtEvaluate = \typeDefinitions bindings declName expr ->
Decl.makeDecl @Entity declName <$> declEvaluate typeDefinitions bindings expr
@ -27,3 +30,6 @@ instance IsDeclType Entity where
left (ER.mkEvaluationError ctx . ER.ParseError . ER.EvaluationParseErrorParsec) $
makeEntity <$> Wasp.Psl.Parser.Model.parseBody pslString
_ -> Left $ mkEvaluationError ctx $ ER.ExpectedType (Type.QuoterType "psl") (TC.AST.exprType expr)
entityDeclTypeName :: String
entityDeclTypeName = "entity"

View File

@ -27,15 +27,15 @@ data TypeError'
-- We use "unify" in the TypeChecker when trying to infer the common type for
-- typed expressions that we know should be of the same type (e.g. for
-- elements in the list).
= UnificationError TypeCoercionError
= UnificationError TypeCoercionError
-- | Type coercion error that occurs when trying to use the typed expression
-- of type T1 where T2 is expected. If T2 is a super type of T1 and T1 can be
-- safely coerced to T2, no problem, but if not, we get this error.
| CoercionError TypeCoercionError
| NoDeclarationType TypeName
| UndefinedIdentifier Identifier
| QuoterUnknownTag QuoterTag
| DictDuplicateField DictFieldName
| CoercionError TypeCoercionError
| NoDeclarationType TypeName
| UndefinedIdentifier Identifier
| QuoterUnknownTag QuoterTag
| DictDuplicateField DictFieldName
deriving (Eq, Show)
{- ORMOLU_ENABLE -}

View File

@ -0,0 +1,71 @@
module Analyzer.ValidTest where
import Data.Either (fromRight, isRight)
import Test.Tasty.Hspec
import Wasp.Analyzer.Parser hiding (withCtx)
import qualified Wasp.Analyzer.Parser as P
import Wasp.Analyzer.Parser.Valid (validateAst)
import qualified Wasp.Version as WV
spec_ValidateAst :: Spec
spec_ValidateAst = do
it "Returns an error when entities are used" $ do
validateAndParseSource (waspSourceLines ++ entityDeclarationLines)
`shouldBe` Left
( "Entities can no longer be defined in the .wasp file. You should migrate your entities to the schema.prisma file. Read more: https://wasp-lang.dev/docs/migrate-from-0-13-to-0-14#migrate-to-the-new-schemaprisma-file",
P.Ctx
( P.SourceRegion
(P.SourcePosition 34 1)
(P.SourcePosition 37 5)
)
)
it "Returns AST when everything is correct" $ do
isRight (validateAndParseSource waspSourceLines) `shouldBe` True
where
validateAndParseSource = validateAst . parseSource
parseSource = fromRight (error "Parsing went wrong") . parseStatements . unlines
waspSourceLines =
[ "app Todo {",
" wasp: {",
" version: \"^" ++ show WV.waspVersion ++ "\",",
" },",
" title: \"Todo App\",",
" head: [\"foo\", \"bar\"],",
" auth: {",
" userEntity: User,",
" methods: {",
" usernameAndPassword: {",
" userSignupFields: import { getUserFields } from \"@src/auth/signup.js\",",
" }",
" },",
" onAuthFailedRedirectTo: \"/\",",
" },",
"}",
"",
"page HomePage {",
" component: import Home from \"@src/pages/Main\"",
"}",
"",
"route HomeRoute { path: \"/\", to: HomePage }",
"",
"query getUsers {",
" fn: import { getAllUsers } from \"@src/foo.js\",",
" entities: [User]",
"}",
"",
"action updateUser {",
" fn: import { updateUser } from \"@src/foo.js\",",
" entities: [User],",
" auth: true",
"}"
]
entityDeclarationLines =
[ "entity User {=psl",
" id Int @id @default(autoincrement())",
" email String @unique",
"psl=}"
]

View File

@ -201,6 +201,7 @@ library
Wasp.Analyzer.Parser.SourceSpan
Wasp.Analyzer.Parser.Token
Wasp.Analyzer.Parser.TokenSet
Wasp.Analyzer.Parser.Valid
Wasp.Analyzer.StdTypeDefinitions
Wasp.Analyzer.StdTypeDefinitions.App.Dependency
Wasp.Analyzer.StdTypeDefinitions.Entity
@ -612,6 +613,7 @@ test-suite waspc-test
Analyzer.TestUtil
Analyzer.TypeChecker.InternalTest
Analyzer.TypeCheckerTest
Analyzer.ValidTest
AnalyzerTest
AppSpec.ValidTest
AppSpec.EntityTest

View File

@ -108,6 +108,7 @@ waspErrorAsPrettyEditorMessage = Text.pack . fst . W.getErrorMessageAndCtx
waspErrorSource :: W.AnalyzeError -> Text
waspErrorSource (W.ParseError _) = "parse"
waspErrorSource (W.ValidationError _) = "validate"
waspErrorSource (W.TypeError _) = "typecheck"
waspErrorSource (W.EvaluationError _) = "evaluate"