@coursebuilder/adapter-drizzle 0.1.4 → 0.3.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 (82) hide show
  1. package/dist/{chunk-3RJVKNXB.js → chunk-4PJWD6YK.js} +421 -392
  2. package/dist/{chunk-WQ7UXKZC.js → chunk-J63FMGT7.js} +538 -200
  3. package/dist/{chunk-2CI3IGR2.js → chunk-MCARHGE5.js} +2 -2
  4. package/dist/{chunk-Q5WENQT6.js → chunk-PIQXK2CY.js} +2 -2
  5. package/dist/{chunk-5HT43CVZ.js → chunk-QMDE6HUA.js} +43 -3
  6. package/dist/{chunk-53266VWH.js → chunk-QY4TPO7D.js} +2 -2
  7. package/dist/chunk-ZD3R6UPJ.js +96 -0
  8. package/dist/index.js +11 -11
  9. package/dist/lib/mysql/index.cjs +1110 -671
  10. package/dist/lib/mysql/index.cjs.map +1 -1
  11. package/dist/lib/mysql/index.d.cts +26 -0
  12. package/dist/lib/mysql/index.d.ts +26 -0
  13. package/dist/lib/mysql/index.js +11 -11
  14. package/dist/lib/mysql/schemas/auth/accounts.cjs +83 -72
  15. package/dist/lib/mysql/schemas/auth/accounts.cjs.map +1 -1
  16. package/dist/lib/mysql/schemas/auth/accounts.js +6 -3
  17. package/dist/lib/mysql/schemas/auth/role-permissions.cjs +73 -62
  18. package/dist/lib/mysql/schemas/auth/role-permissions.cjs.map +1 -1
  19. package/dist/lib/mysql/schemas/auth/role-permissions.js +7 -4
  20. package/dist/lib/mysql/schemas/auth/roles.cjs +73 -62
  21. package/dist/lib/mysql/schemas/auth/roles.cjs.map +1 -1
  22. package/dist/lib/mysql/schemas/auth/roles.js +6 -3
  23. package/dist/lib/mysql/schemas/auth/sessions.cjs +73 -62
  24. package/dist/lib/mysql/schemas/auth/sessions.cjs.map +1 -1
  25. package/dist/lib/mysql/schemas/auth/sessions.js +7 -4
  26. package/dist/lib/mysql/schemas/auth/user-permissions.cjs +74 -63
  27. package/dist/lib/mysql/schemas/auth/user-permissions.cjs.map +1 -1
  28. package/dist/lib/mysql/schemas/auth/user-permissions.js +6 -3
  29. package/dist/lib/mysql/schemas/auth/user-roles.cjs +74 -63
  30. package/dist/lib/mysql/schemas/auth/user-roles.cjs.map +1 -1
  31. package/dist/lib/mysql/schemas/auth/user-roles.js +6 -3
  32. package/dist/lib/mysql/schemas/auth/users.cjs +158 -147
  33. package/dist/lib/mysql/schemas/auth/users.cjs.map +1 -1
  34. package/dist/lib/mysql/schemas/auth/users.js +6 -3
  35. package/dist/lib/mysql/schemas/commerce/coupon.cjs +209 -103
  36. package/dist/lib/mysql/schemas/commerce/coupon.cjs.map +1 -1
  37. package/dist/lib/mysql/schemas/commerce/coupon.d.cts +3 -0
  38. package/dist/lib/mysql/schemas/commerce/coupon.d.ts +3 -0
  39. package/dist/lib/mysql/schemas/commerce/coupon.js +6 -3
  40. package/dist/lib/mysql/schemas/commerce/merchant-charge.cjs +154 -10
  41. package/dist/lib/mysql/schemas/commerce/merchant-charge.cjs.map +1 -1
  42. package/dist/lib/mysql/schemas/commerce/merchant-charge.d.cts +7 -1
  43. package/dist/lib/mysql/schemas/commerce/merchant-charge.d.ts +7 -1
  44. package/dist/lib/mysql/schemas/commerce/merchant-charge.js +6 -1
  45. package/dist/lib/mysql/schemas/commerce/price.cjs +68 -57
  46. package/dist/lib/mysql/schemas/commerce/price.cjs.map +1 -1
  47. package/dist/lib/mysql/schemas/commerce/price.js +6 -3
  48. package/dist/lib/mysql/schemas/commerce/product.cjs +115 -104
  49. package/dist/lib/mysql/schemas/commerce/product.cjs.map +1 -1
  50. package/dist/lib/mysql/schemas/commerce/product.js +6 -3
  51. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs +251 -13
  52. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.cjs.map +1 -1
  53. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.d.cts +7 -1
  54. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.d.ts +7 -1
  55. package/dist/lib/mysql/schemas/commerce/purchase-user-transfer.js +15 -1
  56. package/dist/lib/mysql/schemas/commerce/purchase.cjs +71 -56
  57. package/dist/lib/mysql/schemas/commerce/purchase.cjs.map +1 -1
  58. package/dist/lib/mysql/schemas/commerce/purchase.js +6 -3
  59. package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs +75 -64
  60. package/dist/lib/mysql/schemas/commerce/upgradable-products.cjs.map +1 -1
  61. package/dist/lib/mysql/schemas/commerce/upgradable-products.js +7 -4
  62. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs +96 -85
  63. package/dist/lib/mysql/schemas/communication/communication-preferences.cjs.map +1 -1
  64. package/dist/lib/mysql/schemas/communication/communication-preferences.js +6 -3
  65. package/dist/lib/mysql/schemas/content/content-contributions.cjs +100 -89
  66. package/dist/lib/mysql/schemas/content/content-contributions.cjs.map +1 -1
  67. package/dist/lib/mysql/schemas/content/content-contributions.js +6 -3
  68. package/dist/lib/mysql/schemas/content/content-resource-product.cjs +66 -55
  69. package/dist/lib/mysql/schemas/content/content-resource-product.cjs.map +1 -1
  70. package/dist/lib/mysql/schemas/content/content-resource-product.js +6 -3
  71. package/dist/lib/mysql/schemas/content/content-resource-resource.cjs +79 -68
  72. package/dist/lib/mysql/schemas/content/content-resource-resource.cjs.map +1 -1
  73. package/dist/lib/mysql/schemas/content/content-resource-resource.js +6 -3
  74. package/dist/lib/mysql/schemas/content/content-resource.cjs +103 -92
  75. package/dist/lib/mysql/schemas/content/content-resource.cjs.map +1 -1
  76. package/dist/lib/mysql/schemas/content/content-resource.js +6 -3
  77. package/package.json +2 -2
  78. package/src/lib/mysql/index.ts +456 -95
  79. package/src/lib/mysql/schemas/commerce/coupon.ts +18 -1
  80. package/src/lib/mysql/schemas/commerce/merchant-charge.ts +29 -1
  81. package/src/lib/mysql/schemas/commerce/purchase-user-transfer.ts +29 -1
  82. package/dist/chunk-PJHGIRZM.js +0 -50
@@ -3,7 +3,9 @@ import { addSeconds, isAfter } from 'date-fns'
3
3
  import {
4
4
  and,
5
5
  asc,
6
+ desc,
6
7
  eq,
8
+ gte,
7
9
  inArray,
8
10
  isNotNull,
9
11
  isNull,
@@ -38,6 +40,7 @@ import {
38
40
  Purchase,
39
41
  purchaseSchema,
40
42
  PurchaseUserTransfer,
43
+ purchaseUserTransferSchema,
41
44
  PurchaseUserTransferState,
42
45
  ResourceProgress,
43
46
  resourceProgressSchema,
@@ -52,7 +55,9 @@ import {
52
55
  } from '@coursebuilder/core/schemas/content-resource-schema'
53
56
  import { merchantAccountSchema } from '@coursebuilder/core/schemas/merchant-account-schema'
54
57
  import { merchantCustomerSchema } from '@coursebuilder/core/schemas/merchant-customer-schema'
58
+ import { type ModuleProgress } from '@coursebuilder/core/schemas/resource-progress-schema'
55
59
  import { VideoResourceSchema } from '@coursebuilder/core/schemas/video-resource'
60
+ import { logger } from '@coursebuilder/core/utils/logger'
56
61
  import { validateCoupon } from '@coursebuilder/core/utils/validate-coupon'
57
62
 
58
63
  import {
@@ -93,7 +98,10 @@ import {
93
98
  getCouponSchema,
94
99
  } from './schemas/commerce/coupon.js'
95
100
  import { getMerchantAccountSchema } from './schemas/commerce/merchant-account.js'
96
- import { getMerchantChargeSchema } from './schemas/commerce/merchant-charge.js'
101
+ import {
102
+ getMerchantChargeRelationsSchema,
103
+ getMerchantChargeSchema,
104
+ } from './schemas/commerce/merchant-charge.js'
97
105
  import { getMerchantCouponSchema } from './schemas/commerce/merchant-coupon.js'
98
106
  import { getMerchantCustomerSchema } from './schemas/commerce/merchant-customer.js'
99
107
  import { getMerchantPriceSchema } from './schemas/commerce/merchant-price.js'
@@ -104,7 +112,10 @@ import {
104
112
  getProductRelationsSchema,
105
113
  getProductSchema,
106
114
  } from './schemas/commerce/product.js'
107
- import { getPurchaseUserTransferSchema } from './schemas/commerce/purchase-user-transfer.js'
115
+ import {
116
+ getPurchaseUserTransferRelationsSchema,
117
+ getPurchaseUserTransferSchema,
118
+ } from './schemas/commerce/purchase-user-transfer.js'
108
119
  import {
109
120
  getPurchaseRelationsSchema,
110
121
  getPurchaseSchema,
@@ -164,6 +175,7 @@ export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
164
175
  couponRelations: getCouponRelationsSchema(mysqlTable),
165
176
  merchantAccount: getMerchantAccountSchema(mysqlTable),
166
177
  merchantCharge: getMerchantChargeSchema(mysqlTable),
178
+ merchantChargeRelations: getMerchantChargeRelationsSchema(mysqlTable),
167
179
  merchantCoupon: getMerchantCouponSchema(mysqlTable),
168
180
  merchantCustomer: getMerchantCustomerSchema(mysqlTable),
169
181
  merchantPrice: getMerchantPriceSchema(mysqlTable),
@@ -174,6 +186,8 @@ export function getCourseBuilderSchema(mysqlTable: MySqlTableFn) {
174
186
  purchases: getPurchaseSchema(mysqlTable),
175
187
  purchaseRelations: getPurchaseRelationsSchema(mysqlTable),
176
188
  purchaseUserTransfer: getPurchaseUserTransferSchema(mysqlTable),
189
+ purchaseUserTransferRelations:
190
+ getPurchaseUserTransferRelationsSchema(mysqlTable),
177
191
  communicationChannel: getCommunicationChannelSchema(mysqlTable),
178
192
  communicationPreferenceTypes:
179
193
  getCommunicationPreferenceTypesSchema(mysqlTable),
@@ -234,7 +248,7 @@ export function mySqlDrizzleAdapter(
234
248
  resourceProgress,
235
249
  } = createTables(tableFn)
236
250
 
237
- return {
251
+ const adapter: CourseBuilderAdapter = {
238
252
  client,
239
253
  async redeemFullPriceCoupon(options) {
240
254
  const {
@@ -246,7 +260,7 @@ export function mySqlDrizzleAdapter(
246
260
  } = options
247
261
  const email = String(baseEmail).replace(' ', '+')
248
262
 
249
- const coupon = await this.getCouponWithBulkPurchases(couponId)
263
+ const coupon = await adapter.getCouponWithBulkPurchases(couponId)
250
264
 
251
265
  const productId =
252
266
  (coupon && (coupon.restrictedToProductId as string)) ||
@@ -259,16 +273,14 @@ export function mySqlDrizzleAdapter(
259
273
  if (coupon && couponValidation.isRedeemable) {
260
274
  // if the Coupon is the Bulk Coupon of a Bulk Purchase,
261
275
  // then a bulk coupon is being redeemed
262
- const bulkCouponRedemption = Boolean(
263
- coupon.bulkCouponPurchases[0]?.bulkCouponId,
264
- )
276
+ const bulkCouponRedemption = Boolean(coupon.bulkPurchase?.bulkCouponId)
265
277
 
266
- const { user } = await this.findOrCreateUser(email)
278
+ const { user } = await adapter.findOrCreateUser(email)
267
279
 
268
280
  if (!user) throw new Error(`unable-to-create-user-${email}`)
269
281
 
270
282
  const currentUser = currentUserId
271
- ? await this.getUserById(currentUserId)
283
+ ? await adapter.getUserById(currentUserId)
272
284
  : null
273
285
 
274
286
  const redeemingForCurrentUser = currentUser?.id === user.id
@@ -277,7 +289,7 @@ export function mySqlDrizzleAdapter(
277
289
  // Purchase record for this product that is valid and wasn't a bulk
278
290
  // coupon purchase.
279
291
  const existingPurchases =
280
- await this.getExistingNonBulkValidPurchasesOfProduct({
292
+ await adapter.getExistingNonBulkValidPurchasesOfProduct({
281
293
  userId: user.id,
282
294
  productId,
283
295
  })
@@ -287,7 +299,7 @@ export function mySqlDrizzleAdapter(
287
299
 
288
300
  const purchaseId = `purchase-${v4()}`
289
301
 
290
- await this.createPurchase({
302
+ await adapter.createPurchase({
291
303
  id: purchaseId,
292
304
  userId: user.id,
293
305
  couponId: bulkCouponRedemption ? null : coupon.id,
@@ -299,11 +311,11 @@ export function mySqlDrizzleAdapter(
299
311
  },
300
312
  })
301
313
 
302
- const newPurchase = await this.getPurchase(purchaseId)
314
+ const newPurchase = await adapter.getPurchase(purchaseId)
303
315
 
304
- await this.incrementCouponUsedCount(coupon.id)
316
+ await adapter.incrementCouponUsedCount(coupon.id)
305
317
 
306
- await this.createPurchaseTransfer({
318
+ await adapter.createPurchaseTransfer({
307
319
  sourceUserId: user.id,
308
320
  purchaseId: purchaseId,
309
321
  expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
@@ -411,7 +423,7 @@ export function mySqlDrizzleAdapter(
411
423
  }),
412
424
  )
413
425
  },
414
- availableUpgradesForProduct(
426
+ async availableUpgradesForProduct(
415
427
  purchases: any,
416
428
  productId: string,
417
429
  ): Promise<any[]> {
@@ -419,21 +431,24 @@ export function mySqlDrizzleAdapter(
419
431
  ({ productId }: Purchase) => productId,
420
432
  )
421
433
 
422
- return client.query.upgradableProducts.findMany({
423
- where: and(
424
- eq(upgradableProducts.upgradableToId, productId),
425
- inArray(
426
- upgradableProducts.upgradableFromId,
427
- previousPurchaseProductIds,
434
+ if (previousPurchaseProductIds.length > 0) {
435
+ return await client.query.upgradableProducts.findMany({
436
+ where: and(
437
+ eq(upgradableProducts.upgradableToId, productId),
438
+ inArray(
439
+ upgradableProducts.upgradableFromId,
440
+ previousPurchaseProductIds,
441
+ ),
428
442
  ),
429
- ),
430
- })
443
+ })
444
+ }
445
+ return []
431
446
  },
432
447
  clearLessonProgressForUser(options: {
433
448
  userId: string
434
449
  lessons: { id: string; slug: string }[]
435
450
  }): Promise<void> {
436
- throw new Error('Method not implemented.')
451
+ throw new Error('clearLessonProgressForUser Method not implemented.')
437
452
  },
438
453
  async completeLessonProgressForUser(options: {
439
454
  userId: string
@@ -486,11 +501,40 @@ export function mySqlDrizzleAdapter(
486
501
 
487
502
  return parsedLessonProgress.data
488
503
  },
489
- couponForIdOrCode(options: {
504
+ async couponForIdOrCode(options: {
490
505
  code?: string
491
506
  couponId?: string
492
507
  }): Promise<(Coupon & { merchantCoupon: MerchantCoupon }) | null> {
493
- throw new Error('Method not implemented.')
508
+ if (!options.couponId && !options.code) return null
509
+ const couponForIdOrCode = await client.query.coupon.findFirst({
510
+ where: or(
511
+ and(
512
+ or(
513
+ options.code ? eq(coupon.code, options.code) : undefined,
514
+ options.couponId ? eq(coupon.id, options.couponId) : undefined,
515
+ ),
516
+ gte(coupon.expires, new Date()),
517
+ ),
518
+ and(
519
+ or(
520
+ options.code ? eq(coupon.code, options.code) : undefined,
521
+ options.couponId ? eq(coupon.id, options.couponId) : undefined,
522
+ ),
523
+ isNull(coupon.expires),
524
+ ),
525
+ ),
526
+ with: {
527
+ merchantCoupon: true,
528
+ },
529
+ })
530
+
531
+ if (!couponForIdOrCode) return null
532
+
533
+ return couponSchema
534
+ .extend({
535
+ merchantCoupon: merchantCouponSchema,
536
+ })
537
+ .parse(couponForIdOrCode)
494
538
  },
495
539
  async createMerchantChargeAndPurchase(options): Promise<Purchase> {
496
540
  const purchaseId = await client.transaction(async (trx) => {
@@ -576,13 +620,10 @@ export function mySqlDrizzleAdapter(
576
620
  ),
577
621
  )
578
622
  .then((res) => {
579
- console.log({ res })
580
623
  return res[0]?.coupons ?? null
581
624
  }),
582
625
  )
583
626
 
584
- console.log({ existingBulkCoupon })
585
-
586
627
  const isBulkPurchase =
587
628
  quantity > 1 ||
588
629
  Boolean(existingBulkCoupon) ||
@@ -637,16 +678,12 @@ export function mySqlDrizzleAdapter(
637
678
  merchantAccountId,
638
679
  })
639
680
 
640
- console.log({ newMerchantSession })
641
-
642
681
  const merchantCouponUsed = stripeCouponId
643
682
  ? await client.query.merchantCoupon.findFirst({
644
683
  where: eq(merchantCoupon.identifier, stripeCouponId),
645
684
  })
646
685
  : null
647
686
 
648
- console.log({ merchantCouponUsed })
649
-
650
687
  const pppMerchantCoupon = appliedPPPStripeCouponId
651
688
  ? await client.query.merchantCoupon.findFirst({
652
689
  where: and(
@@ -656,8 +693,6 @@ export function mySqlDrizzleAdapter(
656
693
  })
657
694
  : null
658
695
 
659
- console.log({ pppMerchantCoupon })
660
-
661
696
  const newPurchaseStatus =
662
697
  merchantCouponUsed?.type === 'ppp' || pppMerchantCoupon
663
698
  ? 'Restricted'
@@ -677,8 +712,6 @@ export function mySqlDrizzleAdapter(
677
712
  couponId: usedCouponId || null,
678
713
  })
679
714
 
680
- console.log({ newPurchase })
681
-
682
715
  const oneWeekInMilliseconds = 1000 * 60 * 60 * 24 * 7
683
716
 
684
717
  const newPurchaseUserTransfer = await client
@@ -692,8 +725,6 @@ export function mySqlDrizzleAdapter(
692
725
  sourceUserId: userId,
693
726
  })
694
727
 
695
- console.log({ newPurchaseUserTransfer })
696
-
697
728
  // const result = await Promise.all([
698
729
  // newMerchantCharge,
699
730
  // newPurchase,
@@ -704,8 +735,6 @@ export function mySqlDrizzleAdapter(
704
735
  //
705
736
  // console.log('result', { result })
706
737
 
707
- console.log('inside', { purchaseId })
708
-
709
738
  return purchaseId
710
739
  } catch (error) {
711
740
  console.error(error)
@@ -714,8 +743,6 @@ export function mySqlDrizzleAdapter(
714
743
  }
715
744
  })
716
745
 
717
- console.log('putside', { purchaseId })
718
-
719
746
  const parsedPurchase = purchaseSchema.safeParse(
720
747
  await client.query.purchases.findFirst({
721
748
  where: eq(purchaseTable.id, purchaseId as string),
@@ -752,7 +779,7 @@ export function mySqlDrizzleAdapter(
752
779
  return merchantCustomer
753
780
  }
754
781
 
755
- return await this.createMerchantCustomer({
782
+ return await adapter.createMerchantCustomer({
756
783
  identifier: options.identifier,
757
784
  merchantAccountId: options.merchantAccountId,
758
785
  userId: options.user.id,
@@ -765,10 +792,10 @@ export function mySqlDrizzleAdapter(
765
792
  user: User
766
793
  isNewUser: boolean
767
794
  }> {
768
- const user = await this.getUserByEmail?.(email)
795
+ const user = await adapter.getUserByEmail?.(email)
769
796
 
770
797
  if (!user) {
771
- const newUser = await this.createUser?.({
798
+ const newUser = await adapter.createUser?.({
772
799
  id: `u_${v4()}`,
773
800
  email,
774
801
  name,
@@ -799,19 +826,94 @@ export function mySqlDrizzleAdapter(
799
826
  .then((res) => res[0] ?? null),
800
827
  )
801
828
  },
802
- getCouponWithBulkPurchases(couponId: string): Promise<
829
+ async getCouponWithBulkPurchases(couponId: string): Promise<
803
830
  | (Coupon & {
804
- bulkCouponPurchases: { bulkCouponId: string }[]
831
+ bulkPurchase?: Purchase | null
832
+ bulkCouponPurchases: { bulkCouponId?: string | null }[]
805
833
  })
806
834
  | null
807
835
  > {
808
- throw new Error('Method not implemented.')
836
+ logger.debug('getCouponWithBulkPurchases', { couponId })
837
+ let couponData
838
+ try {
839
+ couponData =
840
+ (await client.query.coupon.findFirst({
841
+ where: eq(coupon.id, couponId),
842
+ with: {
843
+ bulkPurchase: true,
844
+ bulkCouponPurchases: true,
845
+ },
846
+ })) || null
847
+ } catch (e) {
848
+ console.log('getCouponWithBulkPurchases')
849
+ logger.error(e as Error)
850
+ }
851
+
852
+ if (!couponData) {
853
+ logger.debug('getCouponWithBulkPurchases', {
854
+ couponId,
855
+ error: 'no coupon found',
856
+ })
857
+ return null
858
+ }
859
+
860
+ const parsedCoupon = couponSchema
861
+ .merge(
862
+ z.object({
863
+ bulkCouponPurchases: z.array(purchaseSchema),
864
+ bulkPurchase: purchaseSchema.nullable().optional(),
865
+ }),
866
+ )
867
+ .nullable()
868
+ .safeParse(couponData)
869
+
870
+ if (!parsedCoupon.success) {
871
+ console.error('Error parsing coupon', couponData)
872
+ return null
873
+ }
874
+ return parsedCoupon.data
809
875
  },
810
- getDefaultCoupon(productIds?: string[]): Promise<{
876
+ async getDefaultCoupon(productIds?: string[]): Promise<{
811
877
  defaultMerchantCoupon: MerchantCoupon
812
878
  defaultCoupon: Coupon
813
879
  } | null> {
814
- throw new Error('Method not implemented.')
880
+ const activeSaleCoupon = await client.query.coupon.findFirst({
881
+ where: and(
882
+ eq(coupon.status, 1),
883
+ eq(coupon.default, true),
884
+ gte(coupon.expires, new Date()),
885
+ or(
886
+ productIds
887
+ ? inArray(coupon.restrictedToProductId, productIds)
888
+ : undefined,
889
+ isNull(coupon.restrictedToProductId),
890
+ ),
891
+ ),
892
+ orderBy: desc(coupon.percentageDiscount),
893
+ with: {
894
+ merchantCoupon: true,
895
+ product: true,
896
+ },
897
+ })
898
+ if (activeSaleCoupon) {
899
+ const { restrictedToProductId } = activeSaleCoupon
900
+ const validForProdcutId = restrictedToProductId
901
+ ? productIds?.includes(restrictedToProductId as string)
902
+ : true
903
+
904
+ const { merchantCoupon: defaultMerchantCoupon, ...defaultCoupon } =
905
+ activeSaleCoupon
906
+
907
+ if (validForProdcutId) {
908
+ return {
909
+ defaultMerchantCoupon: merchantCouponSchema.parse(
910
+ defaultMerchantCoupon,
911
+ ),
912
+ defaultCoupon: couponSchema.parse(defaultCoupon),
913
+ }
914
+ }
915
+ }
916
+ return null
815
917
  },
816
918
  getLessonProgressCountsByDate(): Promise<
817
919
  {
@@ -819,13 +921,141 @@ export function mySqlDrizzleAdapter(
819
921
  completedAt: string
820
922
  }[]
821
923
  > {
822
- throw new Error('Method not implemented.')
924
+ throw new Error('getLessonProgressCountsByDate Method not implemented.')
823
925
  },
824
- getLessonProgressForUser(userId: string): Promise<ResourceProgress[]> {
825
- throw new Error('Method not implemented.')
926
+ async getLessonProgressForUser(
927
+ userId: string,
928
+ ): Promise<ResourceProgress[]> {
929
+ const userProgress = await client.query.resourceProgress.findMany({
930
+ where: eq(resourceProgress.userId, userId),
931
+ })
932
+ const parsed = z.array(resourceProgressSchema).safeParse(userProgress)
933
+ if (!parsed.success) {
934
+ console.error('Error parsing user progress', userProgress)
935
+ return []
936
+ }
937
+ return parsed.data
938
+ },
939
+ async getModuleProgressForUser(
940
+ userIdOrEmail: string,
941
+ moduleIdOrSlug: string,
942
+ ): Promise<ModuleProgress> {
943
+ const module = await client.query.contentResource.findFirst({
944
+ where: or(
945
+ eq(contentResource.id, moduleIdOrSlug),
946
+ eq(
947
+ sql`JSON_EXTRACT (${contentResource.fields}, "$.slug")`,
948
+ moduleIdOrSlug,
949
+ ),
950
+ ),
951
+ with: {
952
+ resources: {
953
+ orderBy: asc(contentResourceResource.position),
954
+ },
955
+ },
956
+ })
957
+
958
+ const parsedModule = ContentResourceSchema.parse(module)
959
+
960
+ const moduleResources =
961
+ await client.query.contentResourceResource.findMany({
962
+ where: inArray(
963
+ contentResourceResource.resourceOfId,
964
+ parsedModule.resources?.map((r) => r.resourceId) ?? [],
965
+ ),
966
+ orderBy: asc(contentResourceResource.position),
967
+ })
968
+
969
+ const parsedModuleResources = z
970
+ .array(ContentResourceResourceSchema)
971
+ .safeParse(moduleResources)
972
+
973
+ if (!parsedModuleResources.success) {
974
+ console.error(
975
+ 'Error parsing module resources',
976
+ parsedModuleResources.error,
977
+ )
978
+ return {
979
+ completedLessons: [],
980
+ nextResource: null,
981
+ percentCompleted: 0,
982
+ completedLessonsCount: 0,
983
+ totalLessonsCount: 0,
984
+ }
985
+ }
986
+
987
+ const user = await client.query.users.findFirst({
988
+ where: or(eq(users.id, userIdOrEmail), eq(users.email, userIdOrEmail)),
989
+ with: {
990
+ roles: true,
991
+ },
992
+ })
993
+
994
+ if (!user) {
995
+ console.error('User not found', userIdOrEmail)
996
+ return {
997
+ completedLessons: [],
998
+ nextResource: null,
999
+ percentCompleted: 0,
1000
+ completedLessonsCount: 0,
1001
+ totalLessonsCount: parsedModuleResources.data.length,
1002
+ }
1003
+ }
1004
+
1005
+ const parsedUser = userSchema.parse(user)
1006
+
1007
+ const userProgress = await client.query.resourceProgress.findMany({
1008
+ where: and(
1009
+ eq(resourceProgress.userId, parsedUser.id),
1010
+ isNotNull(resourceProgress.completedAt),
1011
+ inArray(
1012
+ resourceProgress.contentResourceId,
1013
+ parsedModuleResources.data.map((r) => r.resourceId),
1014
+ ),
1015
+ ),
1016
+ orderBy: asc(resourceProgress.completedAt),
1017
+ })
1018
+
1019
+ const nextResourceId = moduleResources.find(
1020
+ (r) => !userProgress.find((p) => p.contentResourceId === r.resourceId),
1021
+ )?.resourceId
1022
+
1023
+ const nextResource = await client.query.contentResource.findFirst({
1024
+ where: eq(contentResource.id, nextResourceId as string),
1025
+ })
1026
+
1027
+ const parsedNextResource = ContentResourceSchema.nullable()
1028
+ .optional()
1029
+ .default(null)
1030
+ .parse(nextResource)
1031
+
1032
+ const parsedProgress = z
1033
+ .array(resourceProgressSchema)
1034
+ .safeParse(userProgress)
1035
+ if (!parsedProgress.success) {
1036
+ console.error('Error parsing user progress', parsedProgress.error)
1037
+ return {
1038
+ completedLessons: [],
1039
+ nextResource: null,
1040
+ percentCompleted: 0,
1041
+ completedLessonsCount: 0,
1042
+ totalLessonsCount: parsedModuleResources.data.length,
1043
+ }
1044
+ }
1045
+ const percentCompleted = Math.round(
1046
+ (parsedProgress.data.length / parsedModuleResources.data.length) * 100,
1047
+ )
1048
+
1049
+ return {
1050
+ completedLessons: parsedProgress.data,
1051
+ nextResource: parsedNextResource,
1052
+ percentCompleted,
1053
+ completedLessonsCount: parsedProgress.data.length,
1054
+ totalLessonsCount: parsedModuleResources.data.length,
1055
+ }
826
1056
  },
827
1057
  getLessonProgresses(): Promise<ResourceProgress[]> {
828
- throw new Error('Method not implemented.')
1058
+ throw new Error('getLessonProgresses Method not implemented.')
829
1059
  },
830
1060
  async getMerchantCharge(
831
1061
  merchantChargeId: string,
@@ -833,7 +1063,6 @@ export function mySqlDrizzleAdapter(
833
1063
  const mCharge = await client.query.merchantCharge.findFirst({
834
1064
  where: eq(merchantCharge.id, merchantChargeId),
835
1065
  })
836
- console.log('mCharge', mCharge)
837
1066
  const parsed = merchantChargeSchema.safeParse(mCharge)
838
1067
  if (!parsed.success) {
839
1068
  console.error('Error parsing merchantCharge', mCharge)
@@ -906,7 +1135,7 @@ export function mySqlDrizzleAdapter(
906
1135
  )
907
1136
  },
908
1137
  getPrice(productId: string): Promise<Price | null> {
909
- throw new Error('Method not implemented.')
1138
+ throw new Error('getPrice not implemented.')
910
1139
  },
911
1140
  async getPriceForProduct(productId: string): Promise<Price | null> {
912
1141
  return priceSchema.nullable().parse(
@@ -936,7 +1165,7 @@ export function mySqlDrizzleAdapter(
936
1165
  id: newPurchaseId,
937
1166
  })
938
1167
 
939
- const newPurchase = await this.getPurchase(newPurchaseId)
1168
+ const newPurchase = await adapter.getPurchase(newPurchaseId)
940
1169
 
941
1170
  return purchaseSchema.parse(newPurchase)
942
1171
  },
@@ -944,20 +1173,28 @@ export function mySqlDrizzleAdapter(
944
1173
  async getPurchaseForStripeCharge(
945
1174
  stripeChargeId: string,
946
1175
  ): Promise<Purchase | null> {
1176
+ logger.debug('getPurchaseForStripeCharge', { stripeChargeId })
1177
+
1178
+ const chargeForPurchase = merchantChargeSchema.nullable().parse(
1179
+ (await client.query.merchantCharge.findFirst({
1180
+ where: eq(merchantCharge.identifier, stripeChargeId),
1181
+ })) || null,
1182
+ )
1183
+
1184
+ if (!chargeForPurchase) {
1185
+ logger.error(new Error('No charge found for purchase'))
1186
+ return null
1187
+ }
1188
+
947
1189
  const purchase = purchaseSchema.safeParse(
948
- await client
949
- .select()
950
- .from(purchaseTable)
951
- .leftJoin(
952
- merchantCharge,
953
- and(
954
- eq(merchantCharge.identifier, stripeChargeId),
955
- eq(merchantCharge.id, purchaseTable.merchantChargeId),
956
- ),
957
- )
958
- .then((res) => {
959
- return res[0]?.purchases ?? null
960
- }),
1190
+ await client.query.purchases.findFirst({
1191
+ where: eq(purchaseTable.merchantChargeId, chargeForPurchase.id),
1192
+ with: {
1193
+ user: true,
1194
+ product: true,
1195
+ bulkCoupon: true,
1196
+ },
1197
+ }),
961
1198
  )
962
1199
 
963
1200
  if (!purchase.success) {
@@ -966,7 +1203,7 @@ export function mySqlDrizzleAdapter(
966
1203
 
967
1204
  return purchase.data
968
1205
  },
969
- getPurchaseUserTransferById(options: { id: string }): Promise<
1206
+ async getPurchaseUserTransferById(options: { id: string }): Promise<
970
1207
  | (PurchaseUserTransfer & {
971
1208
  sourceUser: User
972
1209
  targetUser: User | null
@@ -974,15 +1211,51 @@ export function mySqlDrizzleAdapter(
974
1211
  })
975
1212
  | null
976
1213
  > {
977
- throw new Error('Method not implemented.')
1214
+ const purchaseTransferData =
1215
+ await client.query.purchaseUserTransfer.findFirst({
1216
+ where: eq(purchaseUserTransfer.id, options.id),
1217
+ with: {
1218
+ sourceUser: true,
1219
+ targetUser: true,
1220
+ purchase: true,
1221
+ },
1222
+ })
1223
+
1224
+ return purchaseUserTransferSchema
1225
+ .merge(
1226
+ z.object({
1227
+ sourceUser: userSchema,
1228
+ targetUser: userSchema.nullable(),
1229
+ purchase: purchaseSchema,
1230
+ }),
1231
+ )
1232
+ .nullable()
1233
+ .parse(purchaseTransferData)
978
1234
  },
979
- getPurchaseWithUser(purchaseId: string): Promise<
1235
+ async getPurchaseWithUser(purchaseId: string): Promise<
980
1236
  | (Purchase & {
981
1237
  user: User
982
1238
  })
983
1239
  | null
984
1240
  > {
985
- throw new Error('Method not implemented.')
1241
+ const purchaseData =
1242
+ (await client.query.purchases.findFirst({
1243
+ where: eq(purchaseTable.id, purchaseId),
1244
+ with: {
1245
+ user: true,
1246
+ },
1247
+ })) || null
1248
+
1249
+ const parsedPurchase = purchaseSchema
1250
+ .merge(z.object({ user: userSchema }))
1251
+ .nullable()
1252
+ .safeParse(purchaseData)
1253
+
1254
+ if (!parsedPurchase.success) {
1255
+ console.error('Error parsing purchase', parsedPurchase.error)
1256
+ return null
1257
+ }
1258
+ return parsedPurchase.data
986
1259
  },
987
1260
  async getPurchasesForUser(userId?: string): Promise<Purchase[]> {
988
1261
  if (!userId) {
@@ -1021,7 +1294,7 @@ export function mySqlDrizzleAdapter(
1021
1294
  userId: string,
1022
1295
  ): Promise<{
1023
1296
  purchase?: Purchase
1024
- existingPurchase?: Purchase & { product?: Product | null }
1297
+ existingPurchase?: Purchase | null
1025
1298
  availableUpgrades: UpgradableProduct[]
1026
1299
  }> {
1027
1300
  const visiblePurchaseStates = ['Valid', 'Refunded', 'Restricted']
@@ -1043,8 +1316,6 @@ export function mySqlDrizzleAdapter(
1043
1316
 
1044
1317
  const allPurchases = parsedPurchases.success ? parsedPurchases.data : []
1045
1318
 
1046
- console.log('🦦', { allPurchases })
1047
-
1048
1319
  const thePurchase = await client.query.purchases.findFirst({
1049
1320
  where: and(
1050
1321
  eq(purchaseTable.id, purchaseId),
@@ -1066,8 +1337,6 @@ export function mySqlDrizzleAdapter(
1066
1337
  }
1067
1338
  }
1068
1339
 
1069
- console.log('🦦', { parsedPurchase: parsedPurchase.data })
1070
-
1071
1340
  const purchaseCanUpgrade = ['Valid', 'Restricted'].includes(
1072
1341
  parsedPurchase.data.state || '',
1073
1342
  )
@@ -1095,24 +1364,56 @@ export function mySqlDrizzleAdapter(
1095
1364
  })
1096
1365
  }
1097
1366
 
1098
- const existingPurchase = allPurchases.find(
1099
- (p) => p.product?.id === parsedPurchase.data.product?.id,
1100
- )
1367
+ const existingPurchase = purchaseSchema
1368
+ .optional()
1369
+ .nullable()
1370
+ .parse(
1371
+ (await client.query.purchases.findFirst({
1372
+ where: and(
1373
+ eq(purchaseTable.userId, userId),
1374
+ eq(purchaseTable.productId, parsedPurchase.data.productId),
1375
+ inArray(purchaseTable.status, ['Valid', 'Restricted']),
1376
+ isNull(purchaseTable.bulkCouponId),
1377
+ not(eq(purchaseTable.id, parsedPurchase.data.id)),
1378
+ ),
1379
+ with: {
1380
+ user: true,
1381
+ product: true,
1382
+ bulkCoupon: true,
1383
+ },
1384
+ })) || null,
1385
+ )
1101
1386
 
1102
- return Promise.resolve({
1387
+ return {
1103
1388
  availableUpgrades: z
1104
1389
  .array(upgradableProductSchema)
1105
1390
  .parse(availableUpgrades),
1106
1391
  existingPurchase,
1107
1392
  purchase: parsedPurchase.data,
1108
- })
1393
+ }
1109
1394
  },
1110
1395
  async getUserById(userId: string): Promise<User | null> {
1111
- return userSchema.nullable().parse(
1112
- await client.query.users.findFirst({
1396
+ const user = await client.query.users
1397
+ .findFirst({
1113
1398
  where: eq(users.id, userId),
1114
- }),
1115
- )
1399
+ with: {
1400
+ roles: {
1401
+ with: {
1402
+ role: true,
1403
+ },
1404
+ },
1405
+ },
1406
+ })
1407
+ .then(async (res) => {
1408
+ if (res) {
1409
+ return {
1410
+ ...res,
1411
+ roles: res.roles.map((r) => r.role),
1412
+ }
1413
+ }
1414
+ })
1415
+
1416
+ return userSchema.nullable().parse(user ?? null)
1116
1417
  },
1117
1418
  async pricesOfPurchasesTowardOneBundle({
1118
1419
  userId,
@@ -1156,12 +1457,56 @@ export function mySqlDrizzleAdapter(
1156
1457
 
1157
1458
  return z.array(priceSchema).parse(foundPrices)
1158
1459
  },
1159
- toggleLessonProgressForUser(options: {
1460
+ async toggleLessonProgressForUser(options: {
1160
1461
  userId: string
1161
1462
  lessonId?: string
1162
- lessonSlug?: string
1163
1463
  }): Promise<ResourceProgress | null> {
1164
- throw new Error('Method not implemented.')
1464
+ if (!options.lessonId) {
1465
+ throw new Error('No lessonId provided')
1466
+ }
1467
+
1468
+ let lessonProgress = await client.query.resourceProgress.findFirst({
1469
+ where: and(
1470
+ eq(resourceProgress.userId, options.userId),
1471
+ eq(resourceProgress.contentResourceId, options.lessonId),
1472
+ ),
1473
+ })
1474
+
1475
+ const now = new Date()
1476
+
1477
+ if (lessonProgress) {
1478
+ await client
1479
+ .update(resourceProgress)
1480
+ .set({
1481
+ completedAt: lessonProgress.completedAt ? null : now,
1482
+ updatedAt: now,
1483
+ })
1484
+ .where(eq(resourceProgress.contentResourceId, options.lessonId))
1485
+ } else {
1486
+ await client.insert(resourceProgress).values({
1487
+ userId: options.userId,
1488
+ contentResourceId: options.lessonId,
1489
+ completedAt: now,
1490
+ updatedAt: now,
1491
+ })
1492
+ }
1493
+
1494
+ lessonProgress = await client.query.resourceProgress.findFirst({
1495
+ where: and(
1496
+ eq(resourceProgress.userId, options.userId),
1497
+ eq(resourceProgress.contentResourceId, options.lessonId),
1498
+ ),
1499
+ })
1500
+
1501
+ const parsedLessonProgress =
1502
+ resourceProgressSchema.safeParse(lessonProgress)
1503
+
1504
+ if (!parsedLessonProgress.success) {
1505
+ console.error('Error parsing lesson progress', lessonProgress)
1506
+ return null
1507
+ }
1508
+
1509
+ return parsedLessonProgress.data
1165
1510
  },
1166
1511
  transferPurchasesToNewUser(options: {
1167
1512
  merchantCustomerId: string
@@ -1173,13 +1518,27 @@ export function mySqlDrizzleAdapter(
1173
1518
  chargeId: string,
1174
1519
  status: 'Valid' | 'Refunded' | 'Disputed' | 'Banned' | 'Restricted',
1175
1520
  ): Promise<Purchase | undefined> {
1176
- throw new Error('Method not implemented.')
1521
+ throw new Error('updatePurchaseStatusForCharge Method not implemented.')
1177
1522
  },
1178
- updatePurchaseUserTransferTransferState(options: {
1523
+ async updatePurchaseUserTransferTransferState(options: {
1179
1524
  id: string
1180
1525
  transferState: PurchaseUserTransferState
1181
1526
  }): Promise<PurchaseUserTransfer | null> {
1182
- throw new Error('Method not implemented.')
1527
+ await client
1528
+ .update(purchaseUserTransfer)
1529
+ .set({
1530
+ transferState: options.transferState,
1531
+ })
1532
+ .where(eq(purchaseUserTransfer.id, options.id))
1533
+
1534
+ const purchaseUserTransferData =
1535
+ (await client.query.purchaseUserTransfer.findFirst({
1536
+ where: eq(purchaseUserTransfer.id, options.id),
1537
+ })) || null
1538
+
1539
+ return purchaseUserTransferSchema
1540
+ .nullable()
1541
+ .parse(purchaseUserTransferData)
1183
1542
  },
1184
1543
  addResourceToResource: async function (options) {
1185
1544
  const { parentResourceId, childResourceId } = options
@@ -1571,4 +1930,6 @@ export function mySqlDrizzleAdapter(
1571
1930
  return undefined
1572
1931
  },
1573
1932
  }
1933
+
1934
+ return adapter
1574
1935
  }