@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.
- package/lib/__test__/schemaEquality.test.js +35 -14
- package/lib/eject/domain/schemas/checkoutSession.schema.ts +24 -4
- package/lib/eject/domain/schemas/paymentLink.schema.ts +24 -4
- package/lib/eject/services/billingPortal.service.ts +104 -32
- package/lib/eject/services/checkoutSession.service.ts +115 -32
- package/lib/eject/services/paymentLink.service.ts +116 -41
- package/lib/eject/services/plan.service.ts +60 -22
- package/lib/eject/services/subscription.service.ts +83 -26
- package/lib/schemas/checkoutSession.schema.d.ts +97 -11
- package/lib/schemas/checkoutSession.schema.d.ts.map +1 -1
- package/lib/schemas/paymentLink.schema.d.ts +101 -12
- package/lib/schemas/paymentLink.schema.d.ts.map +1 -1
- package/lib/schemas/typebox/checkoutSession.schema.d.ts +130 -10
- package/lib/schemas/typebox/checkoutSession.schema.d.ts.map +1 -1
- package/lib/schemas/typebox/checkoutSession.schema.js +9 -3
- package/lib/schemas/typebox/paymentLink.schema.d.ts +144 -12
- package/lib/schemas/typebox/paymentLink.schema.d.ts.map +1 -1
- package/lib/schemas/typebox/paymentLink.schema.js +9 -3
- package/lib/schemas/zod/checkoutSession.schema.d.ts +66 -12
- package/lib/schemas/zod/checkoutSession.schema.d.ts.map +1 -1
- package/lib/schemas/zod/checkoutSession.schema.js +9 -3
- package/lib/schemas/zod/paymentLink.schema.d.ts +66 -12
- package/lib/schemas/zod/paymentLink.schema.d.ts.map +1 -1
- package/lib/schemas/zod/paymentLink.schema.js +9 -3
- package/lib/services/billingPortal.service.d.ts +25 -7
- package/lib/services/billingPortal.service.d.ts.map +1 -1
- package/lib/services/billingPortal.service.js +87 -29
- package/lib/services/checkoutSession.service.d.ts +63 -20
- package/lib/services/checkoutSession.service.d.ts.map +1 -1
- package/lib/services/checkoutSession.service.js +67 -16
- package/lib/services/paymentLink.service.d.ts +39 -20
- package/lib/services/paymentLink.service.d.ts.map +1 -1
- package/lib/services/paymentLink.service.js +88 -24
- package/lib/services/plan.service.d.ts +19 -7
- package/lib/services/plan.service.d.ts.map +1 -1
- package/lib/services/plan.service.js +49 -17
- package/lib/services/subscription.service.d.ts +21 -7
- package/lib/services/subscription.service.d.ts.map +1 -1
- package/lib/services/subscription.service.js +72 -20
- package/lib/tsconfig.tsbuildinfo +1 -1
- 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
|
-
#
|
|
46
|
-
InstanceTypeRecord<typeof this.
|
|
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
|
|
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.#
|
|
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
|
-
|
|
105
|
+
if (this.evaluatedTelemetryOptions.logging) {
|
|
106
|
+
this.openTelemetryCollector.info('Creating payment link', paymentLinkDto);
|
|
107
|
+
}
|
|
108
|
+
|
|
83
109
|
const paymentLink =
|
|
84
|
-
this.#
|
|
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(
|
|
89
|
-
value:
|
|
125
|
+
key: this.createCacheKey(createdPaymentLinkDto.id),
|
|
126
|
+
value: createdPaymentLinkDto,
|
|
90
127
|
ttlMilliseconds: this.cache.getTtlMilliseconds()
|
|
91
128
|
});
|
|
92
129
|
|
|
93
|
-
return
|
|
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.#
|
|
109
|
-
paymentLinkDto
|
|
148
|
+
await this.#mappers.UpdatePaymentLinkDtoMapper.deserializeDtoToEntity(
|
|
149
|
+
paymentLinkDto,
|
|
150
|
+
this.em
|
|
110
151
|
);
|
|
111
|
-
|
|
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:
|
|
166
|
+
value: updatedLinkDto,
|
|
115
167
|
ttlMilliseconds: this.cache.getTtlMilliseconds()
|
|
116
168
|
});
|
|
117
169
|
|
|
118
|
-
return
|
|
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.#
|
|
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
|
|
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.#
|
|
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
|
-
#
|
|
47
|
-
InstanceTypeRecord<typeof this.
|
|
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
|
|
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.#
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
97
|
-
this
|
|
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.#
|
|
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.#
|
|
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
|
-
|
|
119
|
-
this
|
|
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.#
|
|
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
|
-
#
|
|
71
|
-
InstanceTypeRecord<typeof this.
|
|
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
|
|
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.#
|
|
109
|
+
this.#mappers = transformIntoInternalDtoMapper<
|
|
99
110
|
SchemaValidator,
|
|
100
|
-
typeof this.
|
|
111
|
+
typeof this.mappers,
|
|
101
112
|
Entities,
|
|
102
113
|
Dto
|
|
103
|
-
>(
|
|
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
|
-
|
|
111
|
-
this
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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
|
-
|
|
169
|
-
this
|
|
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.#
|
|
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
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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');
|