diff --git a/waspc/data/Generator/templates/react-app/src/crud/_crud.ts b/waspc/data/Generator/templates/sdk/crud/_crud.ts
similarity index 92%
rename from waspc/data/Generator/templates/react-app/src/crud/_crud.ts
rename to waspc/data/Generator/templates/sdk/crud/_crud.ts
index d2e66b58de..24a14bd26f 100644
--- a/waspc/data/Generator/templates/react-app/src/crud/_crud.ts
+++ b/waspc/data/Generator/templates/sdk/crud/_crud.ts
@@ -1,8 +1,8 @@
{{={= =}=}}
-import { createAction } from "../actions/core";
-import { useAction } from "../actions";
-import { createQuery } from "../queries/core";
-import { useQuery } from "../queries";
+import { createAction } from "wasp/rpc/actions/core";
+import { useAction } from "wasp/rpc";
+import { createQuery } from "wasp/rpc/queries/core";
+import { useQuery } from "wasp/rpc";
import {
{=# operations.Get =}
GetQueryResolved,
@@ -19,7 +19,7 @@ import {
{=# operations.Delete =}
DeleteActionResolved,
{=/ operations.Delete =}
-} from '../../../server/src/crud/{= name =}'
+} from 'wasp/server/crud/{= name =}'
function createCrud() {
{=# operations.Get =}
diff --git a/waspc/data/Generator/templates/sdk/package.json b/waspc/data/Generator/templates/sdk/package.json
index 90cadbfade..531b202f21 100644
--- a/waspc/data/Generator/templates/sdk/package.json
+++ b/waspc/data/Generator/templates/sdk/package.json
@@ -27,9 +27,13 @@
"./rpc": "./dist/rpc/index.js",
{=! Used by users, documented. =}
"./rpc/queries": "./dist/rpc/queries/index.js",
+ {=! Used by our code, uncodumented (but accessible) for users. =}
+ "./rpc/queries/core": "./dist/rpc/queries/core.js",
{=! Used by users, documented. =}
"./rpc/actions": "./dist/rpc/actions/index.js",
{=! Used by our code, uncodumented (but accessible) for users. =}
+ "./rpc/actions/core": "./dist/rpc/actions/core.js",
+ {=! Used by our code, uncodumented (but accessible) for users. =}
"./rpc/queryClient": "./dist/rpc/queryClient.js",
{=! Used by users, documented. =}
"./types": "./dist/types/index.js",
@@ -117,6 +121,9 @@
{=! Used by users, documented. =}
"./dbSeed/types": "./dist/dbSeed/types.js",
{=! Used by users, documented. =}
+ "./crud/*": "./dist/crud/*.js",
+ {=! Used by our code, uncodumented (but accessible) for users. =}
+ "./server/crud/*": "./dist/server/crud/*",
"./email": "./dist/email/index.js",
{=! Used by our code, uncodumented (but accessible) for users. =}
"./email/core/types": "./dist/email/core/types.js",
diff --git a/waspc/data/Generator/templates/sdk/server/crud/_operationTypes.ts b/waspc/data/Generator/templates/sdk/server/crud/_operationTypes.ts
new file mode 100644
index 0000000000..6fd34aea45
--- /dev/null
+++ b/waspc/data/Generator/templates/sdk/server/crud/_operationTypes.ts
@@ -0,0 +1,105 @@
+{{={= =}=}}
+import type {
+ {=# isAuthEnabled =}
+ AuthenticatedAction,
+ AuthenticatedQuery,
+ {=/ isAuthEnabled =}
+ {=^ isAuthEnabled =}
+ Action,
+ Query,
+ {=/ isAuthEnabled =}
+ _{= crud.entityUpper =},
+} from "wasp/server/_types";
+import type { Prisma } from "@prisma/client";
+import { Payload } from "wasp/server/_types/serialization";
+import type {
+ {= crud.entityUpper =},
+} from "wasp/entities";
+{=# overrides.GetAll.isDefined =}
+{=& overrides.GetAll.importStatement =}
+{=/ overrides.GetAll.isDefined =}
+{=# overrides.Get.isDefined =}
+{=& overrides.Get.importStatement =}
+{=/ overrides.Get.isDefined =}
+{=# overrides.Create.isDefined =}
+{=& overrides.Create.importStatement =}
+{=/ overrides.Create.isDefined =}
+{=# overrides.Update.isDefined =}
+{=& overrides.Update.importStatement =}
+{=/ overrides.Update.isDefined =}
+{=# overrides.Delete.isDefined =}
+{=& overrides.Delete.importStatement =}
+{=/ overrides.Delete.isDefined =}
+
+type _WaspEntityTagged = _{= crud.entityUpper =}
+type _WaspEntity = {= crud.entityUpper =}
+
+{=# crud.operations.GetAll =}
+// Get All query
+export type GetAllQuery = {= queryType =}<[_WaspEntityTagged], Input, Output>
+{=^ overrides.GetAll.isDefined =}
+type GetAllInput = {}
+type GetAllOutput = _WaspEntity[]
+export type GetAllQueryResolved = GetAllQuery
+{=/ overrides.GetAll.isDefined =}
+{=# overrides.GetAll.isDefined =}
+const _waspGetAllQuery = {= overrides.GetAll.importIdentifier =}
+export type GetAllQueryResolved = typeof _waspGetAllQuery
+{=/ overrides.GetAll.isDefined =}
+{=/ crud.operations.GetAll =}
+
+{=# crud.operations.Get =}
+// Get query
+export type GetQuery = {= queryType =}<[_WaspEntityTagged], Input, Output>
+{=^ overrides.Get.isDefined =}
+type GetInput = Prisma.{= crud.entityUpper =}WhereUniqueInput
+type GetOutput = _WaspEntity | null
+export type GetQueryResolved = GetQuery
+{=/ overrides.Get.isDefined =}
+{=# overrides.Get.isDefined =}
+const _waspGetQuery = {= overrides.Get.importIdentifier =}
+export type GetQueryResolved = typeof _waspGetQuery
+{=/ overrides.Get.isDefined =}
+{=/ crud.operations.Get =}
+
+{=# crud.operations.Create =}
+// Create action
+export type CreateAction= {= actionType =}<[_WaspEntityTagged], Input, Output>
+{=^ overrides.Create.isDefined =}
+type CreateInput = Prisma.{= crud.entityUpper =}CreateInput
+type CreateOutput = _WaspEntity
+export type CreateActionResolved = CreateAction
+{=/ overrides.Create.isDefined =}
+{=# overrides.Create.isDefined =}
+const _waspCreateAction = {= overrides.Create.importIdentifier =}
+export type CreateActionResolved = typeof _waspCreateAction
+{=/ overrides.Create.isDefined =}
+{=/ crud.operations.Create =}
+
+{=# crud.operations.Update =}
+// Update action
+export type UpdateAction = {= actionType =}<[_WaspEntityTagged], Input, Output>
+{=^ overrides.Update.isDefined =}
+type UpdateInput = Prisma.{= crud.entityUpper =}UpdateInput & Prisma.{= crud.entityUpper =}WhereUniqueInput
+type UpdateOutput = _WaspEntity
+export type UpdateActionResolved = UpdateAction
+{=/ overrides.Update.isDefined =}
+{=# overrides.Update.isDefined =}
+const _waspUpdateAction = {= overrides.Update.importIdentifier =}
+export type UpdateActionResolved = typeof _waspUpdateAction
+{=/ overrides.Update.isDefined =}
+{=/ crud.operations.Update =}
+
+{=# crud.operations.Delete =}
+// Delete action
+export type DeleteAction = {= actionType =}<[_WaspEntityTagged], Input, Output>
+{=^ overrides.Delete.isDefined =}
+type DeleteInput = Prisma.{= crud.entityUpper =}WhereUniqueInput
+type DeleteOutput = _WaspEntity
+export type DeleteActionResolved = DeleteAction
+{=/ overrides.Delete.isDefined =}
+{=# overrides.Delete.isDefined =}
+const _waspDeleteAction = {= overrides.Delete.importIdentifier =}
+export type DeleteActionResolved = typeof _waspDeleteAction
+{=/ overrides.Delete.isDefined =}
+{=/ crud.operations.Delete =}
diff --git a/waspc/data/Generator/templates/server/src/crud/_operations.ts b/waspc/data/Generator/templates/server/src/crud/_operations.ts
index 0185a6cfd7..31237b2dc9 100644
--- a/waspc/data/Generator/templates/server/src/crud/_operations.ts
+++ b/waspc/data/Generator/templates/server/src/crud/_operations.ts
@@ -1,25 +1,14 @@
{{={= =}=}}
import prisma from "wasp/server/dbClient";
-import type {
- {=# isAuthEnabled =}
- AuthenticatedAction,
- AuthenticatedQuery,
- {=/ isAuthEnabled =}
- {=^ isAuthEnabled =}
- Action,
- Query,
- {=/ isAuthEnabled =}
- _{= crud.entityUpper =},
-} from "../_types";
import type { Prisma } from "@prisma/client";
-import { Payload } from "../_types/serialization.js";
import type {
{= crud.entityUpper =},
-} from "../entities";
+} from "wasp/entities";
{=# isAuthEnabled =}
-import { throwInvalidCredentialsError } from '../auth/utils.js'
+import { throwInvalidCredentialsError } from 'wasp/auth/utils'
{=/ isAuthEnabled =}
+import type { GetAllQuery, GetQuery, CreateAction, UpdateAction, DeleteAction } from "{= crudTypesImportPath =}";
{=# overrides.GetAll.isDefined =}
{=& overrides.GetAll.importStatement =}
{=/ overrides.GetAll.isDefined =}
@@ -36,7 +25,6 @@ import { throwInvalidCredentialsError } from '../auth/utils.js'
{=& overrides.Delete.importStatement =}
{=/ overrides.Delete.isDefined =}
-type _WaspEntityTagged = _{= crud.entityUpper =}
type _WaspEntity = {= crud.entityUpper =}
const entities = {
{= crud.entityUpper =}: prisma.{= crud.entityLower =},
@@ -48,12 +36,7 @@ const entities = {
{=# crud.operations.GetAll =}
// Get All query
{=!
-// 1. We define the type for the operation using "queryType" template variable which is either
-// AuthenticatedQuery or Query (it depends on whether auth is enabled or not).
-=}
-export type GetAllQuery = {= queryType =}<[_WaspEntityTagged], Input, Output>
-{=!
-// 2. Then, we either use the default implementation of the operation...
+// 1. We either use the default implementation of the operation...
=}
{=^ overrides.GetAll.isDefined =}
type GetAllInput = {}
@@ -74,13 +57,7 @@ const _waspGetAllQuery = {= overrides.GetAll.importIdentifier =}
{=/ overrides.GetAll.isDefined =}
{=!
-// 3. We then define the final type for the operation, which is the type of the function we defined in the previous step.
-// It will pick up either the default implementation or the one from the overrides.
-=}
-export type GetAllQueryResolved = typeof _waspGetAllQuery
-
-{=!
-// 4. We define a function that is used as the Express route handler
+// 2. We define a function that is used as the Express route handler
=}
export async function getAllFn(args, context) {
return (_waspGetAllQuery as any)(args, {
@@ -95,7 +72,6 @@ export async function getAllFn(args, context) {
{=# crud.operations.Get =}
// Get query
-export type GetQuery = {= queryType =}<[_WaspEntityTagged], Input, Output>
{=^ overrides.Get.isDefined =}
type GetInput = Prisma.{= crud.entityUpper =}WhereUniqueInput
type GetOutput = _WaspEntity | null
@@ -109,7 +85,6 @@ const _waspGetQuery: GetQuery = ((args, context) => {
{=# overrides.Get.isDefined =}
const _waspGetQuery = {= overrides.Get.importIdentifier =}
{=/ overrides.Get.isDefined =}
-export type GetQueryResolved = typeof _waspGetQuery
export async function getFn(args, context) {
return (_waspGetQuery as any)(args, {
@@ -121,7 +96,6 @@ export async function getFn(args, context) {
{=# crud.operations.Create =}
// Create action
-export type CreateAction= {= actionType =}<[_WaspEntityTagged], Input, Output>
{=^ overrides.Create.isDefined =}
type CreateInput = Prisma.{= crud.entityUpper =}CreateInput
type CreateOutput = _WaspEntity
@@ -136,8 +110,6 @@ const _waspCreateAction: CreateAction = ((args, conte
const _waspCreateAction = {= overrides.Create.importIdentifier =}
{=/ overrides.Create.isDefined =}
-export type CreateActionResolved = typeof _waspCreateAction
-
export async function createFn(args, context) {
return (_waspCreateAction as any)(args, {
...context,
@@ -148,7 +120,6 @@ export async function createFn(args, context) {
{=# crud.operations.Update =}
// Update action
-export type UpdateAction = {= actionType =}<[_WaspEntityTagged], Input, Output>
{=^ overrides.Update.isDefined =}
type UpdateInput = Prisma.{= crud.entityUpper =}UpdateInput & Prisma.{= crud.entityUpper =}WhereUniqueInput
type UpdateOutput = _WaspEntity
@@ -167,8 +138,6 @@ const _waspUpdateAction: UpdateAction = ((args, conte
const _waspUpdateAction = {= overrides.Update.importIdentifier =}
{=/ overrides.Update.isDefined =}
-export type UpdateActionResolved = typeof _waspUpdateAction
-
export async function updateFn(args, context) {
return (_waspUpdateAction as any)(args, {
...context,
@@ -179,7 +148,6 @@ export async function updateFn(args, context) {
{=# crud.operations.Delete =}
// Delete action
-export type DeleteAction = {= actionType =}<[_WaspEntityTagged], Input, Output>
{=^ overrides.Delete.isDefined =}
type DeleteInput = Prisma.{= crud.entityUpper =}WhereUniqueInput
type DeleteOutput = _WaspEntity
@@ -195,8 +163,6 @@ const _waspDeleteAction: DeleteAction = ((args, conte
const _waspDeleteAction = {= overrides.Delete.importIdentifier =}
{=/ overrides.Delete.isDefined =}
-export type DeleteActionResolved = typeof _waspDeleteAction
-
export async function deleteFn(args, context) {
return (_waspDeleteAction as any)(args, {
...context,
diff --git a/waspc/examples/todo-typescript/.wasp/out/sdk/wasp/package.json b/waspc/examples/todo-typescript/.wasp/out/sdk/wasp/package.json
index 509f8c604e..1f6d172974 100644
--- a/waspc/examples/todo-typescript/.wasp/out/sdk/wasp/package.json
+++ b/waspc/examples/todo-typescript/.wasp/out/sdk/wasp/package.json
@@ -16,7 +16,9 @@
"./core/auth": "./dist/core/auth.js",
"./rpc": "./dist/rpc/index.js",
"./rpc/queries": "./dist/rpc/queries/index.js",
+ "./rpc/queries/core": "./dist/rpc/queries/core.js",
"./rpc/actions": "./dist/rpc/actions/index.js",
+ "./rpc/actions/core": "./dist/rpc/actions/core.js",
"./rpc/queryClient": "./dist/rpc/queryClient.js",
"./types": "./dist/types/index.js",
"./auth": "./dist/auth/index.js",
@@ -59,6 +61,8 @@
"./server/queries": "./dist/server/queries/index.js",
"./server/auth/email": "./dist/server/auth/email/index.js",
"./dbSeed/types": "./dist/dbSeed/types.js",
+ "./crud/*": "./dist/crud/*.js",
+ "./server/crud/*": "./dist/server/crud/*",
"./email": "./dist/email/index.js",
"./email/core/types": "./dist/email/core/types.js",
"./server/auth/email/utils": "./dist/server/auth/email/utils.js",
diff --git a/waspc/examples/todo-typescript/main.wasp b/waspc/examples/todo-typescript/main.wasp
index 8a9f9a0187..4acb2bafa4 100644
--- a/waspc/examples/todo-typescript/main.wasp
+++ b/waspc/examples/todo-typescript/main.wasp
@@ -70,6 +70,19 @@ entity Task {=psl
userId Int
psl=}
+crud Tasks {
+ entity: Task,
+ operations: {
+ getAll: {
+ overrideFn: import { getAllQuery } from "@src/task/crud.js",
+ },
+ create: {},
+ update: {},
+ get: {},
+ delete: {}
+ }
+}
+
route RootRoute { path: "/", to: MainPage }
page MainPage {
authRequired: true,
diff --git a/waspc/examples/todo-typescript/src/MainPage.tsx b/waspc/examples/todo-typescript/src/MainPage.tsx
index 59ee858266..89d3ab2caa 100644
--- a/waspc/examples/todo-typescript/src/MainPage.tsx
+++ b/waspc/examples/todo-typescript/src/MainPage.tsx
@@ -1,34 +1,37 @@
-import "./Main.css";
-import React, { useEffect, FormEventHandler, FormEvent } from "react";
-import logout from "wasp/auth/logout";
-import { useQuery, useAction } from "wasp/rpc"; // Wasp uses a thin wrapper around react-query
-import { getTasks } from "wasp/rpc/queries";
+import './Main.css'
+import React, { useEffect, FormEventHandler, FormEvent } from 'react'
+import logout from 'wasp/auth/logout'
+import { useQuery, useAction } from 'wasp/rpc' // Wasp uses a thin wrapper around react-query
+import { getTasks } from 'wasp/rpc/queries'
import {
createTask,
updateTask,
deleteTasks,
customEmailSending,
-} from "wasp/rpc/actions";
-import waspLogo from "./waspLogo.png";
-import type { Task } from "wasp/entities";
-import type { User } from "wasp/auth/types";
-import { getFirstProviderUserId } from "wasp/auth/user";
-import { Link } from "react-router-dom";
+} from 'wasp/rpc/actions'
+import waspLogo from './waspLogo.png'
+import type { Task } from 'wasp/entities'
+import type { User } from 'wasp/auth/types'
+import { getFirstProviderUserId } from 'wasp/auth/user'
+import { Link } from 'react-router-dom'
+import { Tasks } from 'wasp/crud/Tasks'
// import login from "wasp/auth/login";
// import signup from "wasp/auth/signup";
import useAuth from 'wasp/auth/useAuth'
export const MainPage = ({ user }: { user: User }) => {
- const { data: tasks, isLoading, error } = useQuery(getTasks);
+ const { data: tasks, isLoading, error } = useQuery(getTasks)
const { data: userAgain } = useAuth()
- if (isLoading) return "Loading...";
- if (error) return "Error: " + error;
+ const { data: allTasks } = Tasks.getAll.useQuery()
+
+ if (isLoading) return 'Loading...'
+ if (error) return 'Error: ' + error
// console.log(login);
// console.log(signup);
- const completed = tasks?.filter((task) => task.isDone).map((task) => task.id);
+ const completed = tasks?.filter((task) => task.isDone).map((task) => task.id)
return (
@@ -45,6 +48,8 @@ export const MainPage = ({ user }: { user: User }) => {
)}
{tasks && }
+ All
+ {allTasks && }
- );
-};
+ )
+}
function Todo({ id, isDone, description }: Task) {
const handleIsDoneChange: FormEventHandler = async (
@@ -68,11 +73,11 @@ function Todo({ id, isDone, description }: Task) {
await updateTask({
id,
isDone: event.currentTarget.checked,
- });
+ })
} catch (err: any) {
- window.alert("Error while updating task " + err?.message);
+ window.alert('Error while updating task ' + err?.message)
}
- };
+ }
return (
@@ -87,38 +92,38 @@ function Todo({ id, isDone, description }: Task) {
- );
+ )
}
function TasksList({ tasks }: { tasks: Task[] }) {
- if (tasks.length === 0) return No tasks yet.
;
+ if (tasks.length === 0) return No tasks yet.
return (
{tasks.map((task, idx) => (
))}
- );
+ )
}
function NewTaskForm() {
const handleSubmit = async (event: FormEvent) => {
- event.preventDefault();
+ event.preventDefault()
try {
- const description = event.currentTarget.description.value;
- console.log(description);
- event.currentTarget.reset();
- await createTask({ description });
+ const description = event.currentTarget.description.value
+ console.log(description)
+ event.currentTarget.reset()
+ await createTask({ description })
} catch (err: any) {
- window.alert("Error: " + err?.message);
+ window.alert('Error: ' + err?.message)
}
- };
+ }
return (
- );
+ )
}
diff --git a/waspc/examples/todo-typescript/src/task/crud.ts b/waspc/examples/todo-typescript/src/task/crud.ts
new file mode 100644
index 0000000000..042cdf7563
--- /dev/null
+++ b/waspc/examples/todo-typescript/src/task/crud.ts
@@ -0,0 +1,6 @@
+import { Task } from "wasp/entities";
+import { GetAllQuery } from "wasp/server/crud/Tasks";
+
+export const getAllQuery = ((args, context) => {
+ return context.entities.Task.findMany({});
+}) satisfies GetAllQuery<{}, Task[]>;
diff --git a/waspc/src/Wasp/Generator/Common.hs b/waspc/src/Wasp/Generator/Common.hs
index 5ce25639ca..cecdd011d8 100644
--- a/waspc/src/Wasp/Generator/Common.hs
+++ b/waspc/src/Wasp/Generator/Common.hs
@@ -10,14 +10,18 @@ module Wasp.Generator.Common
makeJsonWithEntityData,
GeneratedSrcDir,
makeJsArrayFromHaskellList,
+ dropExtensionFromImportPath,
)
where
import Data.Aeson (KeyValue ((.=)), object)
import qualified Data.Aeson as Aeson
import Data.List (intercalate)
-import StrongPath (Dir, Rel, reldir)
+import Data.Maybe (fromJust)
+import StrongPath (Dir, File, Path, Posix, Rel, reldir)
+import qualified StrongPath as SP
import StrongPath.Types (Path')
+import System.FilePath (splitExtension)
import Wasp.Generator.Templates (TemplatesDir)
import qualified Wasp.SemanticVersion as SV
import Wasp.Util (toLowerFirst)
@@ -72,3 +76,8 @@ makeJsArrayFromHaskellList :: [String] -> String
makeJsArrayFromHaskellList list = "[" ++ intercalate ", " listOfJsStrings ++ "]"
where
listOfJsStrings = map (\s -> "'" ++ s ++ "'") list
+
+dropExtensionFromImportPath :: Path Posix (Rel r) (File f) -> Path Posix (Rel r) (File f)
+dropExtensionFromImportPath = fromJust . SP.parseRelFileP . dropExtension . SP.fromRelFileP
+ where
+ dropExtension = fst . splitExtension
diff --git a/waspc/src/Wasp/Generator/SdkGenerator.hs b/waspc/src/Wasp/Generator/SdkGenerator.hs
index 3607d51788..9a9684d6ef 100644
--- a/waspc/src/Wasp/Generator/SdkGenerator.hs
+++ b/waspc/src/Wasp/Generator/SdkGenerator.hs
@@ -38,6 +38,7 @@ import qualified Wasp.Generator.NpmDependencies as N
import Wasp.Generator.SdkGenerator.ApiRoutesG (genApis)
import Wasp.Generator.SdkGenerator.AuthG (genAuth)
import qualified Wasp.Generator.SdkGenerator.Common as C
+import Wasp.Generator.SdkGenerator.CrudG (genCrud)
import Wasp.Generator.SdkGenerator.EmailSenderG (depsRequiredByEmail, genEmailSender)
import Wasp.Generator.SdkGenerator.JobGenerator (genJobTypes)
import Wasp.Generator.SdkGenerator.RouterGenerator (genRouter)
@@ -97,6 +98,7 @@ genSdkReal spec =
<++> genUniversalDir
<++> genExternalCodeDir (AS.externalCodeFiles spec)
<++> genEntitiesAndServerTypesDirs spec
+ <++> genCrud spec
<++> genJobTypes spec
<++> genApis spec
<++> genWebSockets spec
diff --git a/waspc/src/Wasp/Generator/SdkGenerator/CrudG.hs b/waspc/src/Wasp/Generator/SdkGenerator/CrudG.hs
new file mode 100644
index 0000000000..6f3b08284c
--- /dev/null
+++ b/waspc/src/Wasp/Generator/SdkGenerator/CrudG.hs
@@ -0,0 +1,96 @@
+module Wasp.Generator.SdkGenerator.CrudG
+ ( genCrud,
+ getCrudTypesImportPathForName,
+ )
+where
+
+import Data.Aeson (KeyValue ((.=)), object)
+import qualified Data.Aeson.Types as Aeson.Types
+import Data.Maybe (fromJust)
+import StrongPath
+ ( File',
+ Path,
+ Posix,
+ Rel,
+ reldir,
+ reldirP,
+ relfile,
+ (>),
+ )
+import qualified StrongPath as SP
+import Wasp.AppSpec (AppSpec, getCruds)
+import qualified Wasp.AppSpec as AS
+import qualified Wasp.AppSpec.App as AS.App
+import qualified Wasp.AppSpec.App.Auth as AS.Auth
+import qualified Wasp.AppSpec.Crud as AS.Crud
+import Wasp.AppSpec.Valid (getApp, getIdFieldFromCrudEntity, isAuthEnabled)
+import Wasp.Generator.Crud (crudDeclarationToOperationsList, getCrudFilePath, getCrudOperationJson, makeCrudOperationKeyAndJsonPair)
+import Wasp.Generator.FileDraft (FileDraft)
+import qualified Wasp.Generator.JsImport as GJI
+import Wasp.Generator.Monad (Generator)
+import Wasp.Generator.SdkGenerator.Common (makeSdkImportPath)
+import qualified Wasp.Generator.SdkGenerator.Common as C
+import Wasp.Generator.SdkGenerator.ServerOpsGenerator (extImportToJsImport)
+import Wasp.Util ((<++>))
+
+genCrud :: AppSpec -> Generator [FileDraft]
+genCrud spec =
+ if areThereAnyCruds
+ then
+ genCrudOperations spec cruds
+ <++> genCrudServerOperations spec cruds
+ else return []
+ where
+ cruds = getCruds spec
+ areThereAnyCruds = not $ null cruds
+
+genCrudOperations :: AppSpec -> [(String, AS.Crud.Crud)] -> Generator [FileDraft]
+genCrudOperations spec cruds = return $ map genCrudOperation cruds
+ where
+ genCrudOperation :: (String, AS.Crud.Crud) -> FileDraft
+ genCrudOperation (name, crud) = C.mkTmplFdWithDstAndData tmplPath destPath (Just tmplData)
+ where
+ tmplPath = [relfile|crud/_crud.ts|]
+ destPath = [reldir|crud|] > fromJust (SP.parseRelFile (name ++ ".ts"))
+ tmplData = getCrudOperationJson name crud idField
+ idField = getIdFieldFromCrudEntity spec crud
+
+genCrudServerOperations :: AppSpec -> [(String, AS.Crud.Crud)] -> Generator [FileDraft]
+genCrudServerOperations spec cruds = return $ map genCrudOperation cruds
+ where
+ genCrudOperation :: (String, AS.Crud.Crud) -> FileDraft
+ genCrudOperation (name, crud) = C.mkTmplFdWithDstAndData tmplPath destPath (Just tmplData)
+ where
+ tmplPath = [relfile|server/crud/_operationTypes.ts|]
+ destPath = [reldir|server/crud|] > getCrudFilePath name "ts"
+ tmplData =
+ object
+ [ "crud" .= getCrudOperationJson name crud idField,
+ "isAuthEnabled" .= isAuthEnabled spec,
+ "userEntityUpper" .= maybeUserEntity,
+ "overrides" .= object overrides,
+ "queryType" .= queryTsType,
+ "actionType" .= actionTsType
+ ]
+ idField = getIdFieldFromCrudEntity spec crud
+ maybeUserEntity = AS.refName . AS.Auth.userEntity <$> maybeAuth
+ maybeAuth = AS.App.auth $ snd $ getApp spec
+
+ queryTsType :: String
+ queryTsType = if isAuthEnabled spec then "AuthenticatedQuery" else "Query"
+
+ actionTsType :: String
+ actionTsType = if isAuthEnabled spec then "AuthenticatedAction" else "Action"
+
+ overrides :: [Aeson.Types.Pair]
+ overrides = map operationToOverrideImport crudOperations
+
+ crudOperations = crudDeclarationToOperationsList crud
+
+ operationToOverrideImport :: (AS.Crud.CrudOperation, AS.Crud.CrudOperationOptions) -> Aeson.Types.Pair
+ operationToOverrideImport (operation, options) = makeCrudOperationKeyAndJsonPair operation importJson
+ where
+ importJson = GJI.jsImportToImportJson $ extImportToJsImport <$> AS.Crud.overrideFn options
+
+getCrudTypesImportPathForName :: String -> Path Posix (Rel r) File'
+getCrudTypesImportPathForName crudName = makeSdkImportPath $ [reldirP|server/crud|] > fromJust (SP.parseRelFileP crudName)
diff --git a/waspc/src/Wasp/Generator/ServerGenerator/CrudG.hs b/waspc/src/Wasp/Generator/ServerGenerator/CrudG.hs
index 0b5689d582..bdf184a8dd 100644
--- a/waspc/src/Wasp/Generator/ServerGenerator/CrudG.hs
+++ b/waspc/src/Wasp/Generator/ServerGenerator/CrudG.hs
@@ -24,6 +24,7 @@ import Wasp.Generator.Crud
import qualified Wasp.Generator.Crud.Routes as Routes
import Wasp.Generator.FileDraft (FileDraft)
import Wasp.Generator.Monad (Generator)
+import Wasp.Generator.SdkGenerator.CrudG (getCrudTypesImportPathForName)
import qualified Wasp.Generator.ServerGenerator.Common as C
import Wasp.Generator.ServerGenerator.JsImport (extImportToImportJson)
import Wasp.JsImport (JsImportPath (RelativeImportPath))
@@ -95,7 +96,8 @@ genCrudOperations spec cruds = return $ map genCrudOperation cruds
"userEntityUpper" .= maybeUserEntity,
"overrides" .= object overrides,
"queryType" .= queryTsType,
- "actionType" .= actionTsType
+ "actionType" .= actionTsType,
+ "crudTypesImportPath" .= SP.fromRelFileP (getCrudTypesImportPathForName name)
]
idField = getIdFieldFromCrudEntity spec crud
maybeUserEntity = AS.refName . AS.Auth.userEntity <$> maybeAuth
diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs
index 63c68bc8d0..e48a9f8c52 100644
--- a/waspc/src/Wasp/Generator/WebAppGenerator.hs
+++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs
@@ -43,7 +43,6 @@ import Wasp.Generator.Monad (Generator)
import qualified Wasp.Generator.NpmDependencies as N
import Wasp.Generator.WebAppGenerator.AuthG (genAuth)
import qualified Wasp.Generator.WebAppGenerator.Common as C
-import Wasp.Generator.WebAppGenerator.CrudG (genCrud)
import Wasp.Generator.WebAppGenerator.JsImport (extImportToImportJson)
import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter)
import qualified Wasp.Generator.WebSocket as AS.WS
@@ -77,7 +76,6 @@ genWebApp spec = do
<++> genPublicDir spec
<++> genDotEnv spec
<++> genEnvValidationScript
- <++> genCrud spec
where
genFileCopy = return . C.mkTmplFd
diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs b/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs
index ef001efdef..96420b860b 100644
--- a/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs
+++ b/waspc/src/Wasp/Generator/WebAppGenerator/Common.hs
@@ -28,10 +28,9 @@ module Wasp.Generator.WebAppGenerator.Common
where
import qualified Data.Aeson as Aeson
-import Data.Maybe (fromJust, fromMaybe)
+import Data.Maybe (fromMaybe)
import StrongPath (Abs, Dir, File, File', Path, Path', Posix, Rel, absdirP, reldir, (>))
import qualified StrongPath as SP
-import System.FilePath (splitExtension)
import Wasp.AppSpec (AppSpec)
import qualified Wasp.AppSpec.App as AS.App
import qualified Wasp.AppSpec.App.Client as AS.App.Client
@@ -44,6 +43,7 @@ import Wasp.Generator.Common
ServerRootDir,
UniversalTemplatesDir,
WebAppRootDir,
+ dropExtensionFromImportPath,
universalTemplatesDirInTemplatesDir,
)
import Wasp.Generator.FileDraft (FileDraft, createCopyFileDraft, createTemplateFileDraft)
@@ -138,9 +138,7 @@ mkUniversalTmplFdWithDst relSrcPath relDstPath =
Nothing
toViteImportPath :: Path Posix (Rel r) (File f) -> Path Posix (Rel r) (File f)
-toViteImportPath = fromJust . SP.parseRelFileP . dropExtension . SP.fromRelFileP
- where
- dropExtension = fst . splitExtension
+toViteImportPath = dropExtensionFromImportPath
getBaseDir :: AppSpec -> Path Posix Abs (Dir ())
getBaseDir spec = fromMaybe [absdirP|/|] maybeBaseDir
diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/CrudG.hs b/waspc/src/Wasp/Generator/WebAppGenerator/CrudG.hs
deleted file mode 100644
index 397b85efed..0000000000
--- a/waspc/src/Wasp/Generator/WebAppGenerator/CrudG.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-module Wasp.Generator.WebAppGenerator.CrudG
- ( genCrud,
- )
-where
-
-import Data.Maybe (fromJust)
-import StrongPath (reldir, relfile, (>))
-import qualified StrongPath as SP
-import Wasp.AppSpec (AppSpec, getCruds)
-import qualified Wasp.AppSpec.Crud as AS.Crud
-import Wasp.AppSpec.Valid (getIdFieldFromCrudEntity)
-import Wasp.Generator.Crud (getCrudOperationJson)
-import Wasp.Generator.FileDraft (FileDraft)
-import Wasp.Generator.Monad (Generator)
-import qualified Wasp.Generator.WebAppGenerator.Common as C
-
-genCrud :: AppSpec -> Generator [FileDraft]
-genCrud spec =
- if areThereAnyCruds
- then genCrudOperations spec cruds
- else return []
- where
- cruds = getCruds spec
- areThereAnyCruds = not $ null cruds
-
-genCrudOperations :: AppSpec -> [(String, AS.Crud.Crud)] -> Generator [FileDraft]
-genCrudOperations spec cruds = return $ map genCrudOperation cruds
- where
- genCrudOperation :: (String, AS.Crud.Crud) -> FileDraft
- genCrudOperation (name, crud) = C.mkTmplFdWithDstAndData tmplPath destPath (Just tmplData)
- where
- tmplPath = [relfile|src/crud/_crud.ts|]
- destPath = C.webAppSrcDirInWebAppRootDir > [reldir|crud|] > fromJust (SP.parseRelFile (name ++ ".ts"))
- tmplData = getCrudOperationJson name crud idField
- idField = getIdFieldFromCrudEntity spec crud
diff --git a/waspc/waspc.cabal b/waspc/waspc.cabal
index e5995e29b7..9d24c8a987 100644
--- a/waspc/waspc.cabal
+++ b/waspc/waspc.cabal
@@ -294,6 +294,7 @@ library
Wasp.Generator.SdkGenerator.ApiRoutesG
Wasp.Generator.SdkGenerator.Auth.AuthFormsG
Wasp.Generator.SdkGenerator.Auth.EmailAuthG
+ Wasp.Generator.SdkGenerator.CrudG
Wasp.Generator.SdkGenerator.Auth.LocalAuthG
Wasp.Generator.SdkGenerator.Auth.OAuthAuthG
Wasp.Generator.SdkGenerator.AuthG
@@ -335,7 +336,6 @@ library
Wasp.Generator.WebAppGenerator.Start
Wasp.Generator.WebAppGenerator.Test
Wasp.Generator.WebSocket
- Wasp.Generator.WebAppGenerator.CrudG
Wasp.Generator.WriteFileDrafts
Wasp.JsImport
Wasp.Message