mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-09-11 15:26:19 +03:00
Adds a simple pass-through Jobs implementation. (#553)
This commit is contained in:
parent
801ba69e54
commit
adcb15a41c
@ -0,0 +1,26 @@
|
||||
import { sleep } from '../utils.js'
|
||||
|
||||
/**
|
||||
* "Immutable-ish" passthrough job wrapper, mainly to be used for testing.
|
||||
*/
|
||||
class PassthroughJob {
|
||||
constructor(values) {
|
||||
this.perform = () => { }
|
||||
this.delayMs = 0
|
||||
Object.assign(this, values)
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new PassthroughJob({ ...this, delayMs: ms })
|
||||
}
|
||||
|
||||
performAsync(args) {
|
||||
return {
|
||||
result: sleep(this.delayMs).then(() => this.perform(args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function jobFactory(fn) {
|
||||
return new PassthroughJob({ perform: fn })
|
||||
}
|
5
waspc/data/Generator/templates/server/src/jobs/_job.js
Normal file
5
waspc/data/Generator/templates/server/src/jobs/_job.js
Normal file
@ -0,0 +1,5 @@
|
||||
{{={= =}=}}
|
||||
import { jobFactory } from './{= jobFactoryName =}.js'
|
||||
{=& jobPerformFnImportStatement =}
|
||||
|
||||
export const {= jobName =} = jobFactory({= jobPerformFnName =})
|
@ -43,3 +43,5 @@ export const prismaErrorToHttpError = (e) => {
|
||||
return new HttpError(500)
|
||||
}
|
||||
}
|
||||
|
||||
export const sleep = ms => new Promise(r => setTimeout(r, ms))
|
||||
|
@ -3,6 +3,7 @@ import System.Info (os)
|
||||
import Test.Tasty (TestTree, defaultMain, testGroup)
|
||||
import Tests.WaspBuildTest (waspBuild)
|
||||
import Tests.WaspCompileTest (waspCompile)
|
||||
import Tests.WaspJobTest (waspJob)
|
||||
import Tests.WaspMigrateTest (waspMigrate)
|
||||
import Tests.WaspNewTest (waspNew)
|
||||
|
||||
@ -23,5 +24,6 @@ tests = do
|
||||
[ waspNew,
|
||||
waspCompile,
|
||||
waspMigrate,
|
||||
waspBuild
|
||||
waspBuild,
|
||||
waspJob
|
||||
]
|
||||
|
@ -8,6 +8,7 @@ module ShellCommands
|
||||
combineShellCommands,
|
||||
cdIntoCurrentProject,
|
||||
appendToWaspFile,
|
||||
createFile,
|
||||
setDbToPSQL,
|
||||
waspCliNew,
|
||||
waspCliCompile,
|
||||
@ -57,6 +58,15 @@ appendToWaspFile content =
|
||||
-- NOTE: Using `show` to preserve newlines in string.
|
||||
return $ "printf " ++ show (content ++ "\n") ++ " >> main.wasp"
|
||||
|
||||
-- NOTE: Pretty fragile. Can't handle spaces in args, *nix only, etc.
|
||||
createFile :: String -> FilePath -> String -> ShellCommandBuilder ShellCommand
|
||||
createFile content relDirFp filename = return $ combineShellCommands [createParentDir, writeContentsToFile]
|
||||
where
|
||||
createParentDir = "mkdir -p ./" ++ relDirFp
|
||||
destinationFile = "./" ++ relDirFp ++ "/" ++ filename
|
||||
contents = show (content ++ "\n")
|
||||
writeContentsToFile = unwords ["printf", contents, ">", destinationFile]
|
||||
|
||||
-- NOTE: This is fragile and will likely break in future. Assumes `app` decl is first line and by default
|
||||
-- we do not have a `db` field. Consider better alternatives.
|
||||
setDbToPSQL :: ShellCommandBuilder ShellCommand
|
||||
|
31
waspc/e2e-test/Tests/WaspJobTest.hs
Normal file
31
waspc/e2e-test/Tests/WaspJobTest.hs
Normal file
@ -0,0 +1,31 @@
|
||||
module Tests.WaspJobTest (waspJob) where
|
||||
|
||||
import GoldenTest (GoldenTest, makeGoldenTest)
|
||||
import ShellCommands
|
||||
( appendToWaspFile,
|
||||
cdIntoCurrentProject,
|
||||
createFile,
|
||||
waspCliCompile,
|
||||
waspCliNew,
|
||||
)
|
||||
|
||||
waspJob :: GoldenTest
|
||||
waspJob = do
|
||||
let entityDecl =
|
||||
" job MySpecialJob { \n\
|
||||
\ perform: import { foo } from \"@ext/jobs/bar.js\" \n\
|
||||
\ } \n"
|
||||
|
||||
let jobFile =
|
||||
" export const foo = async (args) => { \n\
|
||||
\ return 1 \n\
|
||||
\ } \n"
|
||||
|
||||
makeGoldenTest "waspJob" $
|
||||
sequence
|
||||
[ waspCliNew,
|
||||
cdIntoCurrentProject,
|
||||
appendToWaspFile entityDecl,
|
||||
createFile jobFile "./ext/jobs" "bar.js",
|
||||
waspCliCompile
|
||||
]
|
@ -15,6 +15,7 @@ waspBuild/.wasp/build/server/src/dbClient.js
|
||||
waspBuild/.wasp/build/server/src/ext-src/Main.css
|
||||
waspBuild/.wasp/build/server/src/ext-src/MainPage.js
|
||||
waspBuild/.wasp/build/server/src/ext-src/waspLogo.png
|
||||
waspBuild/.wasp/build/server/src/jobs/PassthroughJobFactory.js
|
||||
waspBuild/.wasp/build/server/src/routes/index.js
|
||||
waspBuild/.wasp/build/server/src/routes/operations/index.js
|
||||
waspBuild/.wasp/build/server/src/server.js
|
||||
|
@ -111,6 +111,13 @@
|
||||
],
|
||||
"0f05a89eb945d6d7326110e88776e402833b356202b06d0a8bf652e118d3fd2f"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/jobs/PassthroughJobFactory.js"
|
||||
],
|
||||
"404443274a33f1104e41d47174edfe5290bf5219fdf7e730bbdce1ad24a93fda"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
@ -137,7 +144,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"b75212f40dffee19d17e7dfeb9d3daf936c680abc2467d990398e6c4d8d01d0a"
|
||||
"68a5794f55e24b303d81456a1181a3a2cd70773f6ebc4e7a63dac064834aa8e9"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { sleep } from '../utils.js'
|
||||
|
||||
/**
|
||||
* "Immutable-ish" passthrough job wrapper, mainly to be used for testing.
|
||||
*/
|
||||
class PassthroughJob {
|
||||
constructor(values) {
|
||||
this.perform = () => { }
|
||||
this.delayMs = 0
|
||||
Object.assign(this, values)
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new PassthroughJob({ ...this, delayMs: ms })
|
||||
}
|
||||
|
||||
performAsync(args) {
|
||||
return {
|
||||
result: sleep(this.delayMs).then(() => this.perform(args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function jobFactory(fn) {
|
||||
return new PassthroughJob({ perform: fn })
|
||||
}
|
@ -43,3 +43,5 @@ export const prismaErrorToHttpError = (e) => {
|
||||
return new HttpError(500)
|
||||
}
|
||||
}
|
||||
|
||||
export const sleep = ms => new Promise(r => setTimeout(r, ms))
|
||||
|
@ -15,6 +15,7 @@ waspCompile/.wasp/out/server/src/dbClient.js
|
||||
waspCompile/.wasp/out/server/src/ext-src/Main.css
|
||||
waspCompile/.wasp/out/server/src/ext-src/MainPage.js
|
||||
waspCompile/.wasp/out/server/src/ext-src/waspLogo.png
|
||||
waspCompile/.wasp/out/server/src/jobs/PassthroughJobFactory.js
|
||||
waspCompile/.wasp/out/server/src/routes/index.js
|
||||
waspCompile/.wasp/out/server/src/routes/operations/index.js
|
||||
waspCompile/.wasp/out/server/src/server.js
|
||||
|
@ -111,6 +111,13 @@
|
||||
],
|
||||
"0f05a89eb945d6d7326110e88776e402833b356202b06d0a8bf652e118d3fd2f"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/jobs/PassthroughJobFactory.js"
|
||||
],
|
||||
"404443274a33f1104e41d47174edfe5290bf5219fdf7e730bbdce1ad24a93fda"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
@ -137,7 +144,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"b75212f40dffee19d17e7dfeb9d3daf936c680abc2467d990398e6c4d8d01d0a"
|
||||
"68a5794f55e24b303d81456a1181a3a2cd70773f6ebc4e7a63dac064834aa8e9"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { sleep } from '../utils.js'
|
||||
|
||||
/**
|
||||
* "Immutable-ish" passthrough job wrapper, mainly to be used for testing.
|
||||
*/
|
||||
class PassthroughJob {
|
||||
constructor(values) {
|
||||
this.perform = () => { }
|
||||
this.delayMs = 0
|
||||
Object.assign(this, values)
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new PassthroughJob({ ...this, delayMs: ms })
|
||||
}
|
||||
|
||||
performAsync(args) {
|
||||
return {
|
||||
result: sleep(this.delayMs).then(() => this.perform(args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function jobFactory(fn) {
|
||||
return new PassthroughJob({ perform: fn })
|
||||
}
|
@ -43,3 +43,5 @@ export const prismaErrorToHttpError = (e) => {
|
||||
return new HttpError(500)
|
||||
}
|
||||
}
|
||||
|
||||
export const sleep = ms => new Promise(r => setTimeout(r, ms))
|
||||
|
@ -20,6 +20,7 @@ waspMigrate/.wasp/out/server/src/dbClient.js
|
||||
waspMigrate/.wasp/out/server/src/ext-src/Main.css
|
||||
waspMigrate/.wasp/out/server/src/ext-src/MainPage.js
|
||||
waspMigrate/.wasp/out/server/src/ext-src/waspLogo.png
|
||||
waspMigrate/.wasp/out/server/src/jobs/PassthroughJobFactory.js
|
||||
waspMigrate/.wasp/out/server/src/routes/index.js
|
||||
waspMigrate/.wasp/out/server/src/routes/operations/index.js
|
||||
waspMigrate/.wasp/out/server/src/server.js
|
||||
|
@ -111,6 +111,13 @@
|
||||
],
|
||||
"0f05a89eb945d6d7326110e88776e402833b356202b06d0a8bf652e118d3fd2f"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/jobs/PassthroughJobFactory.js"
|
||||
],
|
||||
"404443274a33f1104e41d47174edfe5290bf5219fdf7e730bbdce1ad24a93fda"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
@ -137,7 +144,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"b75212f40dffee19d17e7dfeb9d3daf936c680abc2467d990398e6c4d8d01d0a"
|
||||
"68a5794f55e24b303d81456a1181a3a2cd70773f6ebc4e7a63dac064834aa8e9"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { sleep } from '../utils.js'
|
||||
|
||||
/**
|
||||
* "Immutable-ish" passthrough job wrapper, mainly to be used for testing.
|
||||
*/
|
||||
class PassthroughJob {
|
||||
constructor(values) {
|
||||
this.perform = () => { }
|
||||
this.delayMs = 0
|
||||
Object.assign(this, values)
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new PassthroughJob({ ...this, delayMs: ms })
|
||||
}
|
||||
|
||||
performAsync(args) {
|
||||
return {
|
||||
result: sleep(this.delayMs).then(() => this.perform(args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function jobFactory(fn) {
|
||||
return new PassthroughJob({ perform: fn })
|
||||
}
|
@ -43,3 +43,5 @@ export const prismaErrorToHttpError = (e) => {
|
||||
return new HttpError(500)
|
||||
}
|
||||
}
|
||||
|
||||
export const sleep = ms => new Promise(r => setTimeout(r, ms))
|
||||
|
7
waspc/examples/todoApp/ext/jobs/bar.js
Normal file
7
waspc/examples/todoApp/ext/jobs/bar.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { sleep } from '@wasp/utils.js'
|
||||
|
||||
export const foo = async (args) => {
|
||||
console.log("Inside Job bar's callback foo: ", args)
|
||||
await sleep(4000)
|
||||
return "I am the Job's result!"
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
import { mySpecialJob } from '@wasp/jobs/mySpecialJob.js'
|
||||
|
||||
let someResource = undefined
|
||||
|
||||
export const getSomeResource = () => someResource
|
||||
@ -6,6 +8,12 @@ const setup = async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
someResource = 'This resource is now set up.'
|
||||
console.log('Custom server setup done!')
|
||||
|
||||
console.log('Kicking off Job...')
|
||||
// Or: const runningJob = mySpecialJob.delay(1000).performAsync({ something: "here" })
|
||||
const runningJob = mySpecialJob.performAsync({ something: "here" })
|
||||
console.log('Waiting for Job result...')
|
||||
runningJob.result.then(res => { console.log(res) }).finally(() => { console.log("Job done!") })
|
||||
}
|
||||
|
||||
export default setup
|
||||
|
@ -107,3 +107,7 @@ action toggleAllTasks {
|
||||
fn: import { toggleAllTasks } from "@ext/actions.js",
|
||||
entities: [Task]
|
||||
}
|
||||
|
||||
job mySpecialJob {
|
||||
perform: import { foo } from "@ext/jobs/bar.js"
|
||||
}
|
||||
|
@ -13,7 +13,18 @@ import Data.Maybe
|
||||
fromMaybe,
|
||||
isJust,
|
||||
)
|
||||
import StrongPath (Dir, File', Path, Path', Posix, Rel, reldir, reldirP, relfile, (</>))
|
||||
import StrongPath
|
||||
( Dir,
|
||||
File',
|
||||
Path,
|
||||
Path',
|
||||
Posix,
|
||||
Rel,
|
||||
reldir,
|
||||
reldirP,
|
||||
relfile,
|
||||
(</>),
|
||||
)
|
||||
import Wasp.AppSpec (AppSpec)
|
||||
import qualified Wasp.AppSpec as AS
|
||||
import qualified Wasp.AppSpec.App as AS.App
|
||||
@ -38,6 +49,7 @@ import Wasp.Generator.ServerGenerator.Common
|
||||
import qualified Wasp.Generator.ServerGenerator.Common as C
|
||||
import Wasp.Generator.ServerGenerator.ConfigG (genConfigFile)
|
||||
import qualified Wasp.Generator.ServerGenerator.ExternalCodeGenerator as ServerExternalCodeGenerator
|
||||
import Wasp.Generator.ServerGenerator.JobGenerator (genJobFactories, genJobs)
|
||||
import Wasp.Generator.ServerGenerator.OperationsG (genOperations)
|
||||
import Wasp.Generator.ServerGenerator.OperationsRoutesG (genOperationsRoutes)
|
||||
import qualified Wasp.SemanticVersion as SV
|
||||
@ -55,6 +67,8 @@ genServer spec =
|
||||
<++> genSrcDir spec
|
||||
<++> genExternalCodeDir ServerExternalCodeGenerator.generatorStrategy (AS.externalCodeFiles spec)
|
||||
<++> genDotEnv spec
|
||||
<++> genJobs spec
|
||||
<++> genJobFactories
|
||||
|
||||
genDotEnv :: AppSpec -> Generator [FileDraft]
|
||||
genDotEnv spec = return $
|
||||
|
82
waspc/src/Wasp/Generator/ServerGenerator/JobGenerator.hs
Normal file
82
waspc/src/Wasp/Generator/ServerGenerator/JobGenerator.hs
Normal file
@ -0,0 +1,82 @@
|
||||
module Wasp.Generator.ServerGenerator.JobGenerator
|
||||
( genJobs,
|
||||
genJobFactories,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson (object, (.=))
|
||||
import Data.Maybe
|
||||
( fromJust,
|
||||
)
|
||||
import qualified GHC.Enum as Enum
|
||||
import StrongPath
|
||||
( Dir,
|
||||
File',
|
||||
Path,
|
||||
Path',
|
||||
Posix,
|
||||
Rel,
|
||||
parseRelFile,
|
||||
reldir,
|
||||
reldirP,
|
||||
relfile,
|
||||
(</>),
|
||||
)
|
||||
import Wasp.AppSpec (AppSpec, getJobs)
|
||||
import Wasp.AppSpec.Job (Job (perform))
|
||||
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
||||
import Wasp.Generator.FileDraft (FileDraft)
|
||||
import Wasp.Generator.JsImport (getJsImportDetailsForExtFnImport)
|
||||
import Wasp.Generator.Monad (Generator)
|
||||
import Wasp.Generator.ServerGenerator.Common
|
||||
( ServerSrcDir,
|
||||
)
|
||||
import qualified Wasp.Generator.ServerGenerator.Common as C
|
||||
|
||||
-- | TODO: Make this not hardcoded!
|
||||
relPosixPathFromJobFileToExtSrcDir :: Path Posix (Rel (Dir ServerSrcDir)) (Dir GeneratedExternalCodeDir)
|
||||
relPosixPathFromJobFileToExtSrcDir = [reldirP|../ext-src|]
|
||||
|
||||
data JobFactory = PassthroughJobFactory
|
||||
deriving (Show, Eq, Ord, Enum, Enum.Bounded)
|
||||
|
||||
jobFactories :: [JobFactory]
|
||||
jobFactories = enumFrom minBound :: [JobFactory]
|
||||
|
||||
-- TODO: In future we will detect what type of JobFactory
|
||||
-- to use based on what the Job is using.
|
||||
jobFactoryForJob :: Job -> JobFactory
|
||||
jobFactoryForJob _ = PassthroughJobFactory
|
||||
|
||||
jobFactoryFilePath :: JobFactory -> Path' (Rel d) File'
|
||||
jobFactoryFilePath PassthroughJobFactory = [relfile|src/jobs/PassthroughJobFactory.js|]
|
||||
|
||||
genJobs :: AppSpec -> Generator [FileDraft]
|
||||
genJobs spec = return $ genJob <$> getJobs spec
|
||||
where
|
||||
tmplFile = C.asTmplFile [relfile|src/jobs/_job.js|]
|
||||
dstFileFromJobName jobName = C.asServerFile $ [reldir|src/jobs/|] </> fromJust (parseRelFile $ jobName ++ ".js")
|
||||
genJob :: (String, Job) -> FileDraft
|
||||
genJob (jobName, job) =
|
||||
let (jobPerformFnName, jobPerformFnImportStatement) = getJsImportDetailsForExtFnImport relPosixPathFromJobFileToExtSrcDir $ perform job
|
||||
in C.mkTmplFdWithDstAndData
|
||||
tmplFile
|
||||
(dstFileFromJobName jobName)
|
||||
( Just $
|
||||
object
|
||||
[ "jobName" .= jobName,
|
||||
"jobPerformFnName" .= jobPerformFnName,
|
||||
"jobPerformFnImportStatement" .= jobPerformFnImportStatement,
|
||||
"jobFactoryName" .= show (jobFactoryForJob job)
|
||||
]
|
||||
)
|
||||
|
||||
genJobFactories :: Generator [FileDraft]
|
||||
genJobFactories = return $ genJobFactory <$> jobFactories
|
||||
where
|
||||
genJobFactory :: JobFactory -> FileDraft
|
||||
genJobFactory jobFactory =
|
||||
let jobFactoryFp = jobFactoryFilePath jobFactory
|
||||
sourceTemplateFp = C.asTmplFile jobFactoryFp
|
||||
destinationServerFp = C.asServerFile jobFactoryFp
|
||||
in C.mkTmplFdWithDstAndData sourceTemplateFp destinationServerFp Nothing
|
@ -208,6 +208,7 @@ library
|
||||
Wasp.Generator.ServerGenerator.Common
|
||||
Wasp.Generator.ServerGenerator.ConfigG
|
||||
Wasp.Generator.ServerGenerator.ExternalCodeGenerator
|
||||
Wasp.Generator.ServerGenerator.JobGenerator
|
||||
Wasp.Generator.ServerGenerator.OperationsG
|
||||
Wasp.Generator.ServerGenerator.OperationsRoutesG
|
||||
Wasp.Generator.ServerGenerator.Setup
|
||||
@ -384,6 +385,7 @@ test-suite e2e-test
|
||||
ShellCommands
|
||||
Tests.WaspBuildTest
|
||||
Tests.WaspCompileTest
|
||||
Tests.WaspJobTest
|
||||
Tests.WaspMigrateTest
|
||||
Tests.WaspNewTest
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user