@forklaunch/implementation-billing-base 0.1.17 → 0.2.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.
Files changed (41) hide show
  1. package/lib/__test__/schemaEquality.test.js +35 -14
  2. package/lib/eject/domain/schemas/checkoutSession.schema.ts +24 -4
  3. package/lib/eject/domain/schemas/paymentLink.schema.ts +24 -4
  4. package/lib/eject/services/billingPortal.service.ts +104 -32
  5. package/lib/eject/services/checkoutSession.service.ts +115 -32
  6. package/lib/eject/services/paymentLink.service.ts +116 -41
  7. package/lib/eject/services/plan.service.ts +60 -22
  8. package/lib/eject/services/subscription.service.ts +83 -26
  9. package/lib/schemas/checkoutSession.schema.d.ts +97 -11
  10. package/lib/schemas/checkoutSession.schema.d.ts.map +1 -1
  11. package/lib/schemas/paymentLink.schema.d.ts +101 -12
  12. package/lib/schemas/paymentLink.schema.d.ts.map +1 -1
  13. package/lib/schemas/typebox/checkoutSession.schema.d.ts +130 -10
  14. package/lib/schemas/typebox/checkoutSession.schema.d.ts.map +1 -1
  15. package/lib/schemas/typebox/checkoutSession.schema.js +9 -3
  16. package/lib/schemas/typebox/paymentLink.schema.d.ts +144 -12
  17. package/lib/schemas/typebox/paymentLink.schema.d.ts.map +1 -1
  18. package/lib/schemas/typebox/paymentLink.schema.js +9 -3
  19. package/lib/schemas/zod/checkoutSession.schema.d.ts +66 -12
  20. package/lib/schemas/zod/checkoutSession.schema.d.ts.map +1 -1
  21. package/lib/schemas/zod/checkoutSession.schema.js +9 -3
  22. package/lib/schemas/zod/paymentLink.schema.d.ts +66 -12
  23. package/lib/schemas/zod/paymentLink.schema.d.ts.map +1 -1
  24. package/lib/schemas/zod/paymentLink.schema.js +9 -3
  25. package/lib/services/billingPortal.service.d.ts +25 -7
  26. package/lib/services/billingPortal.service.d.ts.map +1 -1
  27. package/lib/services/billingPortal.service.js +87 -29
  28. package/lib/services/checkoutSession.service.d.ts +63 -20
  29. package/lib/services/checkoutSession.service.d.ts.map +1 -1
  30. package/lib/services/checkoutSession.service.js +67 -16
  31. package/lib/services/paymentLink.service.d.ts +39 -20
  32. package/lib/services/paymentLink.service.d.ts.map +1 -1
  33. package/lib/services/paymentLink.service.js +88 -24
  34. package/lib/services/plan.service.d.ts +19 -7
  35. package/lib/services/plan.service.d.ts.map +1 -1
  36. package/lib/services/plan.service.js +49 -17
  37. package/lib/services/subscription.service.d.ts +21 -7
  38. package/lib/services/subscription.service.d.ts.map +1 -1
  39. package/lib/services/subscription.service.js +72 -20
  40. package/lib/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +6 -6
@@ -1,15 +1,17 @@
1
1
  import { IdDto, IdsDto, InstanceTypeRecord } from '@forklaunch/common';
2
2
  import { createCacheKey, TtlCache } from '@forklaunch/core/cache';
3
+ import {
4
+ evaluateTelemetryOptions,
5
+ MetricsDefinition,
6
+ OpenTelemetryCollector,
7
+ TelemetryOptions
8
+ } from '@forklaunch/core/http';
3
9
  import {
4
10
  InternalDtoMapper,
5
11
  RequestDtoMapperConstructor,
6
12
  ResponseDtoMapperConstructor,
7
13
  transformIntoInternalDtoMapper
8
14
  } from '@forklaunch/core/mappers';
9
- import {
10
- MetricsDefinition,
11
- OpenTelemetryCollector
12
- } from '@forklaunch/core/http';
13
15
  import { PaymentLinkService } from '@forklaunch/interfaces-billing/interfaces';
14
16
  import {
15
17
  CreatePaymentLinkDto,
@@ -17,42 +19,51 @@ import {
17
19
  UpdatePaymentLinkDto
18
20
  } from '@forklaunch/interfaces-billing/types';
19
21
  import { AnySchemaValidator } from '@forklaunch/validator';
22
+ import { EntityManager } from '@mikro-orm/core';
20
23
 
21
24
  export class BasePaymentLinkService<
22
25
  SchemaValidator extends AnySchemaValidator,
23
26
  CurrencyEnum,
27
+ StatusEnum,
24
28
  Metrics extends MetricsDefinition = MetricsDefinition,
25
29
  Dto extends {
26
- PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
27
- CreatePaymentLinkDtoMapper: CreatePaymentLinkDto<CurrencyEnum>;
28
- UpdatePaymentLinkDtoMapper: UpdatePaymentLinkDto<CurrencyEnum>;
30
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
31
+ CreatePaymentLinkDtoMapper: CreatePaymentLinkDto<CurrencyEnum, StatusEnum>;
32
+ UpdatePaymentLinkDtoMapper: UpdatePaymentLinkDto<CurrencyEnum, StatusEnum>;
29
33
  } = {
30
- PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
31
- CreatePaymentLinkDtoMapper: CreatePaymentLinkDto<CurrencyEnum>;
32
- UpdatePaymentLinkDtoMapper: UpdatePaymentLinkDto<CurrencyEnum>;
34
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
35
+ CreatePaymentLinkDtoMapper: CreatePaymentLinkDto<CurrencyEnum, StatusEnum>;
36
+ UpdatePaymentLinkDtoMapper: UpdatePaymentLinkDto<CurrencyEnum, StatusEnum>;
33
37
  },
34
38
  Entities extends {
35
- PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
36
- CreatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
37
- UpdatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
39
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
40
+ CreatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
41
+ UpdatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
38
42
  } = {
39
- PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
40
- CreatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
41
- UpdatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
43
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
44
+ CreatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
45
+ UpdatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum, StatusEnum>;
42
46
  }
43
- > implements PaymentLinkService<CurrencyEnum>
47
+ > implements PaymentLinkService<CurrencyEnum, StatusEnum>
44
48
  {
45
- #mapperss: InternalDtoMapper<
46
- InstanceTypeRecord<typeof this.mapperss>,
49
+ #mappers: InternalDtoMapper<
50
+ InstanceTypeRecord<typeof this.mappers>,
47
51
  Entities,
48
52
  Dto
49
53
  >;
54
+ private evaluatedTelemetryOptions: {
55
+ logging?: boolean;
56
+ metrics?: boolean;
57
+ tracing?: boolean;
58
+ };
59
+ private enableDatabaseBackup: boolean;
50
60
 
51
61
  constructor(
62
+ protected readonly em: EntityManager,
52
63
  protected readonly cache: TtlCache,
53
64
  protected readonly openTelemetryCollector: OpenTelemetryCollector<Metrics>,
54
65
  protected readonly schemaValidator: SchemaValidator,
55
- protected readonly mapperss: {
66
+ protected readonly mappers: {
56
67
  PaymentLinkDtoMapper: ResponseDtoMapperConstructor<
57
68
  SchemaValidator,
58
69
  Dto['PaymentLinkDtoMapper'],
@@ -68,9 +79,21 @@ export class BasePaymentLinkService<
68
79
  Dto['UpdatePaymentLinkDtoMapper'],
69
80
  Entities['UpdatePaymentLinkDtoMapper']
70
81
  >;
82
+ },
83
+ readonly options?: {
84
+ enableDatabaseBackup?: boolean;
85
+ telemetry?: TelemetryOptions;
71
86
  }
72
87
  ) {
73
- this.#mapperss = transformIntoInternalDtoMapper(mapperss, schemaValidator);
88
+ this.#mappers = transformIntoInternalDtoMapper(mappers, schemaValidator);
89
+ this.enableDatabaseBackup = options?.enableDatabaseBackup ?? false;
90
+ this.evaluatedTelemetryOptions = options?.telemetry
91
+ ? evaluateTelemetryOptions(options.telemetry).enabled
92
+ : {
93
+ logging: false,
94
+ metrics: false,
95
+ tracing: false
96
+ };
74
97
  }
75
98
 
76
99
  protected cacheKeyPrefix = 'payment_link';
@@ -79,48 +102,78 @@ export class BasePaymentLinkService<
79
102
  async createPaymentLink(
80
103
  paymentLinkDto: Dto['CreatePaymentLinkDtoMapper']
81
104
  ): Promise<Dto['PaymentLinkDtoMapper']> {
82
- // TODO: Perform permission checks here
105
+ if (this.evaluatedTelemetryOptions.logging) {
106
+ this.openTelemetryCollector.info('Creating payment link', paymentLinkDto);
107
+ }
108
+
83
109
  const paymentLink =
84
- this.#mapperss.CreatePaymentLinkDtoMapper.deserializeDtoToEntity(
85
- paymentLinkDto
110
+ await this.#mappers.CreatePaymentLinkDtoMapper.deserializeDtoToEntity(
111
+ paymentLinkDto,
112
+ this.em
86
113
  );
114
+
115
+ if (this.enableDatabaseBackup) {
116
+ await this.em.persistAndFlush(paymentLink);
117
+ }
118
+
119
+ const createdPaymentLinkDto =
120
+ await this.#mappers.PaymentLinkDtoMapper.serializeEntityToDto(
121
+ paymentLink
122
+ );
123
+
87
124
  await this.cache.putRecord({
88
- key: this.createCacheKey(paymentLink.id),
89
- value: paymentLink,
125
+ key: this.createCacheKey(createdPaymentLinkDto.id),
126
+ value: createdPaymentLinkDto,
90
127
  ttlMilliseconds: this.cache.getTtlMilliseconds()
91
128
  });
92
129
 
93
- return this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
94
- paymentLink
95
- );
130
+ return createdPaymentLinkDto;
96
131
  }
97
132
 
98
133
  async updatePaymentLink(
99
134
  paymentLinkDto: Dto['UpdatePaymentLinkDtoMapper']
100
135
  ): Promise<Dto['PaymentLinkDtoMapper']> {
136
+ if (this.evaluatedTelemetryOptions.logging) {
137
+ this.openTelemetryCollector.info('Updating payment link', paymentLinkDto);
138
+ }
139
+
101
140
  const cacheKey = this.createCacheKey(paymentLinkDto.id);
102
- const existingLink =
103
- await this.cache.readRecord<Entities['PaymentLinkDtoMapper']>(cacheKey);
141
+ const existingLink = (
142
+ await this.cache.readRecord<Entities['PaymentLinkDtoMapper']>(cacheKey)
143
+ )?.value;
104
144
  if (!existingLink) {
105
145
  throw new Error('Payment link not found');
106
146
  }
107
147
  const paymentLink =
108
- this.#mapperss.UpdatePaymentLinkDtoMapper.deserializeDtoToEntity(
109
- paymentLinkDto
148
+ await this.#mappers.UpdatePaymentLinkDtoMapper.deserializeDtoToEntity(
149
+ paymentLinkDto,
150
+ this.em
110
151
  );
111
- const updatedLink = { ...existingLink, ...paymentLink };
152
+
153
+ if (this.enableDatabaseBackup) {
154
+ await this.em.persistAndFlush(paymentLink);
155
+ }
156
+
157
+ const updatedLinkDto = {
158
+ ...existingLink,
159
+ ...(await this.#mappers.PaymentLinkDtoMapper.serializeEntityToDto(
160
+ paymentLink
161
+ ))
162
+ };
163
+
112
164
  await this.cache.putRecord({
113
165
  key: cacheKey,
114
- value: updatedLink,
166
+ value: updatedLinkDto,
115
167
  ttlMilliseconds: this.cache.getTtlMilliseconds()
116
168
  });
117
169
 
118
- return this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
119
- updatedLink
120
- );
170
+ return updatedLinkDto;
121
171
  }
122
172
 
123
173
  async getPaymentLink({ id }: IdDto): Promise<Dto['PaymentLinkDtoMapper']> {
174
+ if (this.evaluatedTelemetryOptions.logging) {
175
+ this.openTelemetryCollector.info('Getting payment link', { id });
176
+ }
124
177
  const cacheKey = this.createCacheKey(id);
125
178
  const paymentLink =
126
179
  await this.cache.readRecord<Entities['PaymentLinkDtoMapper']>(cacheKey);
@@ -128,23 +181,45 @@ export class BasePaymentLinkService<
128
181
  throw new Error('Payment link not found');
129
182
  }
130
183
 
131
- return this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
184
+ return this.#mappers.PaymentLinkDtoMapper.serializeEntityToDto(
132
185
  paymentLink.value
133
186
  );
134
187
  }
135
188
 
136
189
  async expirePaymentLink({ id }: IdDto): Promise<void> {
137
190
  this.openTelemetryCollector.info('Payment link expired', { id });
191
+
192
+ if (this.enableDatabaseBackup) {
193
+ const paymentLink = await this.em.upsert('PaymentLink', {
194
+ id,
195
+ status: 'EXPIRED'
196
+ });
197
+ await this.em.removeAndFlush(paymentLink);
198
+ }
138
199
  await this.cache.deleteRecord(this.createCacheKey(id));
139
200
  }
140
201
 
141
202
  async handlePaymentSuccess({ id }: IdDto): Promise<void> {
142
203
  this.openTelemetryCollector.info('Payment link success', { id });
204
+ if (this.enableDatabaseBackup) {
205
+ const paymentLink = await this.em.upsert('PaymentLink', {
206
+ id,
207
+ status: 'COMPLETED'
208
+ });
209
+ await this.em.removeAndFlush(paymentLink);
210
+ }
143
211
  await this.cache.deleteRecord(this.createCacheKey(id));
144
212
  }
145
213
 
146
214
  async handlePaymentFailure({ id }: IdDto): Promise<void> {
147
215
  this.openTelemetryCollector.info('Payment link failure', { id });
216
+ if (this.enableDatabaseBackup) {
217
+ const paymentLink = await this.em.upsert('PaymentLink', {
218
+ id,
219
+ status: 'FAILED'
220
+ });
221
+ await this.em.removeAndFlush(paymentLink);
222
+ }
148
223
  await this.cache.deleteRecord(this.createCacheKey(id));
149
224
  }
150
225
 
@@ -155,12 +230,12 @@ export class BasePaymentLinkService<
155
230
  idsDto?.ids.map((id) => this.createCacheKey(id)) ??
156
231
  (await this.cache.listKeys(this.cacheKeyPrefix));
157
232
 
158
- return await Promise.all(
233
+ return Promise.all(
159
234
  keys.map(async (key) => {
160
235
  const paymentLink =
161
236
  await this.cache.readRecord<Entities['PaymentLinkDtoMapper']>(key);
162
237
  const paymentLinkDto =
163
- this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
238
+ this.#mappers.PaymentLinkDtoMapper.serializeEntityToDto(
164
239
  paymentLink.value
165
240
  );
166
241
  return paymentLinkDto;
@@ -1,14 +1,16 @@
1
1
  import { IdDto, IdsDto, InstanceTypeRecord } from '@forklaunch/common';
2
+ import {
3
+ evaluateTelemetryOptions,
4
+ MetricsDefinition,
5
+ OpenTelemetryCollector,
6
+ TelemetryOptions
7
+ } from '@forklaunch/core/http';
2
8
  import {
3
9
  InternalDtoMapper,
4
10
  RequestDtoMapperConstructor,
5
11
  ResponseDtoMapperConstructor,
6
12
  transformIntoInternalDtoMapper
7
13
  } from '@forklaunch/core/mappers';
8
- import {
9
- MetricsDefinition,
10
- OpenTelemetryCollector
11
- } from '@forklaunch/core/http';
12
14
  import { PlanService } from '@forklaunch/interfaces-billing/interfaces';
13
15
  import {
14
16
  CreatePlanDto,
@@ -43,17 +45,22 @@ export class BasePlanService<
43
45
  }
44
46
  > implements PlanService<PlanCadenceEnum, BillingProviderEnum>
45
47
  {
46
- #mapperss: InternalDtoMapper<
47
- InstanceTypeRecord<typeof this.mapperss>,
48
+ #mappers: InternalDtoMapper<
49
+ InstanceTypeRecord<typeof this.mappers>,
48
50
  Entities,
49
51
  Dto
50
52
  >;
53
+ private evaluatedTelemetryOptions: {
54
+ logging?: boolean;
55
+ metrics?: boolean;
56
+ tracing?: boolean;
57
+ };
51
58
 
52
59
  constructor(
53
60
  private em: EntityManager,
54
61
  private readonly openTelemetryCollector: OpenTelemetryCollector<Metrics>,
55
62
  private readonly schemaValidator: SchemaValidator,
56
- private readonly mapperss: {
63
+ private readonly mappers: {
57
64
  PlanDtoMapper: ResponseDtoMapperConstructor<
58
65
  SchemaValidator,
59
66
  Dto['PlanDtoMapper'],
@@ -69,22 +76,37 @@ export class BasePlanService<
69
76
  Dto['UpdatePlanDtoMapper'],
70
77
  Entities['UpdatePlanDtoMapper']
71
78
  >;
79
+ },
80
+ readonly options?: {
81
+ telemetry?: TelemetryOptions;
72
82
  }
73
83
  ) {
74
- this.#mapperss = transformIntoInternalDtoMapper(mapperss, schemaValidator);
84
+ this.#mappers = transformIntoInternalDtoMapper(mappers, schemaValidator);
85
+ this.evaluatedTelemetryOptions = options?.telemetry
86
+ ? evaluateTelemetryOptions(options.telemetry).enabled
87
+ : {
88
+ logging: false,
89
+ metrics: false,
90
+ tracing: false
91
+ };
75
92
  }
76
93
 
77
94
  async listPlans(
78
95
  idsDto?: IdsDto,
79
96
  em?: EntityManager
80
97
  ): Promise<Dto['PlanDtoMapper'][]> {
81
- return (
82
- await (em ?? this.em).findAll('Plan', {
83
- filters: idsDto?.ids ? { id: { $in: idsDto.ids } } : undefined
84
- })
85
- ).map((plan) =>
86
- this.#mapperss.PlanDtoMapper.serializeEntityToDto(
87
- plan as Entities['PlanDtoMapper']
98
+ if (this.evaluatedTelemetryOptions.logging) {
99
+ this.openTelemetryCollector.info('Listing plans', idsDto);
100
+ }
101
+ return Promise.all(
102
+ (
103
+ await (em ?? this.em).findAll('Plan', {
104
+ filters: idsDto?.ids ? { id: { $in: idsDto.ids } } : undefined
105
+ })
106
+ ).map((plan) =>
107
+ this.#mappers.PlanDtoMapper.serializeEntityToDto(
108
+ plan as Entities['PlanDtoMapper']
109
+ )
88
110
  )
89
111
  );
90
112
  }
@@ -93,20 +115,28 @@ export class BasePlanService<
93
115
  planDto: Dto['CreatePlanDtoMapper'],
94
116
  em?: EntityManager
95
117
  ): Promise<Dto['PlanDtoMapper']> {
96
- const plan =
97
- this.#mapperss.CreatePlanDtoMapper.deserializeDtoToEntity(planDto);
118
+ if (this.evaluatedTelemetryOptions.logging) {
119
+ this.openTelemetryCollector.info('Creating plan', planDto);
120
+ }
121
+ const plan = await this.#mappers.CreatePlanDtoMapper.deserializeDtoToEntity(
122
+ planDto,
123
+ em ?? this.em
124
+ );
98
125
  await (em ?? this.em).transactional(async (innerEm) => {
99
126
  await innerEm.persist(plan);
100
127
  });
101
- return this.#mapperss.PlanDtoMapper.serializeEntityToDto(plan);
128
+ return this.#mappers.PlanDtoMapper.serializeEntityToDto(plan);
102
129
  }
103
130
 
104
131
  async getPlan(
105
132
  idDto: IdDto,
106
133
  em?: EntityManager
107
134
  ): Promise<Dto['PlanDtoMapper']> {
135
+ if (this.evaluatedTelemetryOptions.logging) {
136
+ this.openTelemetryCollector.info('Getting plan', idDto);
137
+ }
108
138
  const plan = await (em ?? this.em).findOneOrFail('Plan', idDto);
109
- return this.#mapperss.PlanDtoMapper.serializeEntityToDto(
139
+ return this.#mappers.PlanDtoMapper.serializeEntityToDto(
110
140
  plan as Entities['PlanDtoMapper']
111
141
  );
112
142
  }
@@ -115,18 +145,26 @@ export class BasePlanService<
115
145
  planDto: Dto['UpdatePlanDtoMapper'],
116
146
  em?: EntityManager
117
147
  ): Promise<Dto['PlanDtoMapper']> {
118
- const plan =
119
- this.#mapperss.UpdatePlanDtoMapper.deserializeDtoToEntity(planDto);
148
+ if (this.evaluatedTelemetryOptions.logging) {
149
+ this.openTelemetryCollector.info('Updating plan', planDto);
150
+ }
151
+ const plan = await this.#mappers.UpdatePlanDtoMapper.deserializeDtoToEntity(
152
+ planDto,
153
+ em ?? this.em
154
+ );
120
155
  const updatedPlan = await (em ?? this.em).upsert(plan);
121
156
  await (em ?? this.em).transactional(async (innerEm) => {
122
157
  await innerEm.persist(plan);
123
158
  });
124
159
  const updatedPlanDto =
125
- this.#mapperss.PlanDtoMapper.serializeEntityToDto(updatedPlan);
160
+ await this.#mappers.PlanDtoMapper.serializeEntityToDto(updatedPlan);
126
161
  return updatedPlanDto;
127
162
  }
128
163
 
129
164
  async deletePlan(idDto: { id: string }, em?: EntityManager): Promise<void> {
165
+ if (this.evaluatedTelemetryOptions.logging) {
166
+ this.openTelemetryCollector.info('Deleting plan', idDto);
167
+ }
130
168
  await (em ?? this.em).nativeDelete('Plan', idDto);
131
169
  }
132
170
  }
@@ -1,14 +1,16 @@
1
1
  import { IdDto, InstanceTypeRecord } from '@forklaunch/common';
2
+ import {
3
+ evaluateTelemetryOptions,
4
+ MetricsDefinition,
5
+ OpenTelemetryCollector,
6
+ TelemetryOptions
7
+ } from '@forklaunch/core/http';
2
8
  import {
3
9
  InternalDtoMapper,
4
10
  RequestDtoMapperConstructor,
5
11
  ResponseDtoMapperConstructor,
6
12
  transformIntoInternalDtoMapper
7
13
  } from '@forklaunch/core/mappers';
8
- import {
9
- MetricsDefinition,
10
- OpenTelemetryCollector
11
- } from '@forklaunch/core/http';
12
14
  import { SubscriptionService } from '@forklaunch/interfaces-billing/interfaces';
13
15
  import {
14
16
  CreateSubscriptionDto,
@@ -67,17 +69,22 @@ export class BaseSubscriptionService<
67
69
  }
68
70
  > implements SubscriptionService<PartyType, BillingProviderType>
69
71
  {
70
- #mapperss: InternalDtoMapper<
71
- InstanceTypeRecord<typeof this.mapperss>,
72
+ #mappers: InternalDtoMapper<
73
+ InstanceTypeRecord<typeof this.mappers>,
72
74
  Entities,
73
75
  Dto
74
76
  >;
77
+ private evaluatedTelemetryOptions: {
78
+ logging?: boolean;
79
+ metrics?: boolean;
80
+ tracing?: boolean;
81
+ };
75
82
 
76
83
  constructor(
77
84
  protected em: EntityManager,
78
85
  protected readonly openTelemetryCollector: OpenTelemetryCollector<Metrics>,
79
86
  protected readonly schemaValidator: SchemaValidator,
80
- protected readonly mapperss: {
87
+ protected readonly mappers: {
81
88
  SubscriptionDtoMapper: ResponseDtoMapperConstructor<
82
89
  SchemaValidator,
83
90
  Dto['SubscriptionDtoMapper'],
@@ -93,29 +100,49 @@ export class BaseSubscriptionService<
93
100
  Dto['UpdateSubscriptionDtoMapper'],
94
101
  Entities['UpdateSubscriptionDtoMapper']
95
102
  >;
103
+ },
104
+ readonly options?: {
105
+ enableDatabaseBackup?: boolean;
106
+ telemetry?: TelemetryOptions;
96
107
  }
97
108
  ) {
98
- this.#mapperss = transformIntoInternalDtoMapper<
109
+ this.#mappers = transformIntoInternalDtoMapper<
99
110
  SchemaValidator,
100
- typeof this.mapperss,
111
+ typeof this.mappers,
101
112
  Entities,
102
113
  Dto
103
- >(mapperss, this.schemaValidator);
114
+ >(mappers, this.schemaValidator);
115
+ this.evaluatedTelemetryOptions = options?.telemetry
116
+ ? evaluateTelemetryOptions(options.telemetry).enabled
117
+ : {
118
+ logging: false,
119
+ metrics: false,
120
+ tracing: false
121
+ };
104
122
  }
105
123
 
106
124
  async createSubscription(
107
125
  subscriptionDto: Dto['CreateSubscriptionDtoMapper'],
108
126
  em?: EntityManager
109
127
  ): Promise<Dto['SubscriptionDtoMapper']> {
110
- const subscription =
111
- this.#mapperss.CreateSubscriptionDtoMapper.deserializeDtoToEntity(
128
+ if (this.evaluatedTelemetryOptions.logging) {
129
+ this.openTelemetryCollector.info(
130
+ 'Creating subscription',
112
131
  subscriptionDto
113
132
  );
133
+ }
134
+ const subscription =
135
+ await this.#mappers.CreateSubscriptionDtoMapper.deserializeDtoToEntity(
136
+ subscriptionDto,
137
+ em ?? this.em
138
+ );
114
139
  await (em ?? this.em).transactional(async (innerEm) => {
115
140
  await innerEm.persist(subscription);
116
141
  });
117
142
  const createdSubscriptionDto =
118
- this.#mapperss.SubscriptionDtoMapper.serializeEntityToDto(subscription);
143
+ await this.#mappers.SubscriptionDtoMapper.serializeEntityToDto(
144
+ subscription
145
+ );
119
146
  return createdSubscriptionDto;
120
147
  }
121
148
 
@@ -123,11 +150,14 @@ export class BaseSubscriptionService<
123
150
  idDto: IdDto,
124
151
  em?: EntityManager
125
152
  ): Promise<Dto['SubscriptionDtoMapper']> {
153
+ if (this.evaluatedTelemetryOptions.logging) {
154
+ this.openTelemetryCollector.info('Getting subscription', idDto);
155
+ }
126
156
  const subscription = await (em ?? this.em).findOneOrFail(
127
157
  'Subscription',
128
158
  idDto
129
159
  );
130
- return this.#mapperss.SubscriptionDtoMapper.serializeEntityToDto(
160
+ return this.#mappers.SubscriptionDtoMapper.serializeEntityToDto(
131
161
  subscription as Entities['SubscriptionDtoMapper']
132
162
  );
133
163
  }
@@ -136,13 +166,16 @@ export class BaseSubscriptionService<
136
166
  { id }: IdDto,
137
167
  em?: EntityManager
138
168
  ): Promise<Dto['SubscriptionDtoMapper']> {
169
+ if (this.evaluatedTelemetryOptions.logging) {
170
+ this.openTelemetryCollector.info('Getting user subscription', id);
171
+ }
139
172
  const subscription = await (em ?? this.em).findOneOrFail('Subscription', {
140
173
  partyId: id,
141
174
  partyType: 'USER',
142
175
  active: true
143
176
  });
144
177
 
145
- return this.#mapperss.SubscriptionDtoMapper.serializeEntityToDto(
178
+ return this.#mappers.SubscriptionDtoMapper.serializeEntityToDto(
146
179
  subscription as Entities['SubscriptionDtoMapper']
147
180
  );
148
181
  }
@@ -151,12 +184,15 @@ export class BaseSubscriptionService<
151
184
  { id }: IdDto,
152
185
  em?: EntityManager
153
186
  ): Promise<Dto['SubscriptionDtoMapper']> {
187
+ if (this.evaluatedTelemetryOptions.logging) {
188
+ this.openTelemetryCollector.info('Getting organization subscription', id);
189
+ }
154
190
  const subscription = await (em ?? this.em).findOneOrFail('Subscription', {
155
191
  partyId: id,
156
192
  partyType: 'ORGANIZATION',
157
193
  active: true
158
194
  });
159
- return this.#mapperss.SubscriptionDtoMapper.serializeEntityToDto(
195
+ return this.#mappers.SubscriptionDtoMapper.serializeEntityToDto(
160
196
  subscription as Entities['SubscriptionDtoMapper']
161
197
  );
162
198
  }
@@ -165,16 +201,23 @@ export class BaseSubscriptionService<
165
201
  subscriptionDto: Dto['UpdateSubscriptionDtoMapper'],
166
202
  em?: EntityManager
167
203
  ): Promise<Dto['SubscriptionDtoMapper']> {
168
- const subscription =
169
- this.#mapperss.UpdateSubscriptionDtoMapper.deserializeDtoToEntity(
204
+ if (this.evaluatedTelemetryOptions.logging) {
205
+ this.openTelemetryCollector.info(
206
+ 'Updating subscription',
170
207
  subscriptionDto
171
208
  );
209
+ }
210
+ const subscription =
211
+ this.#mappers.UpdateSubscriptionDtoMapper.deserializeDtoToEntity(
212
+ subscriptionDto,
213
+ em ?? this.em
214
+ );
172
215
  const updatedSubscription = await (em ?? this.em).upsert(subscription);
173
216
  await (em ?? this.em).transactional(async (innerEm) => {
174
217
  await innerEm.persist(updatedSubscription);
175
218
  });
176
219
  const updatedSubscriptionDto =
177
- this.#mapperss.SubscriptionDtoMapper.serializeEntityToDto(
220
+ await this.#mappers.SubscriptionDtoMapper.serializeEntityToDto(
178
221
  updatedSubscription
179
222
  );
180
223
 
@@ -185,6 +228,9 @@ export class BaseSubscriptionService<
185
228
  idDto: { id: string },
186
229
  em?: EntityManager
187
230
  ): Promise<void> {
231
+ if (this.evaluatedTelemetryOptions.logging) {
232
+ this.openTelemetryCollector.info('Deleting subscription', idDto);
233
+ }
188
234
  const subscription = await (em ?? this.em).findOne('Subscription', idDto);
189
235
  if (!subscription) {
190
236
  throw new Error('Subscription not found');
@@ -196,6 +242,9 @@ export class BaseSubscriptionService<
196
242
  idsDto: { ids?: string[] },
197
243
  em?: EntityManager
198
244
  ): Promise<Dto['SubscriptionDtoMapper'][]> {
245
+ if (this.evaluatedTelemetryOptions.logging) {
246
+ this.openTelemetryCollector.info('Listing subscriptions', idsDto);
247
+ }
199
248
  const subscriptions = await (em ?? this.em).findAll('Subscription', {
200
249
  where: idsDto.ids
201
250
  ? {
@@ -206,16 +255,21 @@ export class BaseSubscriptionService<
206
255
  : undefined
207
256
  });
208
257
 
209
- return subscriptions.map((subscription) => {
210
- const subscriptionDto =
211
- this.#mapperss.SubscriptionDtoMapper.serializeEntityToDto(
212
- subscription as Entities['SubscriptionDtoMapper']
213
- );
214
- return subscriptionDto;
215
- });
258
+ return Promise.all(
259
+ subscriptions.map((subscription) => {
260
+ const subscriptionDto =
261
+ this.#mappers.SubscriptionDtoMapper.serializeEntityToDto(
262
+ subscription as Entities['SubscriptionDtoMapper']
263
+ );
264
+ return subscriptionDto;
265
+ })
266
+ );
216
267
  }
217
268
 
218
269
  async cancelSubscription(idDto: IdDto, em?: EntityManager): Promise<void> {
270
+ if (this.evaluatedTelemetryOptions.logging) {
271
+ this.openTelemetryCollector.info('Canceling subscription', idDto);
272
+ }
219
273
  const subscription = await (em ?? this.em).findOne('Subscription', idDto);
220
274
  if (!subscription) {
221
275
  throw new Error('Subscription not found');
@@ -227,6 +281,9 @@ export class BaseSubscriptionService<
227
281
  }
228
282
 
229
283
  async resumeSubscription(idDto: IdDto, em?: EntityManager): Promise<void> {
284
+ if (this.evaluatedTelemetryOptions.logging) {
285
+ this.openTelemetryCollector.info('Resuming subscription', idDto);
286
+ }
230
287
  const subscription = await (em ?? this.em).findOne('Subscription', idDto);
231
288
  if (!subscription) {
232
289
  throw new Error('Subscription not found');