From b74eb88ff0ae341179b99dff0ce34bd553bb26de Mon Sep 17 00:00:00 2001 From: Shayne Czyzewski Date: Wed, 12 Jan 2022 11:28:21 -0500 Subject: [PATCH] Makes DbGenerator responsible for movement of migrations dir (#420) Closes #105 --- waspc/cli/Wasp/Cli/Command/Compile.hs | 6 -- waspc/cli/Wasp/Cli/Command/Db/Migrate.hs | 79 +++---------------- waspc/package.yaml | 1 + waspc/src/Wasp/Generator/DbGenerator.hs | 27 ++++--- .../Wasp/Generator/DbGenerator/Operations.hs | 31 ++++++-- waspc/src/Wasp/Generator/FileDraft.hs | 18 ++++- .../Generator/FileDraft/CopyDirFileDraft.hs | 40 ++++++++++ .../Generator/FileDraft/WriteableMonad.hs | 19 ++++- waspc/test/Generator/MockWriteableMonad.hs | 24 ++++-- waspc/test/Generator/WebAppGeneratorTest.hs | 2 + 10 files changed, 146 insertions(+), 101 deletions(-) create mode 100644 waspc/src/Wasp/Generator/FileDraft/CopyDirFileDraft.hs diff --git a/waspc/cli/Wasp/Cli/Command/Compile.hs b/waspc/cli/Wasp/Cli/Command/Compile.hs index 698ec694c..85cea2d96 100644 --- a/waspc/cli/Wasp/Cli/Command/Compile.hs +++ b/waspc/cli/Wasp/Cli/Command/Compile.hs @@ -13,10 +13,6 @@ import Wasp.Cli.Command.Common ( findWaspProjectRootDirFromCwd, waspSaysC, ) -import Wasp.Cli.Command.Db.Migrate - ( MigrationDirCopyDirection (..), - copyDbMigrationsDir, - ) import qualified Wasp.Cli.Common as Common import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage) import Wasp.Common (WaspProjectDir) @@ -59,5 +55,3 @@ compileIOWithOptions options waspProjectDir outDir = runExceptT $ do -- TODO: Use throwIO instead of Either to return exceptions? liftIO (Wasp.Lib.compile waspProjectDir outDir options) >>= either throwError return - liftIO (copyDbMigrationsDir CopyMigDirDown waspProjectDir outDir) - >>= maybe (return ()) (throwError . ("Copying migration folder failed: " ++)) diff --git a/waspc/cli/Wasp/Cli/Command/Db/Migrate.hs b/waspc/cli/Wasp/Cli/Command/Db/Migrate.hs index ee0762d8f..26a6d75e7 100644 --- a/waspc/cli/Wasp/Cli/Command/Db/Migrate.hs +++ b/waspc/cli/Wasp/Cli/Command/Db/Migrate.hs @@ -1,19 +1,13 @@ module Wasp.Cli.Command.Db.Migrate ( migrateDev, - copyDbMigrationsDir, - MigrationDirCopyDirection (..), ) where -import Control.Monad.Catch (catch) import Control.Monad.Except (throwError) import Control.Monad.IO.Class (liftIO) -- Wasp generator interface. -import qualified Path as P -import qualified Path.IO as PathIO -import StrongPath (Abs, Dir, Path', ()) -import qualified StrongPath.Path as SP.Path +import StrongPath (()) import Wasp.Cli.Command (Command, CommandError (..)) import Wasp.Cli.Command.Common ( findWaspProjectRootDirFromCwd, @@ -21,11 +15,12 @@ import Wasp.Cli.Command.Common ) import qualified Wasp.Cli.Common as Cli.Common import Wasp.Cli.Terminal (asWaspFailureMessage, asWaspStartMessage, asWaspSuccessMessage) -import Wasp.Common (WaspProjectDir, dbMigrationsDirInWaspProjectDir) -import Wasp.Generator.Common (ProjectRootDir) -import Wasp.Generator.DbGenerator (dbMigrationsDirInDbRootDir, dbRootDirInProjectRootDir) +import qualified Wasp.Common import qualified Wasp.Generator.DbGenerator.Operations as DbOps +-- | NOTE(shayne): Performs database schema migration (based on current schema) in the generated project. +-- This assumes the wasp project migrations dir was copied from wasp source project by a previous compile. +-- The migrate function takes care of copying migrations from the generated project back to the source code. migrateDev :: Command () migrateDev = do waspProjectDir <- findWaspProjectRootDirFromCwd @@ -34,69 +29,13 @@ migrateDev = do Cli.Common.dotWaspDirInWaspProjectDir Cli.Common.generatedCodeDirInDotWaspDir - -- TODO(matija): It might make sense that this (copying migrations folder from source to - -- the generated proejct) is responsibility of the generator. Since migrations can also be - -- considered part of a "source" code, then generator could take care of it and this command - -- wouldn't have to deal with it. We opened an issue on Github about this. - -- - -- NOTE(matija): we need to copy migrations down before running "migrate dev" to make sure - -- all the latest migrations are in the generated project (e.g. Wasp dev checked out something - -- new) - otherwise "dev" would create a new migration for that and we would end up with two - -- migrations doing the same thing (which might result in conflict, e.g. during db creation). - waspSaysC $ asWaspStartMessage "Copying migrations folder from Wasp to Prisma project..." - copyDbMigrationDir waspProjectDir genProjectRootDir CopyMigDirDown + let waspDbMigrationsDir = + waspProjectDir + Wasp.Common.dbMigrationsDirInWaspProjectDir waspSaysC $ asWaspStartMessage "Performing migration..." - migrateResult <- liftIO $ DbOps.migrateDev genProjectRootDir + migrateResult <- liftIO $ DbOps.migrateDevAndCopyToSource waspDbMigrationsDir genProjectRootDir case migrateResult of Left migrateError -> throwError $ CommandError $ asWaspFailureMessage "Migrate dev failed:" ++ migrateError Right () -> waspSaysC $ asWaspSuccessMessage "Migration done." - - waspSaysC $ asWaspStartMessage "Copying migrations folder from Prisma to Wasp project..." - copyDbMigrationDir waspProjectDir genProjectRootDir CopyMigDirUp - - waspSaysC $ asWaspSuccessMessage "All done!" - where - copyDbMigrationDir waspProjectDir genProjectRootDir copyDirection = do - copyDbMigDirResult <- - liftIO $ copyDbMigrationsDir copyDirection waspProjectDir genProjectRootDir - case copyDbMigDirResult of - Nothing -> waspSaysC $ asWaspSuccessMessage "Done copying migrations folder." - Just err -> throwError $ CommandError $ asWaspFailureMessage "Copying migration folder failed:" ++ err - -data MigrationDirCopyDirection = CopyMigDirUp | CopyMigDirDown deriving (Eq) - --- | Copy migrations directory between Wasp source and the generated project. -copyDbMigrationsDir :: - -- | Copy direction (source -> gen or gen-> source) - MigrationDirCopyDirection -> - Path' Abs (Dir WaspProjectDir) -> - Path' Abs (Dir ProjectRootDir) -> - IO (Maybe String) -copyDbMigrationsDir copyDirection waspProjectDir genProjectRootDir = do - -- Migration folder in Wasp source (seen by Wasp dev and versioned). - let dbMigrationsDirInWaspProjectDirAbsPath = waspProjectDir dbMigrationsDirInWaspProjectDir - - -- Migration folder in the generated code. - let dbMigrationsDirInGenProjectDirAbsPath = - genProjectRootDir dbRootDirInProjectRootDir - dbMigrationsDirInDbRootDir - - let srcPathAbsDir = - if copyDirection == CopyMigDirUp - then SP.Path.toPathAbsDir dbMigrationsDirInGenProjectDirAbsPath - else SP.Path.toPathAbsDir dbMigrationsDirInWaspProjectDirAbsPath - - let targetPathAbsDir = - if copyDirection == CopyMigDirUp - then SP.Path.toPathAbsDir dbMigrationsDirInWaspProjectDirAbsPath - else SP.Path.toPathAbsDir dbMigrationsDirInGenProjectDirAbsPath - - doesSrcDirExist <- PathIO.doesDirExist srcPathAbsDir - if doesSrcDirExist - then - PathIO.copyDirRecur srcPathAbsDir targetPathAbsDir >> return Nothing - `catch` (\e -> return $ Just $ show (e :: P.PathException)) - `catch` (\e -> return $ Just $ show (e :: IOError)) - else return Nothing diff --git a/waspc/package.yaml b/waspc/package.yaml index ee0329e04..940262e37 100644 --- a/waspc/package.yaml +++ b/waspc/package.yaml @@ -78,6 +78,7 @@ library: - mtl - strong-path - template-haskell + - path-io executables: wasp-cli: diff --git a/waspc/src/Wasp/Generator/DbGenerator.hs b/waspc/src/Wasp/Generator/DbGenerator.hs index c8ba72106..4bd54e59f 100644 --- a/waspc/src/Wasp/Generator/DbGenerator.hs +++ b/waspc/src/Wasp/Generator/DbGenerator.hs @@ -9,13 +9,14 @@ where import Control.Monad (when) import Data.Aeson (object, (.=)) -import Data.Maybe (isNothing) +import Data.Maybe (isNothing, maybeToList) import StrongPath (Abs, Dir, File', Path', Rel, reldir, relfile, ()) import qualified StrongPath as SP import System.Directory (doesDirectoryExist, removeDirectoryRecursive) +import Wasp.Common (DbMigrationsDir) import Wasp.CompileOptions (CompileOptions) import Wasp.Generator.Common (ProjectRootDir) -import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft) +import Wasp.Generator.FileDraft (FileDraft, createCopyDirFileDraft, createTemplateFileDraft) import Wasp.Generator.Templates (TemplatesDir) import qualified Wasp.Psl.Ast.Model as Psl.Ast.Model import qualified Wasp.Psl.Generator.Model as Psl.Generator.Model @@ -25,14 +26,10 @@ import qualified Wasp.Wasp.Db as Wasp.Db import Wasp.Wasp.Entity (Entity) import qualified Wasp.Wasp.Entity as Wasp.Entity --- * Path definitions - data DbRootDir data DbTemplatesDir -data DbMigrationsDir - dbRootDirInProjectRootDir :: Path' (Rel ProjectRootDir) (Dir DbRootDir) dbRootDirInProjectRootDir = [reldir|db|] @@ -53,13 +50,6 @@ dbSchemaFileInProjectRootDir = dbRootDirInProjectRootDir dbSchemaFileInDbRoo dbMigrationsDirInDbRootDir :: Path' (Rel DbRootDir) (Dir DbMigrationsDir) dbMigrationsDirInDbRootDir = [reldir|migrations|] --- * Db generator - -genDb :: Wasp -> CompileOptions -> [FileDraft] -genDb wasp _ = - [ genPrismaSchema wasp - ] - preCleanup :: Wasp -> Path' Abs (Dir ProjectRootDir) -> CompileOptions -> IO () preCleanup wasp projectRootDir _ = do deleteGeneratedMigrationsDirIfRedundant wasp projectRootDir @@ -76,6 +66,10 @@ deleteGeneratedMigrationsDirIfRedundant wasp projectRootDir = do where projectMigrationsDirAbsFilePath = SP.fromAbsDir $ projectRootDir dbRootDirInProjectRootDir dbMigrationsDirInDbRootDir +genDb :: Wasp -> CompileOptions -> [FileDraft] +genDb wasp _ = + genPrismaSchema wasp : maybeToList (genMigrationsDir wasp) + genPrismaSchema :: Wasp -> FileDraft genPrismaSchema wasp = createTemplateFileDraft dstPath tmplSrcPath (Just templateData) where @@ -102,3 +96,10 @@ genPrismaSchema wasp = createTemplateFileDraft dstPath tmplSrcPath (Just templat entityToPslModelSchema entity = Psl.Generator.Model.generateModel $ Psl.Ast.Model.Model (Wasp.Entity._name entity) (Wasp.Entity._pslModelBody entity) + +genMigrationsDir :: Wasp -> Maybe FileDraft +genMigrationsDir wasp = + (getMigrationsDir wasp) >>= \waspMigrationsDir -> + Just $ createCopyDirFileDraft (SP.castDir genProjectMigrationsDir) (SP.castDir waspMigrationsDir) + where + genProjectMigrationsDir = dbRootDirInProjectRootDir dbMigrationsDirInDbRootDir diff --git a/waspc/src/Wasp/Generator/DbGenerator/Operations.hs b/waspc/src/Wasp/Generator/DbGenerator/Operations.hs index f2de271ac..04bf49e82 100644 --- a/waspc/src/Wasp/Generator/DbGenerator/Operations.hs +++ b/waspc/src/Wasp/Generator/DbGenerator/Operations.hs @@ -1,14 +1,22 @@ module Wasp.Generator.DbGenerator.Operations - ( migrateDev, + ( migrateDevAndCopyToSource, ) where import Control.Concurrent (Chan, newChan, readChan) import Control.Concurrent.Async (concurrently) +import Control.Monad.Catch (catch) +import qualified Path as P import StrongPath (Abs, Dir, Path') +import qualified StrongPath as SP import System.Exit (ExitCode (..)) +import Wasp.Common (DbMigrationsDir) import Wasp.Generator.Common (ProjectRootDir) +import Wasp.Generator.DbGenerator (dbMigrationsDirInDbRootDir, dbRootDirInProjectRootDir) import qualified Wasp.Generator.DbGenerator.Jobs as DbJobs +import Wasp.Generator.FileDraft.WriteableMonad + ( WriteableMonad (copyDirectoryRecursive), + ) import Wasp.Generator.Job (JobMessage) import qualified Wasp.Generator.Job as J import Wasp.Generator.Job.IO (printJobMessage) @@ -20,13 +28,26 @@ printJobMsgsUntilExitReceived chan = do J.JobOutput {} -> printJobMessage jobMsg >> printJobMsgsUntilExitReceived chan J.JobExit {} -> return () -migrateDev :: Path' Abs (Dir ProjectRootDir) -> IO (Either String ()) -migrateDev projectDir = do +-- | Migrates in the generated project context and then copies the migrations dir back +-- up to the wasp project dir to ensure they remain in sync. +migrateDevAndCopyToSource :: Path' Abs (Dir DbMigrationsDir) -> Path' Abs (Dir ProjectRootDir) -> IO (Either String ()) +migrateDevAndCopyToSource dbMigrationsDirInWaspProjectDirAbs genProjectRootDirAbs = do chan <- newChan (_, dbExitCode) <- concurrently (printJobMsgsUntilExitReceived chan) - (DbJobs.migrateDev projectDir chan) + (DbJobs.migrateDev genProjectRootDirAbs chan) case dbExitCode of - ExitSuccess -> return (Right ()) + ExitSuccess -> copyMigrationsBackToSource genProjectRootDirAbs dbMigrationsDirInWaspProjectDirAbs ExitFailure code -> return $ Left $ "Migrate (dev) failed with exit code: " ++ show code + +-- | Copies the DB migrations from the generated project dir back up to theh wasp project dir +copyMigrationsBackToSource :: Path' Abs (Dir ProjectRootDir) -> Path' Abs (Dir DbMigrationsDir) -> IO (Either String ()) +copyMigrationsBackToSource genProjectRootDirAbs dbMigrationsDirInWaspProjectDirAbs = + do + copyDirectoryRecursive genProjectMigrationsDir waspMigrationsDir >> return (Right ()) + `catch` (\e -> return $ Left $ show (e :: P.PathException)) + `catch` (\e -> return $ Left $ show (e :: IOError)) + where + waspMigrationsDir = SP.castDir dbMigrationsDirInWaspProjectDirAbs + genProjectMigrationsDir = SP.castDir $ genProjectRootDirAbs SP. dbRootDirInProjectRootDir SP. dbMigrationsDirInDbRootDir diff --git a/waspc/src/Wasp/Generator/FileDraft.hs b/waspc/src/Wasp/Generator/FileDraft.hs index 25923f32f..a1c56a9ef 100644 --- a/waspc/src/Wasp/Generator/FileDraft.hs +++ b/waspc/src/Wasp/Generator/FileDraft.hs @@ -5,13 +5,15 @@ module Wasp.Generator.FileDraft createCopyFileDraft, createCopyFileDraftIfExists, createTextFileDraft, + createCopyDirFileDraft, ) where import qualified Data.Aeson as Aeson import Data.Text (Text) -import StrongPath (Abs, File', Path', Rel) +import StrongPath (Abs, Dir', File', Path', Rel) import Wasp.Generator.Common (ProjectRootDir) +import qualified Wasp.Generator.FileDraft.CopyDirFileDraft as CopyDirFD import qualified Wasp.Generator.FileDraft.CopyFileDraft as CopyFD import qualified Wasp.Generator.FileDraft.TemplateFileDraft as TmplFD import qualified Wasp.Generator.FileDraft.TextFileDraft as TextFD @@ -21,15 +23,21 @@ import Wasp.Generator.Templates (TemplatesDir) -- | FileDraft unites different file draft types into a single type, -- so that in the rest of the system they can be passed around as heterogeneous -- collection when needed. +-- +-- TODO: revisit the quick and dirty Linux interpretation of "everything is a file" +-- and treating a directory (`CopyDirFileDraft`) as a `FileDraft`. As is, this may be +-- a source of potential confusion and possibly tech debt to resolve later. data FileDraft = FileDraftTemplateFd TmplFD.TemplateFileDraft | FileDraftCopyFd CopyFD.CopyFileDraft + | FileDraftCopyDirFd CopyDirFD.CopyDirFileDraft | FileDraftTextFd TextFD.TextFileDraft deriving (Show, Eq) instance Writeable FileDraft where write dstDir (FileDraftTemplateFd draft) = write dstDir draft write dstDir (FileDraftCopyFd draft) = write dstDir draft + write dstDir (FileDraftCopyDirFd draft) = write dstDir draft write dstDir (FileDraftTextFd draft) = write dstDir draft createTemplateFileDraft :: @@ -63,6 +71,14 @@ createCopyFileDraftIfExists dstPath srcPath = CopyFD._failIfSrcDoesNotExist = False } +createCopyDirFileDraft :: Path' (Rel ProjectRootDir) Dir' -> Path' Abs Dir' -> FileDraft +createCopyDirFileDraft dstPath srcPath = + FileDraftCopyDirFd $ + CopyDirFD.CopyDirFileDraft + { CopyDirFD._dstPath = dstPath, + CopyDirFD._srcPath = srcPath + } + createTextFileDraft :: Path' (Rel ProjectRootDir) File' -> Text -> FileDraft createTextFileDraft dstPath content = FileDraftTextFd $ TextFD.TextFileDraft {TextFD._dstPath = dstPath, TextFD._content = content} diff --git a/waspc/src/Wasp/Generator/FileDraft/CopyDirFileDraft.hs b/waspc/src/Wasp/Generator/FileDraft/CopyDirFileDraft.hs new file mode 100644 index 000000000..220864a6b --- /dev/null +++ b/waspc/src/Wasp/Generator/FileDraft/CopyDirFileDraft.hs @@ -0,0 +1,40 @@ +module Wasp.Generator.FileDraft.CopyDirFileDraft + ( CopyDirFileDraft (..), + ) +where + +import Control.Monad (when) +import StrongPath + ( Abs, + Dir', + Path', + Rel, + (), + ) +import qualified StrongPath as SP +import Wasp.Generator.Common (ProjectRootDir) +import Wasp.Generator.FileDraft.Writeable +import Wasp.Generator.FileDraft.WriteableMonad (WriteableMonad (copyDirectoryRecursive, createDirectoryIfMissing), doesDirectoryExist) + +-- | File draft based on another dir that is to be recursively copied. +-- +-- TODO: revisit the quick and dirty Linux interpretation of "everything is a file" +-- and treating a directory (`CopyDirFileDraft`) as a `FileDraft`. As is, this may be +-- a source of potential confusion and possibly tech debt to resolve later. +data CopyDirFileDraft = CopyDirFileDraft + { -- | Path where the dir will be copied to. + _dstPath :: !(Path' (Rel ProjectRootDir) Dir'), + -- | Absolute path of source dir to copy. + _srcPath :: !(Path' Abs Dir') + } + deriving (Show, Eq) + +instance Writeable CopyDirFileDraft where + write projectRootAbsPath draft = do + srcDirExists <- doesDirectoryExist $ SP.fromAbsDir srcPathAbsDir + when srcDirExists $ do + createDirectoryIfMissing True (SP.fromAbsDir dstPathAbsDir) + copyDirectoryRecursive srcPathAbsDir dstPathAbsDir + where + srcPathAbsDir = _srcPath draft + dstPathAbsDir = projectRootAbsPath _dstPath draft diff --git a/waspc/src/Wasp/Generator/FileDraft/WriteableMonad.hs b/waspc/src/Wasp/Generator/FileDraft/WriteableMonad.hs index 1f8f6a036..fc21b049b 100644 --- a/waspc/src/Wasp/Generator/FileDraft/WriteableMonad.hs +++ b/waspc/src/Wasp/Generator/FileDraft/WriteableMonad.hs @@ -7,7 +7,9 @@ import Control.Monad.IO.Class (MonadIO) import Data.Aeson as Aeson import Data.Text (Text) import qualified Data.Text.IO -import StrongPath (Abs, Dir, File', Path', Rel) +import qualified Path.IO as PathIO +import StrongPath (Abs, Dir, Dir', File', Path', Rel) +import qualified StrongPath.Path as SP.Path import qualified System.Directory import System.IO.Error (isDoesNotExistError) import UnliftIO.Exception (Exception, catch) @@ -33,8 +35,19 @@ class (MonadIO m) => WriteableMonad m where FilePath -> m () + -- | Copies a directory recursively. + -- It does not follow symbolic links and preserves permissions when possible. + copyDirectoryRecursive :: + -- | Src path. + Path' Abs Dir' -> + -- | Dst path. + Path' Abs Dir' -> + m () + doesFileExist :: FilePath -> m Bool + doesDirectoryExist :: FilePath -> m Bool + writeFileFromText :: FilePath -> Text -> m () getTemplateFileAbsPath :: @@ -69,7 +82,11 @@ instance WriteableMonad IO where else throwIO e ) + copyDirectoryRecursive src dst = do + PathIO.copyDirRecur (SP.Path.toPathAbsDir src) (SP.Path.toPathAbsDir dst) + doesFileExist = System.Directory.doesFileExist + doesDirectoryExist = System.Directory.doesDirectoryExist writeFileFromText = Data.Text.IO.writeFile getTemplateFileAbsPath = Templates.getTemplateFileAbsPath getTemplatesDirAbsPath = Templates.getTemplatesDirAbsPath diff --git a/waspc/test/Generator/MockWriteableMonad.hs b/waspc/test/Generator/MockWriteableMonad.hs index 704d83a31..d247e83ec 100644 --- a/waspc/test/Generator/MockWriteableMonad.hs +++ b/waspc/test/Generator/MockWriteableMonad.hs @@ -15,7 +15,7 @@ import qualified Data.Aeson as Aeson import Data.Bifunctor (first) import Data.Text (Text, pack) import Fixtures (systemSPRoot) -import StrongPath (Abs, Dir, File', Path', Rel, reldir, ()) +import StrongPath (Abs, Dir, Dir', File', Path', Rel, reldir, ()) import Wasp.Generator.FileDraft.WriteableMonad import Wasp.Generator.Templates (TemplatesDir) @@ -29,13 +29,14 @@ defaultMockConfig = { getTemplatesDirAbsPath_impl = systemSPRoot [reldir|mock/templates/dir|], getTemplateFileAbsPath_impl = \path -> systemSPRoot [reldir|mock/templates/dir|] path, compileAndRenderTemplate_impl = \_ _ -> pack "Mock template content", - doesFileExist_impl = const True + doesFileExist_impl = const True, + doesDirectoryExist_impl = const True } getMockLogs :: MockWriteableMonad a -> MockWriteableMonadConfig -> MockWriteableMonadLogs getMockLogs mock config = fst $ execState (unMockWriteableMonad mock) (emptyLogs, config) where - emptyLogs = MockWriteableMonadLogs [] [] [] [] [] [] + emptyLogs = MockWriteableMonadLogs [] [] [] [] [] [] [] instance WriteableMonad MockWriteableMonad where writeFileFromText dstPath text = MockWriteableMonad $ do @@ -66,6 +67,13 @@ instance WriteableMonad MockWriteableMonad where (_, config) <- get return $ doesFileExist_impl config path + doesDirectoryExist path = MockWriteableMonad $ do + (_, config) <- get + return $ doesDirectoryExist_impl config path + + copyDirectoryRecursive srcPath dstPath = MockWriteableMonad $ do + modifyLogs (copyDirectoryRecursive_addCall srcPath dstPath) + throwIO = throwIO instance MonadIO MockWriteableMonad where @@ -85,14 +93,16 @@ data MockWriteableMonadLogs = MockWriteableMonadLogs createDirectoryIfMissing_calls :: [(Bool, FilePath)], copyFile_calls :: [(FilePath, FilePath)], getTemplateFileAbsPath_calls :: [Path' (Rel TemplatesDir) File'], - compileAndRenderTemplate_calls :: [(Path' (Rel TemplatesDir) File', Aeson.Value)] + compileAndRenderTemplate_calls :: [(Path' (Rel TemplatesDir) File', Aeson.Value)], + copyDirectoryRecursive_calls :: [(Path' Abs Dir', Path' Abs Dir')] } data MockWriteableMonadConfig = MockWriteableMonadConfig { getTemplatesDirAbsPath_impl :: Path' Abs (Dir TemplatesDir), getTemplateFileAbsPath_impl :: Path' (Rel TemplatesDir) File' -> Path' Abs File', compileAndRenderTemplate_impl :: Path' (Rel TemplatesDir) File' -> Aeson.Value -> Text, - doesFileExist_impl :: FilePath -> Bool + doesFileExist_impl :: FilePath -> Bool, + doesDirectoryExist_impl :: FilePath -> Bool } writeFileFromText_addCall :: FilePath -> Text -> MockWriteableMonadLogs -> MockWriteableMonadLogs @@ -118,6 +128,10 @@ createDirectoryIfMissing_addCall createParents path logs = (createParents, path) : createDirectoryIfMissing_calls logs } +copyDirectoryRecursive_addCall :: Path' Abs Dir' -> Path' Abs Dir' -> MockWriteableMonadLogs -> MockWriteableMonadLogs +copyDirectoryRecursive_addCall srcPath dstPath logs = + logs {copyDirectoryRecursive_calls = (srcPath, dstPath) : copyDirectoryRecursive_calls logs} + compileAndRenderTemplate_addCall :: Path' (Rel TemplatesDir) File' -> Aeson.Value -> diff --git a/waspc/test/Generator/WebAppGeneratorTest.hs b/waspc/test/Generator/WebAppGeneratorTest.hs index 2164b7962..9e5c2e25b 100644 --- a/waspc/test/Generator/WebAppGeneratorTest.hs +++ b/waspc/test/Generator/WebAppGeneratorTest.hs @@ -6,6 +6,7 @@ import System.FilePath (()) import Test.Tasty.Hspec import qualified Wasp.CompileOptions as CompileOptions import Wasp.Generator.FileDraft +import qualified Wasp.Generator.FileDraft.CopyDirFileDraft as CopyDirFD import qualified Wasp.Generator.FileDraft.CopyFileDraft as CopyFD import qualified Wasp.Generator.FileDraft.TemplateFileDraft as TmplFD import qualified Wasp.Generator.FileDraft.TextFileDraft as TextFD @@ -73,4 +74,5 @@ existsFdWithDst fds dstPath = any ((== dstPath) . getFileDraftDstPath) fds getFileDraftDstPath :: FileDraft -> FilePath getFileDraftDstPath (FileDraftTemplateFd fd) = SP.toFilePath $ TmplFD._dstPath fd getFileDraftDstPath (FileDraftCopyFd fd) = SP.toFilePath $ CopyFD._dstPath fd +getFileDraftDstPath (FileDraftCopyDirFd fd) = SP.toFilePath $ CopyDirFD._dstPath fd getFileDraftDstPath (FileDraftTextFd fd) = SP.toFilePath $ TextFD._dstPath fd