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

Use native DB type in injected auth models if user uses it #2243

Merged
merged 5 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
24 changes: 22 additions & 2 deletions waspc/src/Wasp/Generator/DbGenerator/Auth.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
module Wasp.Generator.DbGenerator.Auth where
module Wasp.Generator.DbGenerator.Auth
( injectAuth,
authEntityName,
authIdentityEntityName,
sessionEntityName,
userFieldOnAuthEntityName,
authFieldOnUserEntityName,
identitiesFieldOnAuthEntityName,
authFieldOnAuthIdentityEntityName,
)
where

import Data.Maybe (fromJust)
import qualified Data.Text as T
Expand All @@ -9,7 +19,9 @@ import Wasp.Generator.Monad
GeneratorError (GenericGeneratorError),
logAndThrowGeneratorError,
)
import qualified Wasp.Psl.Ast.Attribute as Psl.Attribute
import qualified Wasp.Psl.Ast.Model as Psl.Model
import qualified Wasp.Psl.Generator.Attribute as Psl.Generator.Attribute
import qualified Wasp.Psl.Parser.Model as Psl.Parser.Model
import qualified Wasp.Util as Util

Expand Down Expand Up @@ -111,7 +123,7 @@ makeAuthEntity userEntityIdField (userEntityName, _) = case Psl.Parser.Model.par
T.unpack
[trimming|
id ${authEntityIdTypeText} @id @default(uuid())
userId ${userEntityIdTypeText}? @unique
userId ${userEntityIdTypeText}? ${userEntityIdFieldAttributesText}
${userFieldOnAuthEntityNameText} ${userEntityNameText}? @relation(fields: [userId], references: [${userEntityIdFieldName}], onDelete: Cascade)
${identitiesFieldOnAuthEntityNameText} ${authIdentityEntityNameText}[]
${sessionsFieldOnAuthEntityNameText} ${sessionEntityNameText}[]
Expand All @@ -127,6 +139,14 @@ makeAuthEntity userEntityIdField (userEntityName, _) = case Psl.Parser.Model.par

userEntityIdTypeText = T.pack $ show . Psl.Model._type $ userEntityIdField
userEntityIdFieldName = T.pack $ Psl.Model._name userEntityIdField
userEntityIdFieldAttributesText = T.pack $ makeUserEntityIdFieldAttributes userEntityIdField

makeUserEntityIdFieldAttributes :: Psl.Model.Field -> String
makeUserEntityIdFieldAttributes field = unwords attrs
where
attrs = waspDefinedAttrs ++ (Psl.Generator.Attribute.generateAttribute <$> userDefinedNativeDbTypeAttributes)
waspDefinedAttrs = ["@unique"]
userDefinedNativeDbTypeAttributes = filter Psl.Attribute.isNativeDbTypeAttr $ Psl.Model._attrs field

makeSessionEntity :: Generator (String, AS.Entity.Entity)
makeSessionEntity = case Psl.Parser.Model.parseBody sessionEntityPslBody of
Expand Down
1 change: 1 addition & 0 deletions waspc/src/Wasp/Generator/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ data GeneratorState = GeneratorState
}

data GeneratorError = GenericGeneratorError String
deriving (Eq)

instance Show GeneratorError where
show (GenericGeneratorError e) = e
Expand Down
6 changes: 6 additions & 0 deletions waspc/src/Wasp/Psl/Ast/Attribute.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

module Wasp.Psl.Ast.Attribute
( Attribute (..),
isNativeDbTypeAttr,
)
where

import Data.Data (Data)
import Data.List (isPrefixOf)
import qualified Wasp.Psl.Ast.Argument as Psl.Argument
import Prelude hiding (Enum)

Expand All @@ -19,3 +21,7 @@ data Attribute = Attribute
_attrArgs :: [Psl.Argument.Argument]
}
deriving (Show, Eq, Data)

-- | @db.Uuid or @db.String or @db.VarChar are examples of native db types.
isNativeDbTypeAttr :: Attribute -> Bool
isNativeDbTypeAttr = isPrefixOf "db." . _attrName
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
isNativeDbTypeAttr = isPrefixOf "db." . _attrName
isNativeDbTypeAttr = ("db." `isPrefixOf`) . _attrName

reads nicer :D

239 changes: 239 additions & 0 deletions waspc/test/Generator/AuthInjectionTest.hs
Copy link
Member

Choose a reason for hiding this comment

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

A lot of AST here which is a bit hard to read / maintain -> any way we might be able to replace it with actual PSL, here in the test? That is parsed either during test runtime, or even test compile time if that is doable (although I think I remember we had hard time with that).
If not, never mind, but if yes, I think it could make the test easier to read and maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is parsed either during test runtime

I've done this bit since it was similar to something we already did with the full schema file.

getPrismaSchema :: T.Text -> Psl.Schema.Schema

Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
module Generator.AuthInjectionTest where

import Data.Maybe (maybeToList)
import Test.Tasty.Hspec
import qualified Wasp.AppSpec.Entity as AS.Entity
import Wasp.Generator.DbGenerator.Auth (injectAuth)
import Wasp.Generator.Monad (runGenerator)
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

data UserEntityIdField = UserEntityIdField
{ _type :: Psl.Model.FieldType,
_nativeDbType :: Maybe Psl.Attribute.Attribute
}

spec_GeneratorAuthInjectionTest :: Spec
spec_GeneratorAuthInjectionTest = do
describe "injectAuth" $ do
it "injects auth entities and user entity relation" $ do
testAuthInjection $
UserEntityIdField
{ _type = Psl.Model.Int,
_nativeDbType = Nothing
}

it "injects auth entities and user entity relation (user ID is a native db field)" $ do
testAuthInjection $
UserEntityIdField
{ _type = Psl.Model.String,
_nativeDbType = Just $ Psl.Attribute.Attribute "db.Uuid" []
}
where
testAuthInjection :: UserEntityIdField -> Expectation
testAuthInjection
UserEntityIdField
{ _type = userEntityIdFieldType,
_nativeDbType = maybeUserEntityIdFieldNativeDbType
} = do
let userEntityIdField =
Psl.Model.ElementField $
Psl.Model.Field
"id"
userEntityIdFieldType
[]
(Psl.Attribute.Attribute "id" [] : maybeToList maybeUserEntityIdFieldNativeDbType)
let userEntity =
( "User",
AS.Entity.makeEntity $
Psl.Model.Body [userEntityIdField]
)
let authEntityRelation =
Psl.Model.ElementField $
Psl.Model.Field
"auth"
(Psl.Model.UserType "Auth")
[Psl.Model.Optional]
[]
let userEntityWithInjectedRelationship =
( "User",
AS.Entity.makeEntity $
Psl.Model.Body [userEntityIdField, authEntityRelation]
)
let authEntity = makeAuthEntity userEntityIdFieldType maybeUserEntityIdFieldNativeDbType

let allEntities = [userEntity, someOtherEntity]
let (_generatorWarnings, generatorResult) = runGenerator $ injectAuth allEntities userEntity
in generatorResult
`shouldBe` Right
[ userEntityWithInjectedRelationship,
someOtherEntity,
authEntity,
authIdentityEntity,
sessionEntity
]

makeAuthEntity :: Psl.Model.FieldType -> Maybe Psl.Attribute.Attribute -> (String, AS.Entity.Entity)
makeAuthEntity userEntityIdFieldType maybeUserEntityIdFieldNativeDbType =
let userIdField = makeAuthEntityUserIdField userEntityIdFieldType maybeUserEntityIdFieldNativeDbType
in ( "Auth",
AS.Entity.makeEntity
( Psl.Model.Body
[ Psl.Model.ElementField $
Psl.Model.Field
"id"
Psl.Model.String
[]
[ Psl.Attribute.Attribute "id" [],
Psl.Attribute.Attribute "default" [Psl.Argument.ArgUnnamed $ Psl.Argument.FuncExpr "uuid" []]
],
userIdField,
Psl.Model.ElementField $
Psl.Model.Field
"user"
(Psl.Model.UserType "User")
[Psl.Model.Optional]
[ Psl.Attribute.Attribute
"relation"
[ Psl.Argument.ArgNamed "fields" (Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "userId"]),
Psl.Argument.ArgNamed "references" (Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "id"]),
Psl.Argument.ArgNamed "onDelete" (Psl.Argument.IdentifierExpr "Cascade")
]
],
Psl.Model.ElementField $
Psl.Model.Field
"identities"
(Psl.Model.UserType "AuthIdentity")
[Psl.Model.List]
[],
Psl.Model.ElementField $
Psl.Model.Field
"sessions"
(Psl.Model.UserType "Session")
[Psl.Model.List]
[]
]
)
)

makeAuthEntityUserIdField :: Psl.Model.FieldType -> Maybe Psl.Attribute.Attribute -> Psl.Model.Element
makeAuthEntityUserIdField userEntityIdFieldType maybeUserEntityIdFieldNativeDbType =
let userIdFieldAttributes = (Psl.Attribute.Attribute "unique" [] : maybeToList maybeUserEntityIdFieldNativeDbType)
in Psl.Model.ElementField $
Psl.Model.Field
"userId"
userEntityIdFieldType
[Psl.Model.Optional]
userIdFieldAttributes

authIdentityEntity =
( "AuthIdentity",
AS.Entity.makeEntity
( Psl.Model.Body
[ Psl.Model.ElementField $
Psl.Model.Field
"providerName"
Psl.Model.String
[]
[],
Psl.Model.ElementField $
Psl.Model.Field
"providerUserId"
Psl.Model.String
[]
[],
Psl.Model.ElementField $
Psl.Model.Field
"providerData"
Psl.Model.String
[]
[ Psl.Attribute.Attribute "default" [Psl.Argument.ArgUnnamed $ Psl.Argument.StringExpr "{}"]
],
Psl.Model.ElementField $
Psl.Model.Field
"authId"
Psl.Model.String
[]
[],
Psl.Model.ElementField $
Psl.Model.Field
"auth"
(Psl.Model.UserType "Auth")
[]
[ Psl.Attribute.Attribute
"relation"
[ Psl.Argument.ArgNamed "fields" (Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "authId"]),
Psl.Argument.ArgNamed "references" (Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "id"]),
Psl.Argument.ArgNamed "onDelete" (Psl.Argument.IdentifierExpr "Cascade")
]
],
Psl.Model.ElementBlockAttribute $
Psl.Attribute.Attribute "id" [Psl.Argument.ArgUnnamed $ Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "providerName", Psl.Argument.IdentifierExpr "providerUserId"]]
]
)
)

sessionEntity =
( "Session",
AS.Entity.makeEntity
( Psl.Model.Body
[ Psl.Model.ElementField $
Psl.Model.Field
"id"
Psl.Model.String
[]
[ Psl.Attribute.Attribute "id" [],
Psl.Attribute.Attribute "unique" []
],
Psl.Model.ElementField $
Psl.Model.Field
"expiresAt"
Psl.Model.DateTime
[]
[],
Psl.Model.ElementField $
Psl.Model.Field
"userId"
Psl.Model.String
[]
[],
Psl.Model.ElementField $
Psl.Model.Field
"auth"
(Psl.Model.UserType "Auth")
[]
[ Psl.Attribute.Attribute
"relation"
[ Psl.Argument.ArgNamed "references" (Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "id"]),
Psl.Argument.ArgNamed "fields" (Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "userId"]),
Psl.Argument.ArgNamed "onDelete" (Psl.Argument.IdentifierExpr "Cascade")
]
],
Psl.Model.ElementBlockAttribute $
Psl.Attribute.Attribute
"index"
[ Psl.Argument.ArgUnnamed $ Psl.Argument.ArrayExpr [Psl.Argument.IdentifierExpr "userId"]
]
]
)
)

someOtherEntity =
( "SomeOtherEntity",
AS.Entity.makeEntity
( Psl.Model.Body
[ Psl.Model.ElementField $
Psl.Model.Field
"id"
Psl.Model.Int
[]
[ Psl.Attribute.Attribute "id" [],
Psl.Attribute.Attribute
"default"
[ Psl.Argument.ArgUnnamed $ Psl.Argument.FuncExpr "autoincrement" []
]
]
]
)
)
1 change: 1 addition & 0 deletions waspc/waspc.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ test-suite waspc-test
ErrorTest
FilePath.ExtraTest
Fixtures
Generator.AuthInjectionTest
Generator.DbGeneratorTest
Generator.FileDraft.CopyFileDraftTest
Generator.FileDraft.CopyAndModifyTextFileDraftTest
Expand Down
Loading