mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-23 10:14:08 +03:00
Add manual TypeScript support for Operations (#945)
* Support typing backend queries * Implement types for actions and extract entities * Add CR fixes * Add type to imports and exports for entities * Change query extensions back to .js * Tidy up default arguments for operation types * Don't throw syntax error when there are no entities * Update e2e tests * Fix formatting * Fix typo in comment * Update changelog and bump project version
This commit is contained in:
parent
2ed921a442
commit
81ffa6c27c
@ -1,5 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## v0.8.1
|
||||
|
||||
### Import Wasp entity types on the backend
|
||||
You can now import and use the types of Wasp entities in your backend code:
|
||||
```typescript
|
||||
import { Task } from '@wasp/entities/Task'
|
||||
|
||||
const getTasks = (args, context): Task[] => {
|
||||
const tasks: Task[] = // ...
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript support for Queries and Actions
|
||||
Wasp now includes generic type constructors for typing Queries and Actions.
|
||||
This features works hand-in-hand with Wasp entity types described in the
|
||||
previous chapter:
|
||||
```typescript
|
||||
import { Query } from '@wasp/queries'
|
||||
import { Task } from '@wasp/entities/task'
|
||||
|
||||
const getTasks: Query<[Task]> = (args, context) => {
|
||||
// The compiler knows the types of 'args' and 'context'
|
||||
// inside the function's body.
|
||||
}
|
||||
```
|
||||
|
||||
## v0.8.0
|
||||
|
||||
### BREAKING CHANGES
|
||||
@ -117,9 +144,9 @@ directory `foo`, you should:
|
||||
// This previously resolved to ext/LoginPage.js
|
||||
component: import Login from "@ext/LoginPage.js"
|
||||
}
|
||||
|
||||
|
||||
// ...
|
||||
|
||||
|
||||
query getTasks {
|
||||
// This previously resolved to ext/queries.js
|
||||
fn: import { getTasks } from "@ext/queries.js",
|
||||
@ -133,16 +160,16 @@ directory `foo`, you should:
|
||||
// This resolves to src/client/LoginPage.js
|
||||
component: import Login from "@client/LoginPage"
|
||||
}
|
||||
|
||||
|
||||
// ...
|
||||
|
||||
|
||||
query getTasks {
|
||||
// This resolves to src/server/queries.js
|
||||
fn: import { getTasks } from "@server/queries.js",
|
||||
}
|
||||
```
|
||||
Do this for all external imports in your `.wasp` file. After you're done, there shouldn't be any occurences of the string `"@ext"`.
|
||||
|
||||
|
||||
That's it! You should now have a fully working Wasp project in the `foo` directory.
|
||||
|
||||
### [NEW FEATURE] TypeScript support
|
||||
|
@ -6,11 +6,13 @@ import prisma from '../dbClient.js'
|
||||
{=! TODO: This template is exactly the same at the moment as one for queries,
|
||||
consider in the future if it is worth removing this duplication. =}
|
||||
|
||||
export default async (args, context) => {
|
||||
context = { ...context, entities: {
|
||||
{=# entities =}
|
||||
{= name =}: prisma.{= prismaIdentifier =},
|
||||
{=/ entities =}
|
||||
}}
|
||||
return {= jsFnIdentifier =}(args, context)
|
||||
export default async function (args, context) {
|
||||
return {= jsFnIdentifier =}(args, {
|
||||
...context,
|
||||
entities: {
|
||||
{=# entities =}
|
||||
{= name =}: prisma.{= prismaIdentifier =},
|
||||
{=/ entities =}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -44,6 +44,10 @@ const auth = handleRejection(async (req, res, next) => {
|
||||
return res.status(401).send()
|
||||
}
|
||||
|
||||
// TODO: This logic must match the type in types/index.ts (if we remove the
|
||||
// password field from the object here, we must to do the same there).
|
||||
// Ideally, these two things would live in the same place:
|
||||
// https://github.com/wasp-lang/wasp/issues/965
|
||||
const { password, ...userView } = user
|
||||
|
||||
req.user = userView
|
||||
|
18
waspc/data/Generator/templates/server/src/entities/index.ts
Normal file
18
waspc/data/Generator/templates/server/src/entities/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
{{={= =}=}}
|
||||
import {
|
||||
{=# entities =}
|
||||
type {= name =},
|
||||
{=/ entities =}
|
||||
} from "@prisma/client"
|
||||
|
||||
export {
|
||||
{=# entities =}
|
||||
type {= name =},
|
||||
{=/ entities =}
|
||||
} from "@prisma/client"
|
||||
|
||||
export type WaspEntity =
|
||||
{=# entities =}
|
||||
| {= name =}
|
||||
{=/ entities =}
|
||||
| never
|
@ -3,14 +3,16 @@ import prisma from '../dbClient.js'
|
||||
|
||||
{=& jsFnImportStatement =}
|
||||
|
||||
{=! TODO: This template is exactly the same at the moment as one for queries,
|
||||
{=! TODO: This template is exactly the same at the moment as one for actions,
|
||||
consider in the future if it is worth removing this duplication. =}
|
||||
|
||||
export default async (args, context) => {
|
||||
context = { ...context, entities: {
|
||||
{=# entities =}
|
||||
{= name =}: prisma.{= prismaIdentifier =},
|
||||
{=/ entities =}
|
||||
}}
|
||||
return {= jsFnIdentifier =}(args, context)
|
||||
export default async function (args, context) {
|
||||
return {= jsFnIdentifier =}(args, {
|
||||
...context,
|
||||
entities: {
|
||||
{=# entities =}
|
||||
{= name =}: prisma.{= prismaIdentifier =},
|
||||
{=/ entities =}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {= importIdentifier =} from '{= importPath =}'
|
||||
const router = express.Router()
|
||||
|
||||
{=# operationRoutes =}
|
||||
router.post('{= routePath =}', {=# isUsingAuth =} auth, {=/ isUsingAuth =} {= importIdentifier =})
|
||||
router.post('{= routePath =}',{=# isUsingAuth =} auth,{=/ isUsingAuth =} {= importIdentifier =})
|
||||
{=/ operationRoutes =}
|
||||
|
||||
export default router
|
||||
|
57
waspc/data/Generator/templates/server/src/types/index.ts
Normal file
57
waspc/data/Generator/templates/server/src/types/index.ts
Normal file
@ -0,0 +1,57 @@
|
||||
{{={= =}=}}
|
||||
import prisma from "../dbClient.js"
|
||||
import {
|
||||
type WaspEntity,
|
||||
{=# entities =}
|
||||
type {= name =},
|
||||
{=/ entities =}
|
||||
} from "../entities"
|
||||
|
||||
export type Query<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
export type Action<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
{=# isAuthEnabled =}
|
||||
export type AuthenticatedQuery<Entities extends WaspEntity[] = [], Result = unknown> =
|
||||
AuthenticatedOperation<Entities, Result>
|
||||
|
||||
export type AuthenticatedAction<Entities extends WaspEntity[] = [], Result = unknown> =
|
||||
AuthenticatedOperation<Entities, Result>
|
||||
|
||||
type AuthenticatedOperation<Entities extends WaspEntity[], Result> = (
|
||||
args: any,
|
||||
context: {
|
||||
user: {= userViewName =},
|
||||
entities: EntityMap<Entities>,
|
||||
},
|
||||
) => Promise<Result>
|
||||
|
||||
// TODO: This type must match the logic in core/auth.js (if we remove the
|
||||
// password field from the object there, we must do the same here). Ideally,
|
||||
// these two things would live in the same place:
|
||||
// https://github.com/wasp-lang/wasp/issues/965
|
||||
type {= userViewName =} = Omit<{= userEntityName =}, 'password'>
|
||||
{=/ isAuthEnabled =}
|
||||
|
||||
type Operation<Entities extends WaspEntity[], Result> = (
|
||||
args: any,
|
||||
context: {
|
||||
entities: EntityMap<Entities>,
|
||||
},
|
||||
) => Promise<Result>
|
||||
|
||||
type PrismaDelegateFor<EntityName extends string> =
|
||||
{=# entities =}
|
||||
EntityName extends "{= name =}" ? typeof prisma.{= prismaIdentifier =} :
|
||||
{=/ entities =}
|
||||
never
|
||||
|
||||
type WaspNameFor<Entity extends WaspEntity> =
|
||||
{=# entities =}
|
||||
Entity extends {= name =} ? "{= name =}" :
|
||||
{=/ entities =}
|
||||
never
|
||||
|
||||
type EntityMap<Entities extends WaspEntity[]> = {
|
||||
[EntityName in WaspNameFor<Entities[number]>]: PrismaDelegateFor<EntityName>
|
||||
}
|
@ -12,6 +12,7 @@ waspBuild/.wasp/build/server/src/config.js
|
||||
waspBuild/.wasp/build/server/src/core/AuthError.js
|
||||
waspBuild/.wasp/build/server/src/core/HttpError.js
|
||||
waspBuild/.wasp/build/server/src/dbClient.js
|
||||
waspBuild/.wasp/build/server/src/entities/index.ts
|
||||
waspBuild/.wasp/build/server/src/jobs/core/Job.js
|
||||
waspBuild/.wasp/build/server/src/jobs/core/SubmittedJob.js
|
||||
waspBuild/.wasp/build/server/src/jobs/core/allJobs.js
|
||||
@ -21,6 +22,7 @@ waspBuild/.wasp/build/server/src/jobs/core/simpleJob.js
|
||||
waspBuild/.wasp/build/server/src/routes/index.js
|
||||
waspBuild/.wasp/build/server/src/routes/operations/index.js
|
||||
waspBuild/.wasp/build/server/src/server.ts
|
||||
waspBuild/.wasp/build/server/src/types/index.ts
|
||||
waspBuild/.wasp/build/server/src/utils.js
|
||||
waspBuild/.wasp/build/server/tsconfig.json
|
||||
waspBuild/.wasp/build/web-app/.npmrc
|
||||
|
@ -90,6 +90,13 @@
|
||||
],
|
||||
"20c67ca197da3de2d37528ceaff2e40af910be8177f346c6d5c2b2f983810c43"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/entities/index.ts"
|
||||
],
|
||||
"d9a8968d93bb382e1473765db426fb37b0cbe4a6dc4a158140065e3e657a8ac6"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
@ -153,6 +160,13 @@
|
||||
],
|
||||
"c6114654819216004f0ef4a8771a0c1213c655cfd41760fbd835077c53a6b6ba"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/types/index.ts"
|
||||
],
|
||||
"5e097ef2cf636d2b481c303987aaabeb5276900a6dcbd0fd6fa031c95616058b"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
|
8
waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/entities/index.ts
generated
Normal file
8
waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/entities/index.ts
generated
Normal file
@ -0,0 +1,8 @@
|
||||
import {
|
||||
} from "@prisma/client"
|
||||
|
||||
export {
|
||||
} from "@prisma/client"
|
||||
|
||||
export type WaspEntity =
|
||||
| never
|
26
waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/types/index.ts
generated
Normal file
26
waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/server/src/types/index.ts
generated
Normal file
@ -0,0 +1,26 @@
|
||||
import prisma from "../dbClient.js"
|
||||
import {
|
||||
type WaspEntity,
|
||||
} from "../entities"
|
||||
|
||||
export type Query<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
export type Action<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
|
||||
type Operation<Entities extends WaspEntity[], Result> = (
|
||||
args: any,
|
||||
context: {
|
||||
entities: EntityMap<Entities>,
|
||||
},
|
||||
) => Promise<Result>
|
||||
|
||||
type PrismaDelegateFor<EntityName extends string> =
|
||||
never
|
||||
|
||||
type WaspNameFor<Entity extends WaspEntity> =
|
||||
never
|
||||
|
||||
type EntityMap<Entities extends WaspEntity[]> = {
|
||||
[EntityName in WaspNameFor<Entities[number]>]: PrismaDelegateFor<EntityName>
|
||||
}
|
@ -12,6 +12,7 @@ waspCompile/.wasp/out/server/src/config.js
|
||||
waspCompile/.wasp/out/server/src/core/AuthError.js
|
||||
waspCompile/.wasp/out/server/src/core/HttpError.js
|
||||
waspCompile/.wasp/out/server/src/dbClient.js
|
||||
waspCompile/.wasp/out/server/src/entities/index.ts
|
||||
waspCompile/.wasp/out/server/src/jobs/core/Job.js
|
||||
waspCompile/.wasp/out/server/src/jobs/core/SubmittedJob.js
|
||||
waspCompile/.wasp/out/server/src/jobs/core/allJobs.js
|
||||
@ -21,6 +22,7 @@ waspCompile/.wasp/out/server/src/jobs/core/simpleJob.js
|
||||
waspCompile/.wasp/out/server/src/routes/index.js
|
||||
waspCompile/.wasp/out/server/src/routes/operations/index.js
|
||||
waspCompile/.wasp/out/server/src/server.ts
|
||||
waspCompile/.wasp/out/server/src/types/index.ts
|
||||
waspCompile/.wasp/out/server/src/utils.js
|
||||
waspCompile/.wasp/out/server/tsconfig.json
|
||||
waspCompile/.wasp/out/web-app/.npmrc
|
||||
|
@ -90,6 +90,13 @@
|
||||
],
|
||||
"20c67ca197da3de2d37528ceaff2e40af910be8177f346c6d5c2b2f983810c43"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/entities/index.ts"
|
||||
],
|
||||
"d9a8968d93bb382e1473765db426fb37b0cbe4a6dc4a158140065e3e657a8ac6"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
@ -153,6 +160,13 @@
|
||||
],
|
||||
"c6114654819216004f0ef4a8771a0c1213c655cfd41760fbd835077c53a6b6ba"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/types/index.ts"
|
||||
],
|
||||
"5e097ef2cf636d2b481c303987aaabeb5276900a6dcbd0fd6fa031c95616058b"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
|
@ -0,0 +1,8 @@
|
||||
import {
|
||||
} from "@prisma/client"
|
||||
|
||||
export {
|
||||
} from "@prisma/client"
|
||||
|
||||
export type WaspEntity =
|
||||
| never
|
26
waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/src/types/index.ts
generated
Normal file
26
waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/server/src/types/index.ts
generated
Normal file
@ -0,0 +1,26 @@
|
||||
import prisma from "../dbClient.js"
|
||||
import {
|
||||
type WaspEntity,
|
||||
} from "../entities"
|
||||
|
||||
export type Query<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
export type Action<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
|
||||
type Operation<Entities extends WaspEntity[], Result> = (
|
||||
args: any,
|
||||
context: {
|
||||
entities: EntityMap<Entities>,
|
||||
},
|
||||
) => Promise<Result>
|
||||
|
||||
type PrismaDelegateFor<EntityName extends string> =
|
||||
never
|
||||
|
||||
type WaspNameFor<Entity extends WaspEntity> =
|
||||
never
|
||||
|
||||
type EntityMap<Entities extends WaspEntity[]> = {
|
||||
[EntityName in WaspNameFor<Entities[number]>]: PrismaDelegateFor<EntityName>
|
||||
}
|
@ -12,6 +12,7 @@ waspJob/.wasp/out/server/src/config.js
|
||||
waspJob/.wasp/out/server/src/core/AuthError.js
|
||||
waspJob/.wasp/out/server/src/core/HttpError.js
|
||||
waspJob/.wasp/out/server/src/dbClient.js
|
||||
waspJob/.wasp/out/server/src/entities/index.ts
|
||||
waspJob/.wasp/out/server/src/ext-src/jobs/bar.js
|
||||
waspJob/.wasp/out/server/src/jobs/MySpecialJob.js
|
||||
waspJob/.wasp/out/server/src/jobs/core/Job.js
|
||||
@ -23,6 +24,7 @@ waspJob/.wasp/out/server/src/jobs/core/simpleJob.js
|
||||
waspJob/.wasp/out/server/src/routes/index.js
|
||||
waspJob/.wasp/out/server/src/routes/operations/index.js
|
||||
waspJob/.wasp/out/server/src/server.ts
|
||||
waspJob/.wasp/out/server/src/types/index.ts
|
||||
waspJob/.wasp/out/server/src/utils.js
|
||||
waspJob/.wasp/out/server/tsconfig.json
|
||||
waspJob/.wasp/out/web-app/.npmrc
|
||||
|
@ -90,6 +90,13 @@
|
||||
],
|
||||
"20c67ca197da3de2d37528ceaff2e40af910be8177f346c6d5c2b2f983810c43"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/entities/index.ts"
|
||||
],
|
||||
"d9a8968d93bb382e1473765db426fb37b0cbe4a6dc4a158140065e3e657a8ac6"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
@ -167,6 +174,13 @@
|
||||
],
|
||||
"3680f7afaf38fb9e32cb3bc9c64641224e2d2867bdc912730114cbf9360bf9f0"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/types/index.ts"
|
||||
],
|
||||
"5e097ef2cf636d2b481c303987aaabeb5276900a6dcbd0fd6fa031c95616058b"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
|
8
waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/entities/index.ts
generated
Normal file
8
waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/entities/index.ts
generated
Normal file
@ -0,0 +1,8 @@
|
||||
import {
|
||||
} from "@prisma/client"
|
||||
|
||||
export {
|
||||
} from "@prisma/client"
|
||||
|
||||
export type WaspEntity =
|
||||
| never
|
26
waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/types/index.ts
generated
Normal file
26
waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/server/src/types/index.ts
generated
Normal file
@ -0,0 +1,26 @@
|
||||
import prisma from "../dbClient.js"
|
||||
import {
|
||||
type WaspEntity,
|
||||
} from "../entities"
|
||||
|
||||
export type Query<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
export type Action<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
|
||||
type Operation<Entities extends WaspEntity[], Result> = (
|
||||
args: any,
|
||||
context: {
|
||||
entities: EntityMap<Entities>,
|
||||
},
|
||||
) => Promise<Result>
|
||||
|
||||
type PrismaDelegateFor<EntityName extends string> =
|
||||
never
|
||||
|
||||
type WaspNameFor<Entity extends WaspEntity> =
|
||||
never
|
||||
|
||||
type EntityMap<Entities extends WaspEntity[]> = {
|
||||
[EntityName in WaspNameFor<Entities[number]>]: PrismaDelegateFor<EntityName>
|
||||
}
|
@ -17,6 +17,7 @@ waspMigrate/.wasp/out/server/src/config.js
|
||||
waspMigrate/.wasp/out/server/src/core/AuthError.js
|
||||
waspMigrate/.wasp/out/server/src/core/HttpError.js
|
||||
waspMigrate/.wasp/out/server/src/dbClient.js
|
||||
waspMigrate/.wasp/out/server/src/entities/index.ts
|
||||
waspMigrate/.wasp/out/server/src/jobs/core/Job.js
|
||||
waspMigrate/.wasp/out/server/src/jobs/core/SubmittedJob.js
|
||||
waspMigrate/.wasp/out/server/src/jobs/core/allJobs.js
|
||||
@ -26,6 +27,7 @@ waspMigrate/.wasp/out/server/src/jobs/core/simpleJob.js
|
||||
waspMigrate/.wasp/out/server/src/routes/index.js
|
||||
waspMigrate/.wasp/out/server/src/routes/operations/index.js
|
||||
waspMigrate/.wasp/out/server/src/server.ts
|
||||
waspMigrate/.wasp/out/server/src/types/index.ts
|
||||
waspMigrate/.wasp/out/server/src/utils.js
|
||||
waspMigrate/.wasp/out/server/tsconfig.json
|
||||
waspMigrate/.wasp/out/web-app/.npmrc
|
||||
|
@ -90,6 +90,13 @@
|
||||
],
|
||||
"20c67ca197da3de2d37528ceaff2e40af910be8177f346c6d5c2b2f983810c43"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/entities/index.ts"
|
||||
],
|
||||
"372c0ae48ae3aeee517586bbc9e02705e7f1398ce6326c7a0876a12e9d8e74e1"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
@ -153,6 +160,13 @@
|
||||
],
|
||||
"c6114654819216004f0ef4a8771a0c1213c655cfd41760fbd835077c53a6b6ba"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"server/src/types/index.ts"
|
||||
],
|
||||
"3105fd2e1e6d8dc5481b7f4db21290571e945eb2610cd945cee86254b0e92c6c"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
|
11
waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/entities/index.ts
generated
Normal file
11
waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/entities/index.ts
generated
Normal file
@ -0,0 +1,11 @@
|
||||
import {
|
||||
type Task,
|
||||
} from "@prisma/client"
|
||||
|
||||
export {
|
||||
type Task,
|
||||
} from "@prisma/client"
|
||||
|
||||
export type WaspEntity =
|
||||
| Task
|
||||
| never
|
29
waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/types/index.ts
generated
Normal file
29
waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/server/src/types/index.ts
generated
Normal file
@ -0,0 +1,29 @@
|
||||
import prisma from "../dbClient.js"
|
||||
import {
|
||||
type WaspEntity,
|
||||
type Task,
|
||||
} from "../entities"
|
||||
|
||||
export type Query<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
export type Action<Entities extends WaspEntity[] = [], Result = unknown> = Operation<Entities, Result>
|
||||
|
||||
|
||||
type Operation<Entities extends WaspEntity[], Result> = (
|
||||
args: any,
|
||||
context: {
|
||||
entities: EntityMap<Entities>,
|
||||
},
|
||||
) => Promise<Result>
|
||||
|
||||
type PrismaDelegateFor<EntityName extends string> =
|
||||
EntityName extends "Task" ? typeof prisma.task :
|
||||
never
|
||||
|
||||
type WaspNameFor<Entity extends WaspEntity> =
|
||||
Entity extends Task ? "Task" :
|
||||
never
|
||||
|
||||
type EntityMap<Entities extends WaspEntity[]> = {
|
||||
[EntityName in WaspNameFor<Entities[number]>]: PrismaDelegateFor<EntityName>
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import HttpError from '@wasp/core/HttpError.js'
|
||||
import { Task } from '@wasp/entities/'
|
||||
import { AuthenticatedAction } from '@wasp/types'
|
||||
import { getSomeResource } from './serverSetup.js'
|
||||
|
||||
|
||||
export const createTask = async (task, context) => {
|
||||
export const createTask: AuthenticatedAction<[Task]> = async (task, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401)
|
||||
}
|
||||
@ -21,7 +22,7 @@ export const createTask = async (task, context) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const updateTaskIsDone = async ({ id, isDone }, context) => {
|
||||
export const updateTaskIsDone: AuthenticatedAction<[Task]> = async ({ id, isDone }, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401)
|
||||
}
|
||||
@ -37,7 +38,7 @@ export const updateTaskIsDone = async ({ id, isDone }, context) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteCompletedTasks = async (args, context) => {
|
||||
export const deleteCompletedTasks: AuthenticatedAction<[Task]> = async (args, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401)
|
||||
}
|
||||
@ -48,7 +49,7 @@ export const deleteCompletedTasks = async (args, context) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const toggleAllTasks = async (args, context) => {
|
||||
export const toggleAllTasks: AuthenticatedAction<[Task]> = async (args, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401)
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import HttpError from '@wasp/core/HttpError.js'
|
||||
import { Task } from '@wasp/entities'
|
||||
import { AuthenticatedQuery } from '@wasp/types'
|
||||
|
||||
export const getTasks = async (args, context) => {
|
||||
export const getTasks: AuthenticatedQuery<[Task], Task[]> = async (args, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401)
|
||||
}
|
||||
@ -17,11 +19,11 @@ export const getTasks = async (args, context) => {
|
||||
return tasks
|
||||
}
|
||||
|
||||
export const getNumTasks = async (args, context) => {
|
||||
export const getNumTasks: AuthenticatedQuery<[Task], number> = async (args, context) => {
|
||||
return context.entities.Task.count()
|
||||
}
|
||||
|
||||
export const getTask = async ({ id }, context) => {
|
||||
export const getTask: AuthenticatedQuery<[Task], Task> = async ({ id }, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401)
|
||||
}
|
@ -173,6 +173,7 @@ genSrcDir spec =
|
||||
genServerJs spec
|
||||
]
|
||||
<++> genRoutesDir spec
|
||||
<++> genTypesAndEntitiesDirs spec
|
||||
<++> genOperationsRoutes spec
|
||||
<++> genOperations spec
|
||||
<++> genAuth spec
|
||||
@ -236,6 +237,32 @@ genRoutesDir spec =
|
||||
)
|
||||
]
|
||||
|
||||
genTypesAndEntitiesDirs :: AppSpec -> Generator [FileDraft]
|
||||
genTypesAndEntitiesDirs spec = return [entitiesIndexFileDraft, typesIndexFileDraft]
|
||||
where
|
||||
entitiesIndexFileDraft =
|
||||
C.mkTmplFdWithDstAndData
|
||||
[relfile|src/entities/index.ts|]
|
||||
[relfile|src/entities/index.ts|]
|
||||
(Just $ object ["entities" .= allEntities])
|
||||
typesIndexFileDraft =
|
||||
C.mkTmplFdWithDstAndData
|
||||
[relfile|src/types/index.ts|]
|
||||
[relfile|src/types/index.ts|]
|
||||
( Just $
|
||||
object
|
||||
[ "entities" .= allEntities,
|
||||
"isAuthEnabled" .= isJust userEntityName,
|
||||
"userEntityName" .= fromMaybe "" userEntityName,
|
||||
"userViewName" .= fromMaybe "" userViewName
|
||||
]
|
||||
)
|
||||
allEntities = map (C.buildEntityData . fst) $ AS.getDecls @AS.Entity.Entity spec
|
||||
userEntityName = AS.refName . AS.App.Auth.userEntity <$> AS.App.auth (snd $ getApp spec)
|
||||
-- We might want to move this to a more global location in the future, but
|
||||
-- it is currently used only in these two files.
|
||||
userViewName = (++ "View") <$> userEntityName
|
||||
|
||||
operationsRouteInRootRouter :: String
|
||||
operationsRouteInRootRouter = "operations"
|
||||
|
||||
|
@ -12,6 +12,8 @@ module Wasp.Generator.ServerGenerator.Common
|
||||
asServerFile,
|
||||
asServerSrcFile,
|
||||
entityNameToPrismaIdentifier,
|
||||
buildEntityData,
|
||||
toESModulesImportPath,
|
||||
ServerRootDir,
|
||||
ServerSrcDir,
|
||||
ServerTemplatesDir,
|
||||
@ -19,10 +21,12 @@ module Wasp.Generator.ServerGenerator.Common
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson (object, (.=))
|
||||
import qualified Data.Aeson as Aeson
|
||||
import Data.Char (toLower)
|
||||
import StrongPath (Dir, File', Path', Rel, reldir, relfile, (</>))
|
||||
import qualified StrongPath as SP
|
||||
import System.FilePath (splitExtension)
|
||||
import Wasp.Common (WaspProjectDir)
|
||||
import Wasp.Generator.Common (ProjectRootDir)
|
||||
import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft)
|
||||
@ -99,3 +103,21 @@ dotEnvServer = [relfile|.env.server|]
|
||||
-- client SDK identifiers. Useful when creating `context.entities` JS objects in Wasp templates.
|
||||
entityNameToPrismaIdentifier :: String -> String
|
||||
entityNameToPrismaIdentifier entityName = toLower (head entityName) : tail entityName
|
||||
|
||||
buildEntityData :: String -> Aeson.Value
|
||||
buildEntityData name =
|
||||
object
|
||||
[ "name" .= name,
|
||||
"prismaIdentifier" .= entityNameToPrismaIdentifier name
|
||||
]
|
||||
|
||||
-- Converts the real name of the source file (i.e., name on disk) into a name
|
||||
-- that can be used in an ESNext import.
|
||||
-- Specifically, when using the ESNext module system, all source files must be
|
||||
-- imported with a '.js' extension (even if they are '.ts' files).
|
||||
--
|
||||
-- Details: https://github.com/wasp-lang/wasp/issues/812#issuecomment-1335579353
|
||||
toESModulesImportPath :: FilePath -> FilePath
|
||||
toESModulesImportPath = changeExtensionTo "js"
|
||||
where
|
||||
changeExtensionTo ext = (++ '.' : ext) . fst . splitExtension
|
||||
|
@ -65,7 +65,7 @@ genJob (jobName, job) =
|
||||
"jobSchedule" .= Aeson.Text.encodeToLazyText (fromMaybe Aeson.Null maybeJobSchedule),
|
||||
"jobPerformOptions" .= show (fromMaybe AS.JSON.emptyObject maybeJobPerformOptions),
|
||||
"executorJobRelFP" .= toFilePath (executorJobTemplateInJobsDir (J.executor job)),
|
||||
"entities" .= maybe [] (map (buildEntityData . AS.refName)) (J.entities job)
|
||||
"entities" .= maybe [] (map (C.buildEntityData . AS.refName)) (J.entities job)
|
||||
]
|
||||
)
|
||||
where
|
||||
@ -81,13 +81,6 @@ genJob (jobName, job) =
|
||||
]
|
||||
maybeJobSchedule = jobScheduleTmplData <$> J.schedule job
|
||||
|
||||
buildEntityData :: String -> Aeson.Value
|
||||
buildEntityData entityName =
|
||||
object
|
||||
[ "name" .= entityName,
|
||||
"prismaIdentifier" .= C.entityNameToPrismaIdentifier entityName
|
||||
]
|
||||
|
||||
-- Creates a file that is imported on the server to ensure all job JS modules are loaded
|
||||
-- even if they are not referenced by user code. This ensures schedules are started, etc.
|
||||
genAllJobImports :: AppSpec -> FileDraft
|
||||
|
@ -80,15 +80,9 @@ operationTmplData operation =
|
||||
object
|
||||
[ "jsFnImportStatement" .= importStmt,
|
||||
"jsFnIdentifier" .= importIdentifier,
|
||||
"entities" .= maybe [] (map (buildEntityData . AS.refName)) (AS.Operation.getEntities operation)
|
||||
"entities" .= maybe [] (map (C.buildEntityData . AS.refName)) (AS.Operation.getEntities operation)
|
||||
]
|
||||
where
|
||||
(importIdentifier, importStmt) =
|
||||
getJsImportDetailsForExtFnImport relPosixPathFromOperationFileToExtSrcDir $
|
||||
AS.Operation.getFn operation
|
||||
buildEntityData :: String -> Aeson.Value
|
||||
buildEntityData entityName =
|
||||
object
|
||||
[ "name" .= entityName,
|
||||
"prismaIdentifier" .= C.entityNameToPrismaIdentifier entityName
|
||||
]
|
||||
|
@ -65,9 +65,10 @@ genOperationRoute spec operation tmplFile = return $ C.mkTmplFdWithDstAndData tm
|
||||
baseTmplData
|
||||
|
||||
operationImportPath =
|
||||
SP.fromRelFileP $
|
||||
relPosixPathFromOperationsRoutesDirToSrcDir
|
||||
</> fromJust (SP.relFileToPosix $ operationFileInSrcDir operation)
|
||||
C.toESModulesImportPath $
|
||||
SP.fromRelFileP $
|
||||
relPosixPathFromOperationsRoutesDirToSrcDir
|
||||
</> fromJust (SP.relFileToPosix $ operationFileInSrcDir operation)
|
||||
|
||||
data OperationsRoutesDir
|
||||
|
||||
|
@ -6,7 +6,7 @@ cabal-version: 2.4
|
||||
-- Consider using hpack, or maybe even hpack-dhall.
|
||||
|
||||
name: waspc
|
||||
version: 0.8.0
|
||||
version: 0.8.1
|
||||
description: Please see the README on GitHub at <https://github.com/wasp-lang/wasp/waspc#readme>
|
||||
homepage: https://github.com/wasp-lang/wasp/waspc#readme
|
||||
bug-reports: https://github.com/wasp-lang/wasp/issues
|
||||
|
Loading…
Reference in New Issue
Block a user