@smartive/graphql-magic 15.4.0 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.gqmrc.json +4 -2
  2. package/CHANGELOG.md +1 -6
  3. package/dist/bin/gqm.cjs +115 -42
  4. package/dist/cjs/index.cjs +111 -30
  5. package/dist/esm/api/execute.d.ts +1 -1
  6. package/dist/esm/context.d.ts +5 -4
  7. package/dist/esm/db/generate.d.ts +2 -1
  8. package/dist/esm/db/generate.js +13 -8
  9. package/dist/esm/db/generate.js.map +1 -1
  10. package/dist/esm/index.d.ts +1 -0
  11. package/dist/esm/index.js +1 -0
  12. package/dist/esm/index.js.map +1 -1
  13. package/dist/esm/migrations/generate.d.ts +1 -0
  14. package/dist/esm/migrations/generate.js +28 -6
  15. package/dist/esm/migrations/generate.js.map +1 -1
  16. package/dist/esm/models/mutation-hook.d.ts +5 -12
  17. package/dist/esm/models/utils.d.ts +20 -7
  18. package/dist/esm/models/utils.js +8 -0
  19. package/dist/esm/models/utils.js.map +1 -1
  20. package/dist/esm/permissions/check.d.ts +2 -3
  21. package/dist/esm/permissions/check.js.map +1 -1
  22. package/dist/esm/resolvers/arguments.d.ts +1 -1
  23. package/dist/esm/resolvers/mutations.js +5 -2
  24. package/dist/esm/resolvers/mutations.js.map +1 -1
  25. package/dist/esm/schema/utils.js +19 -8
  26. package/dist/esm/schema/utils.js.map +1 -1
  27. package/dist/esm/utils/dates.d.ts +12 -0
  28. package/dist/esm/utils/dates.js +37 -0
  29. package/dist/esm/utils/dates.js.map +1 -0
  30. package/dist/esm/utils/index.d.ts +1 -0
  31. package/dist/esm/utils/index.js +3 -0
  32. package/dist/esm/utils/index.js.map +1 -0
  33. package/dist/esm/values.d.ts +1 -3
  34. package/docker-compose.yml +0 -1
  35. package/docs/docs/1-tutorial.md +6 -6
  36. package/docs/docs/6-graphql-server.md +1 -3
  37. package/docs/docs/7-graphql-client.md +1 -1
  38. package/docs/docs/8-permissions.md +145 -0
  39. package/docs/package-lock.json +177 -177
  40. package/docs/package.json +6 -6
  41. package/knexfile.ts +2 -2
  42. package/migrations/20230912185644_setup.ts +37 -8
  43. package/package.json +5 -4
  44. package/src/bin/gqm/codegen.ts +4 -3
  45. package/src/bin/gqm/gqm.ts +4 -2
  46. package/src/bin/gqm/settings.ts +37 -2
  47. package/src/bin/gqm/templates.ts +19 -8
  48. package/src/context.ts +9 -5
  49. package/src/db/generate.ts +15 -8
  50. package/src/index.ts +1 -0
  51. package/src/migrations/generate.ts +34 -16
  52. package/src/models/mutation-hook.ts +5 -8
  53. package/src/models/utils.ts +24 -0
  54. package/src/permissions/check.ts +2 -3
  55. package/src/resolvers/mutations.ts +10 -6
  56. package/src/schema/utils.ts +14 -2
  57. package/src/utils/dates.ts +48 -0
  58. package/src/utils/index.ts +3 -0
  59. package/src/values.ts +1 -5
  60. package/tests/generated/client/index.ts +3 -1
  61. package/tests/generated/db/index.ts +43 -43
  62. package/tests/utils/database/seed.ts +9 -5
  63. package/tests/utils/server.ts +3 -3
@@ -95,10 +95,10 @@ const nextConfig = {
95
95
  };
96
96
  ```
97
97
 
98
- Install `@smartive/graphql-magic`:
98
+ Install `@smartive/graphql-magic` and needed dependencies:
99
99
 
100
100
  ```bash
101
- npm install @smartive/graphql-magic
101
+ npm install @smartive/graphql-magic @graphql-codegen/typescript-compatibility
102
102
  ```
103
103
 
104
104
  Run the gqm cli:
@@ -123,7 +123,6 @@ services:
123
123
  POSTGRES_USER: postgres
124
124
  POSTGRES_PASSWORD: password
125
125
  POSTGRES_HOST_AUTH_METHOD: trust
126
- TZ: 'Europe/Zurich'
127
126
  ports:
128
127
  - '5432:5432'
129
128
  ```
@@ -213,14 +212,14 @@ Now let's implement the `// TODO: get user` part in the `src/graphql/execute.ts`
213
212
  ```ts
214
213
  const session = await getSession();
215
214
  if (session) {
216
- let dbUser = await db('User').where({ authId: session.user.sid }).first();
215
+ let dbUser = await db('User').where({ authId: session.user.sub }).first();
217
216
  if (!user) {
218
217
  await db('User').insert({
219
218
  id: randomUUID(),
220
- authId: session.user.sid,
219
+ authId: session.user.sub,
221
220
  username: session.user.nickname
222
221
  })
223
- dbUser = await db('User').where({ authId: session.user.sid }).first();
222
+ dbUser = await db('User').where({ authId: session.user.sub }).first();
224
223
  }
225
224
  user = {
226
225
  ...dbUser!,
@@ -322,6 +321,7 @@ Let's make a blog out of this app by adding new models in `src/config/models.ts`
322
321
  updatable: true,
323
322
  }
324
323
  ]
324
+ }
325
325
  ```
326
326
 
327
327
  Generate and run the new migrations and generate the new models:
@@ -1,6 +1,6 @@
1
1
  # Graphql server
2
2
 
3
- ## `executeQuery`
3
+ ## `executeGraphql`
4
4
 
5
5
  `graphql-magic` generates an `execute.ts` file for you, with this structure:
6
6
 
@@ -9,7 +9,6 @@ import knexConfig from "@/knexfile";
9
9
  import { Context, User, execute } from "@smartive/graphql-magic";
10
10
  import { randomUUID } from "crypto";
11
11
  import { knex } from 'knex';
12
- import { DateTime } from "luxon";
13
12
  import { models } from "../config/models";
14
13
 
15
14
  export const executeGraphql = async <T, V = undefined>(
@@ -32,7 +31,6 @@ export const executeGraphql = async <T, V = undefined>(
32
31
  user,
33
32
  models: models,
34
33
  permissions: { ADMIN: true, UNAUTHENTICATED: true },
35
- now: DateTime.local(),
36
34
  });
37
35
  await db.destroy();
38
36
 
@@ -18,7 +18,7 @@ module.exports = {
18
18
 
19
19
  ### Server side
20
20
 
21
- On the server side, and with `next.js` server actions, a graphql api becomes unnecessary, and you can execute query directly using `executeQuery`:
21
+ On the server side, and with `next.js` server actions, a graphql api becomes unnecessary, and you can execute queries directly using `executeGraphql`:
22
22
 
23
23
  ```tsx
24
24
  import { GetMeQuery, GetPostsQuery } from "@/generated/client";
@@ -0,0 +1,145 @@
1
+ # Permissions
2
+
3
+ Permissions are an object provided to the `execute` function.
4
+ The root keys of the objects are the user roles (including the special role `UNAUTHENTICATED` for when the user object is undefined).
5
+
6
+ ```ts
7
+ execute({
8
+ ...
9
+ user: {
10
+ // ...
11
+ role: 'USER' // this is the role that will apply
12
+ },
13
+ permissions: {
14
+ ADMIN: ... // admin permissions
15
+ USER: ... // user permissions
16
+ UNAUTHENTICATED: ... // permissions for unauthenticated users
17
+ }
18
+ })
19
+ ```
20
+
21
+ ## Grant all permissions
22
+
23
+ ```ts
24
+ ADMIN: true
25
+ ```
26
+
27
+ ## Actions
28
+
29
+ - READ
30
+ - CREATE
31
+ - UPDATE
32
+ - DELETE
33
+ - RESTORE
34
+ - LINK
35
+
36
+ Grant all READ permissions on a specific table:
37
+
38
+ ```ts
39
+ User: {} // same as User: { READ: true }
40
+ ```
41
+
42
+ Grant actions other than READ on a specific table:
43
+
44
+ ```ts
45
+ User: { CREATE: true }
46
+ ```
47
+
48
+ ## Linking
49
+
50
+ The LINK permission doesn't give one permission to modify these records,
51
+ but to use them as options for foreign keys in _other_ records that one does have the permission to CREATE/UPDATE.
52
+
53
+ So, for example, if you want a manager to be able to assign a user to a task, you would model it like this:
54
+
55
+ ```ts
56
+ MANAGER: {
57
+ User: { LINK: true }
58
+ Task: { UPDATE: true }
59
+ }
60
+ ```
61
+
62
+ ## Narrowing the record set
63
+
64
+ Use WHERE, which accepts simple table column/value pairs that are then used as sql where filter.
65
+
66
+ ```ts
67
+ GUEST: {
68
+ Post: {
69
+ WHERE: { published: true }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## Derivative permissions
75
+
76
+ In the following way you can define permissions that follow the relational structure.
77
+
78
+ "If I can read a board (because it is public), then I can follow all threads and their authors, and their replies, which I can like."
79
+
80
+ ```ts
81
+ GUEST: {
82
+ Board: {
83
+ WHERE: { public: true }
84
+ RELATIONS: {
85
+ threads: {
86
+ RELATIONS: {
87
+ LINK: true,
88
+ author: {},
89
+ replies: {
90
+ CREATE: true,
91
+ LINK: true,
92
+ likes: {
93
+ CREATE: true
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ ## Me
104
+
105
+ You can use `me` as a special `User` record set containing just yourself.
106
+
107
+ ```ts
108
+ EMPLOYEE: {
109
+ me: {
110
+ UPDATE: true,
111
+ LINK: true,
112
+ RELATIONS: {
113
+ tasks: {
114
+ UPDATE: true
115
+ }
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ Note: for ownership patterns (I own what I create, I can update what I own),
122
+ one must use implicitly generated relationships such as `createdPosts`, `updatedPosts`, `deletedPosts`:
123
+
124
+ ```ts
125
+ GUEST: {
126
+ me: {
127
+ LINK: true,
128
+ RELATIONS: {
129
+ createdPosts: {
130
+ // "guests can create a post with the field createdBy === me"
131
+ CREATE: true,
132
+ UPDATE: true
133
+ },
134
+ updatedPosts: {
135
+ // this is necessary or it won't be possible to create a post
136
+ // "guests can create a post with the field updatedBy === me"
137
+ CREATE: true
138
+
139
+ // this is *not* necessary because the user can already update posts they created
140
+ // UPDATE: true
141
+ }
142
+ }
143
+ }
144
+ }
145
+ ```