@opensaas/stack-auth 0.1.0 → 0.1.1
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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/CLAUDE.md +196 -0
- package/LICENSE +21 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +24 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/types.d.ts +16 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/plugins/index.d.ts +7 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +7 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/schema-converter.d.ts +40 -0
- package/dist/server/schema-converter.d.ts.map +1 -0
- package/dist/server/schema-converter.js +250 -0
- package/dist/server/schema-converter.js.map +1 -0
- package/package.json +14 -4
- package/src/config/index.ts +28 -2
- package/src/config/types.ts +19 -1
- package/src/plugins/index.ts +7 -0
- package/src/server/index.ts +3 -0
- package/src/server/schema-converter.ts +299 -0
- package/tests/config.test.ts +270 -0
- package/tests/lists.test.ts +356 -0
- package/tests/schema-converter.test.ts +304 -0
- package/tsconfig.test.json +7 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +27 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
createUserList,
|
|
4
|
+
createSessionList,
|
|
5
|
+
createAccountList,
|
|
6
|
+
createVerificationList,
|
|
7
|
+
getAuthLists,
|
|
8
|
+
} from '../src/lists/index.js'
|
|
9
|
+
import { text } from '@opensaas/stack-core/fields'
|
|
10
|
+
|
|
11
|
+
describe('createUserList', () => {
|
|
12
|
+
it('should create User list with required fields', () => {
|
|
13
|
+
const userList = createUserList()
|
|
14
|
+
|
|
15
|
+
expect(userList.fields).toHaveProperty('name')
|
|
16
|
+
expect(userList.fields).toHaveProperty('email')
|
|
17
|
+
expect(userList.fields).toHaveProperty('emailVerified')
|
|
18
|
+
expect(userList.fields).toHaveProperty('image')
|
|
19
|
+
expect(userList.fields).toHaveProperty('sessions')
|
|
20
|
+
expect(userList.fields).toHaveProperty('accounts')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should have email field marked as unique', () => {
|
|
24
|
+
const userList = createUserList()
|
|
25
|
+
|
|
26
|
+
expect(userList.fields.email.isIndexed).toBe('unique')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should have name field marked as required', () => {
|
|
30
|
+
const userList = createUserList()
|
|
31
|
+
|
|
32
|
+
expect(userList.fields.name.validation?.isRequired).toBe(true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should have email field marked as required', () => {
|
|
36
|
+
const userList = createUserList()
|
|
37
|
+
|
|
38
|
+
expect(userList.fields.email.validation?.isRequired).toBe(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should have emailVerified field with default false', () => {
|
|
42
|
+
const userList = createUserList()
|
|
43
|
+
|
|
44
|
+
expect(userList.fields.emailVerified.defaultValue).toBe(false)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should extend User list with custom fields', () => {
|
|
48
|
+
const userList = createUserList({
|
|
49
|
+
fields: {
|
|
50
|
+
role: text(),
|
|
51
|
+
company: text(),
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
expect(userList.fields).toHaveProperty('role')
|
|
56
|
+
expect(userList.fields).toHaveProperty('company')
|
|
57
|
+
// Should also have base fields
|
|
58
|
+
expect(userList.fields).toHaveProperty('email')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should have default access control', () => {
|
|
62
|
+
const userList = createUserList()
|
|
63
|
+
|
|
64
|
+
expect(userList.access).toBeDefined()
|
|
65
|
+
expect(userList.access?.operation).toBeDefined()
|
|
66
|
+
expect(userList.access?.operation?.query).toBeDefined()
|
|
67
|
+
expect(userList.access?.operation?.create).toBeDefined()
|
|
68
|
+
expect(userList.access?.operation?.update).toBeDefined()
|
|
69
|
+
expect(userList.access?.operation?.delete).toBeDefined()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should allow querying users', () => {
|
|
73
|
+
const userList = createUserList()
|
|
74
|
+
const queryAccess = userList.access?.operation?.query
|
|
75
|
+
|
|
76
|
+
expect(typeof queryAccess).toBe('function')
|
|
77
|
+
expect(queryAccess?.({})).toBe(true)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should allow creating users', () => {
|
|
81
|
+
const userList = createUserList()
|
|
82
|
+
const createAccess = userList.access?.operation?.create
|
|
83
|
+
|
|
84
|
+
expect(typeof createAccess).toBe('function')
|
|
85
|
+
expect(createAccess?.({})).toBe(true)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should allow users to update their own record', () => {
|
|
89
|
+
const userList = createUserList()
|
|
90
|
+
const updateAccess = userList.access?.operation?.update
|
|
91
|
+
|
|
92
|
+
expect(typeof updateAccess).toBe('function')
|
|
93
|
+
expect(
|
|
94
|
+
updateAccess?.({
|
|
95
|
+
session: { userId: 'user-1' },
|
|
96
|
+
item: { id: 'user-1' },
|
|
97
|
+
}),
|
|
98
|
+
).toBe(true)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should deny users from updating other users', () => {
|
|
102
|
+
const userList = createUserList()
|
|
103
|
+
const updateAccess = userList.access?.operation?.update
|
|
104
|
+
|
|
105
|
+
expect(
|
|
106
|
+
updateAccess?.({
|
|
107
|
+
session: { userId: 'user-1' },
|
|
108
|
+
item: { id: 'user-2' },
|
|
109
|
+
}),
|
|
110
|
+
).toBe(false)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should allow custom access control', () => {
|
|
114
|
+
const customAccess = {
|
|
115
|
+
operation: {
|
|
116
|
+
query: () => false,
|
|
117
|
+
create: () => false,
|
|
118
|
+
update: () => false,
|
|
119
|
+
delete: () => false,
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const userList = createUserList({
|
|
124
|
+
access: customAccess,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
expect(userList.access).toEqual(customAccess)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should support custom hooks', () => {
|
|
131
|
+
const customHooks = {
|
|
132
|
+
resolveInput: async ({ resolvedData }: { resolvedData: unknown }) => resolvedData,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const userList = createUserList({
|
|
136
|
+
hooks: customHooks,
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
expect(userList.hooks).toEqual(customHooks)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe('createSessionList', () => {
|
|
144
|
+
it('should create Session list with required fields', () => {
|
|
145
|
+
const sessionList = createSessionList()
|
|
146
|
+
|
|
147
|
+
expect(sessionList.fields).toHaveProperty('token')
|
|
148
|
+
expect(sessionList.fields).toHaveProperty('expiresAt')
|
|
149
|
+
expect(sessionList.fields).toHaveProperty('ipAddress')
|
|
150
|
+
expect(sessionList.fields).toHaveProperty('userAgent')
|
|
151
|
+
expect(sessionList.fields).toHaveProperty('user')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('should have token field marked as unique', () => {
|
|
155
|
+
const sessionList = createSessionList()
|
|
156
|
+
|
|
157
|
+
expect(sessionList.fields.token.isIndexed).toBe('unique')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should have token field marked as required', () => {
|
|
161
|
+
const sessionList = createSessionList()
|
|
162
|
+
|
|
163
|
+
expect(sessionList.fields.token.validation?.isRequired).toBe(true)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('should have user relationship', () => {
|
|
167
|
+
const sessionList = createSessionList()
|
|
168
|
+
|
|
169
|
+
expect(sessionList.fields.user.type).toBe('relationship')
|
|
170
|
+
expect(sessionList.fields.user.ref).toBe('User.sessions')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should have restrictive access control', () => {
|
|
174
|
+
const sessionList = createSessionList()
|
|
175
|
+
|
|
176
|
+
expect(sessionList.access).toBeDefined()
|
|
177
|
+
expect(sessionList.access?.operation).toBeDefined()
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('should deny querying sessions without session', () => {
|
|
181
|
+
const sessionList = createSessionList()
|
|
182
|
+
const queryAccess = sessionList.access?.operation?.query
|
|
183
|
+
|
|
184
|
+
expect(queryAccess?.({})).toBe(false)
|
|
185
|
+
expect(queryAccess?.({ session: null })).toBe(false)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should allow querying own sessions with filter', () => {
|
|
189
|
+
const sessionList = createSessionList()
|
|
190
|
+
const queryAccess = sessionList.access?.operation?.query
|
|
191
|
+
|
|
192
|
+
const result = queryAccess?.({ session: { userId: 'user-1' } })
|
|
193
|
+
expect(result).toEqual({
|
|
194
|
+
user: { id: { equals: 'user-1' } },
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('should allow creating sessions', () => {
|
|
199
|
+
const sessionList = createSessionList()
|
|
200
|
+
const createAccess = sessionList.access?.operation?.create
|
|
201
|
+
|
|
202
|
+
expect(createAccess?.({})).toBe(true)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('should deny manual session updates', () => {
|
|
206
|
+
const sessionList = createSessionList()
|
|
207
|
+
const updateAccess = sessionList.access?.operation?.update
|
|
208
|
+
|
|
209
|
+
expect(updateAccess?.({})).toBe(false)
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
describe('createAccountList', () => {
|
|
214
|
+
it('should create Account list with required fields', () => {
|
|
215
|
+
const accountList = createAccountList()
|
|
216
|
+
|
|
217
|
+
expect(accountList.fields).toHaveProperty('accountId')
|
|
218
|
+
expect(accountList.fields).toHaveProperty('providerId')
|
|
219
|
+
expect(accountList.fields).toHaveProperty('user')
|
|
220
|
+
expect(accountList.fields).toHaveProperty('accessToken')
|
|
221
|
+
expect(accountList.fields).toHaveProperty('refreshToken')
|
|
222
|
+
expect(accountList.fields).toHaveProperty('password')
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('should have accountId field marked as required', () => {
|
|
226
|
+
const accountList = createAccountList()
|
|
227
|
+
|
|
228
|
+
expect(accountList.fields.accountId.validation?.isRequired).toBe(true)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('should have providerId field marked as required', () => {
|
|
232
|
+
const accountList = createAccountList()
|
|
233
|
+
|
|
234
|
+
expect(accountList.fields.providerId.validation?.isRequired).toBe(true)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('should have user relationship', () => {
|
|
238
|
+
const accountList = createAccountList()
|
|
239
|
+
|
|
240
|
+
expect(accountList.fields.user.type).toBe('relationship')
|
|
241
|
+
expect(accountList.fields.user.ref).toBe('User.accounts')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('should deny querying accounts without session', () => {
|
|
245
|
+
const accountList = createAccountList()
|
|
246
|
+
const queryAccess = accountList.access?.operation?.query
|
|
247
|
+
|
|
248
|
+
expect(queryAccess?.({})).toBe(false)
|
|
249
|
+
expect(queryAccess?.({ session: null })).toBe(false)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('should allow querying own accounts with filter', () => {
|
|
253
|
+
const accountList = createAccountList()
|
|
254
|
+
const queryAccess = accountList.access?.operation?.query
|
|
255
|
+
|
|
256
|
+
const result = queryAccess?.({ session: { userId: 'user-1' } })
|
|
257
|
+
expect(result).toEqual({
|
|
258
|
+
user: { id: { equals: 'user-1' } },
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('should allow users to update their own accounts', () => {
|
|
263
|
+
const accountList = createAccountList()
|
|
264
|
+
const updateAccess = accountList.access?.operation?.update
|
|
265
|
+
|
|
266
|
+
expect(
|
|
267
|
+
updateAccess?.({
|
|
268
|
+
session: { userId: 'user-1' },
|
|
269
|
+
item: { user: { id: 'user-1' } },
|
|
270
|
+
}),
|
|
271
|
+
).toBe(true)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should deny users from updating other accounts', () => {
|
|
275
|
+
const accountList = createAccountList()
|
|
276
|
+
const updateAccess = accountList.access?.operation?.update
|
|
277
|
+
|
|
278
|
+
expect(
|
|
279
|
+
updateAccess?.({
|
|
280
|
+
session: { userId: 'user-1' },
|
|
281
|
+
item: { user: { id: 'user-2' } },
|
|
282
|
+
}),
|
|
283
|
+
).toBe(false)
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
describe('createVerificationList', () => {
|
|
288
|
+
it('should create Verification list with required fields', () => {
|
|
289
|
+
const verificationList = createVerificationList()
|
|
290
|
+
|
|
291
|
+
expect(verificationList.fields).toHaveProperty('identifier')
|
|
292
|
+
expect(verificationList.fields).toHaveProperty('value')
|
|
293
|
+
expect(verificationList.fields).toHaveProperty('expiresAt')
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('should have identifier field marked as required', () => {
|
|
297
|
+
const verificationList = createVerificationList()
|
|
298
|
+
|
|
299
|
+
expect(verificationList.fields.identifier.validation?.isRequired).toBe(true)
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('should have value field marked as required', () => {
|
|
303
|
+
const verificationList = createVerificationList()
|
|
304
|
+
|
|
305
|
+
expect(verificationList.fields.value.validation?.isRequired).toBe(true)
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('should deny querying verification tokens', () => {
|
|
309
|
+
const verificationList = createVerificationList()
|
|
310
|
+
const queryAccess = verificationList.access?.operation?.query
|
|
311
|
+
|
|
312
|
+
expect(queryAccess?.({})).toBe(false)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('should allow creating verification tokens', () => {
|
|
316
|
+
const verificationList = createVerificationList()
|
|
317
|
+
const createAccess = verificationList.access?.operation?.create
|
|
318
|
+
|
|
319
|
+
expect(createAccess?.({})).toBe(true)
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('should deny updates to verification tokens', () => {
|
|
323
|
+
const verificationList = createVerificationList()
|
|
324
|
+
const updateAccess = verificationList.access?.operation?.update
|
|
325
|
+
|
|
326
|
+
expect(updateAccess?.({})).toBe(false)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('should allow deleting verification tokens', () => {
|
|
330
|
+
const verificationList = createVerificationList()
|
|
331
|
+
const deleteAccess = verificationList.access?.operation?.delete
|
|
332
|
+
|
|
333
|
+
expect(deleteAccess?.({})).toBe(true)
|
|
334
|
+
})
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
describe('getAuthLists', () => {
|
|
338
|
+
it('should return all auth lists', () => {
|
|
339
|
+
const lists = getAuthLists()
|
|
340
|
+
|
|
341
|
+
expect(lists).toHaveProperty('User')
|
|
342
|
+
expect(lists).toHaveProperty('Session')
|
|
343
|
+
expect(lists).toHaveProperty('Account')
|
|
344
|
+
expect(lists).toHaveProperty('Verification')
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
it('should pass user config to createUserList', () => {
|
|
348
|
+
const lists = getAuthLists({
|
|
349
|
+
fields: {
|
|
350
|
+
role: text(),
|
|
351
|
+
},
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
expect(lists.User.fields).toHaveProperty('role')
|
|
355
|
+
})
|
|
356
|
+
})
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { convertTableToList, convertBetterAuthSchema } from '../src/server/schema-converter.js'
|
|
3
|
+
|
|
4
|
+
describe('convertTableToList', () => {
|
|
5
|
+
it('should convert string fields', () => {
|
|
6
|
+
const tableSchema = {
|
|
7
|
+
modelName: 'TestTable',
|
|
8
|
+
fields: {
|
|
9
|
+
name: { type: 'string', required: true },
|
|
10
|
+
bio: { type: 'string' },
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
15
|
+
|
|
16
|
+
expect(listConfig.fields).toHaveProperty('name')
|
|
17
|
+
expect(listConfig.fields).toHaveProperty('bio')
|
|
18
|
+
expect(listConfig.fields.name.type).toBe('text')
|
|
19
|
+
expect(listConfig.fields.name.validation?.isRequired).toBe(true)
|
|
20
|
+
expect(listConfig.fields.bio.validation?.isRequired).toBeUndefined()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should convert number fields', () => {
|
|
24
|
+
const tableSchema = {
|
|
25
|
+
modelName: 'TestTable',
|
|
26
|
+
fields: {
|
|
27
|
+
age: { type: 'number', required: true },
|
|
28
|
+
score: { type: 'number', defaultValue: 0 },
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
33
|
+
|
|
34
|
+
expect(listConfig.fields.age.type).toBe('integer')
|
|
35
|
+
expect(listConfig.fields.age.validation?.isRequired).toBe(true)
|
|
36
|
+
expect(listConfig.fields.score.defaultValue).toBe(0)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should convert boolean fields', () => {
|
|
40
|
+
const tableSchema = {
|
|
41
|
+
modelName: 'TestTable',
|
|
42
|
+
fields: {
|
|
43
|
+
active: { type: 'boolean', defaultValue: true },
|
|
44
|
+
verified: { type: 'boolean' },
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
49
|
+
|
|
50
|
+
expect(listConfig.fields.active.type).toBe('checkbox')
|
|
51
|
+
expect(listConfig.fields.active.defaultValue).toBe(true)
|
|
52
|
+
expect(listConfig.fields.verified.type).toBe('checkbox')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should convert date fields', () => {
|
|
56
|
+
const tableSchema = {
|
|
57
|
+
modelName: 'TestTable',
|
|
58
|
+
fields: {
|
|
59
|
+
expiresAt: { type: 'date' },
|
|
60
|
+
createdOn: { type: 'date', defaultValue: 'now' },
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
65
|
+
|
|
66
|
+
expect(listConfig.fields.expiresAt.type).toBe('timestamp')
|
|
67
|
+
expect(listConfig.fields.createdOn.type).toBe('timestamp')
|
|
68
|
+
expect(listConfig.fields.createdOn.defaultValue).toEqual({ kind: 'now' })
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should handle unique fields', () => {
|
|
72
|
+
const tableSchema = {
|
|
73
|
+
modelName: 'TestTable',
|
|
74
|
+
fields: {
|
|
75
|
+
email: { type: 'string', unique: true },
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
80
|
+
|
|
81
|
+
expect(listConfig.fields.email.isIndexed).toBe('unique')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('should skip system fields', () => {
|
|
85
|
+
const tableSchema = {
|
|
86
|
+
modelName: 'TestTable',
|
|
87
|
+
fields: {
|
|
88
|
+
id: { type: 'string', required: true },
|
|
89
|
+
name: { type: 'string' },
|
|
90
|
+
createdAt: { type: 'date' },
|
|
91
|
+
updatedAt: { type: 'date' },
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
96
|
+
|
|
97
|
+
// System fields should be skipped
|
|
98
|
+
expect(listConfig.fields).not.toHaveProperty('id')
|
|
99
|
+
expect(listConfig.fields).not.toHaveProperty('createdAt')
|
|
100
|
+
expect(listConfig.fields).not.toHaveProperty('updatedAt')
|
|
101
|
+
// Regular fields should be included
|
|
102
|
+
expect(listConfig.fields).toHaveProperty('name')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should handle reference fields as text', () => {
|
|
106
|
+
const tableSchema = {
|
|
107
|
+
modelName: 'TestTable',
|
|
108
|
+
fields: {
|
|
109
|
+
userId: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
required: true,
|
|
112
|
+
references: {
|
|
113
|
+
model: 'User',
|
|
114
|
+
field: 'id',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
121
|
+
|
|
122
|
+
expect(listConfig.fields).toHaveProperty('userId')
|
|
123
|
+
expect(listConfig.fields.userId.type).toBe('text')
|
|
124
|
+
expect(listConfig.fields.userId.validation?.isRequired).toBe(true)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('should handle unknown field types', () => {
|
|
128
|
+
const tableSchema = {
|
|
129
|
+
modelName: 'TestTable',
|
|
130
|
+
fields: {
|
|
131
|
+
customField: { type: 'unknown-type' },
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const listConfig = convertTableToList('test_table', tableSchema)
|
|
136
|
+
|
|
137
|
+
// Should default to text
|
|
138
|
+
expect(listConfig.fields.customField.type).toBe('text')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should apply default access control for User table', () => {
|
|
142
|
+
const tableSchema = {
|
|
143
|
+
modelName: 'User',
|
|
144
|
+
fields: {
|
|
145
|
+
name: { type: 'string' },
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const listConfig = convertTableToList('user', tableSchema)
|
|
150
|
+
|
|
151
|
+
expect(listConfig.access).toBeDefined()
|
|
152
|
+
expect(listConfig.access?.operation?.query?.({})).toBe(true)
|
|
153
|
+
expect(listConfig.access?.operation?.create?.({})).toBe(true)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('should apply default access control for Session table', () => {
|
|
157
|
+
const tableSchema = {
|
|
158
|
+
modelName: 'Session',
|
|
159
|
+
fields: {
|
|
160
|
+
token: { type: 'string' },
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const listConfig = convertTableToList('session', tableSchema)
|
|
165
|
+
|
|
166
|
+
expect(listConfig.access?.operation?.query?.({ session: null })).toBe(false)
|
|
167
|
+
expect(listConfig.access?.operation?.create?.({})).toBe(true)
|
|
168
|
+
expect(listConfig.access?.operation?.update?.({})).toBe(false)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('should apply default access control for Verification table', () => {
|
|
172
|
+
const tableSchema = {
|
|
173
|
+
modelName: 'Verification',
|
|
174
|
+
fields: {
|
|
175
|
+
value: { type: 'string' },
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const listConfig = convertTableToList('verification', tableSchema)
|
|
180
|
+
|
|
181
|
+
expect(listConfig.access?.operation?.query?.({})).toBe(false)
|
|
182
|
+
expect(listConfig.access?.operation?.create?.({})).toBe(true)
|
|
183
|
+
expect(listConfig.access?.operation?.update?.({})).toBe(false)
|
|
184
|
+
expect(listConfig.access?.operation?.delete?.({})).toBe(true)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should apply restrictive default access for unknown tables', () => {
|
|
188
|
+
const tableSchema = {
|
|
189
|
+
modelName: 'UnknownTable',
|
|
190
|
+
fields: {
|
|
191
|
+
data: { type: 'string' },
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const listConfig = convertTableToList('unknown_table', tableSchema)
|
|
196
|
+
|
|
197
|
+
expect(listConfig.access?.operation?.query?.({})).toBe(false)
|
|
198
|
+
expect(listConfig.access?.operation?.create?.({})).toBe(false)
|
|
199
|
+
expect(listConfig.access?.operation?.update?.({})).toBe(false)
|
|
200
|
+
expect(listConfig.access?.operation?.delete?.({})).toBe(false)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('should handle OAuth application table', () => {
|
|
204
|
+
const tableSchema = {
|
|
205
|
+
modelName: 'OAuthApplication',
|
|
206
|
+
fields: {
|
|
207
|
+
clientId: { type: 'string', required: true },
|
|
208
|
+
userId: { type: 'string', required: true },
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const listConfig = convertTableToList('oauthapplication', tableSchema)
|
|
213
|
+
|
|
214
|
+
expect(listConfig.access?.operation?.query?.({ session: null })).toBe(false)
|
|
215
|
+
expect(listConfig.access?.operation?.create?.({})).toBe(true)
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
describe('convertBetterAuthSchema', () => {
|
|
220
|
+
it('should convert multiple tables', () => {
|
|
221
|
+
const schema = {
|
|
222
|
+
user: {
|
|
223
|
+
modelName: 'User',
|
|
224
|
+
fields: {
|
|
225
|
+
name: { type: 'string' },
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
session: {
|
|
229
|
+
modelName: 'Session',
|
|
230
|
+
fields: {
|
|
231
|
+
token: { type: 'string' },
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const lists = convertBetterAuthSchema(schema)
|
|
237
|
+
|
|
238
|
+
expect(lists).toHaveProperty('User')
|
|
239
|
+
expect(lists).toHaveProperty('Session')
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('should use modelName for list key', () => {
|
|
243
|
+
const schema = {
|
|
244
|
+
custom_table: {
|
|
245
|
+
modelName: 'CustomTable',
|
|
246
|
+
fields: {
|
|
247
|
+
data: { type: 'string' },
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const lists = convertBetterAuthSchema(schema)
|
|
253
|
+
|
|
254
|
+
expect(lists).toHaveProperty('CustomTable')
|
|
255
|
+
expect(lists).not.toHaveProperty('custom_table')
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('should convert snake_case to PascalCase if no modelName', () => {
|
|
259
|
+
const schema = {
|
|
260
|
+
oauth_application: {
|
|
261
|
+
modelName: '',
|
|
262
|
+
fields: {
|
|
263
|
+
data: { type: 'string' },
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const lists = convertBetterAuthSchema(schema)
|
|
269
|
+
|
|
270
|
+
expect(lists).toHaveProperty('OauthApplication')
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('should handle empty schema', () => {
|
|
274
|
+
const lists = convertBetterAuthSchema({})
|
|
275
|
+
|
|
276
|
+
expect(lists).toEqual({})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('should convert complex schema with multiple field types', () => {
|
|
280
|
+
const schema = {
|
|
281
|
+
test_table: {
|
|
282
|
+
modelName: 'TestTable',
|
|
283
|
+
fields: {
|
|
284
|
+
name: { type: 'string', required: true },
|
|
285
|
+
age: { type: 'number', defaultValue: 0 },
|
|
286
|
+
active: { type: 'boolean', defaultValue: true },
|
|
287
|
+
expiresAt: { type: 'date', defaultValue: 'now' },
|
|
288
|
+
userId: {
|
|
289
|
+
type: 'string',
|
|
290
|
+
references: { model: 'User', field: 'id' },
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const lists = convertBetterAuthSchema(schema)
|
|
297
|
+
|
|
298
|
+
expect(lists.TestTable.fields).toHaveProperty('name')
|
|
299
|
+
expect(lists.TestTable.fields).toHaveProperty('age')
|
|
300
|
+
expect(lists.TestTable.fields).toHaveProperty('active')
|
|
301
|
+
expect(lists.TestTable.fields).toHaveProperty('expiresAt')
|
|
302
|
+
expect(lists.TestTable.fields).toHaveProperty('userId')
|
|
303
|
+
})
|
|
304
|
+
})
|