@serialsubscriptions/platform-integration 0.0.79
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/README.md +1 -0
- package/lib/SSIProject.d.ts +343 -0
- package/lib/SSIProject.js +429 -0
- package/lib/SSIProjectApi.d.ts +384 -0
- package/lib/SSIProjectApi.js +534 -0
- package/lib/SSISubscribedFeatureApi.d.ts +387 -0
- package/lib/SSISubscribedFeatureApi.js +511 -0
- package/lib/SSISubscribedLimitApi.d.ts +384 -0
- package/lib/SSISubscribedLimitApi.js +534 -0
- package/lib/SSISubscribedPlanApi.d.ts +384 -0
- package/lib/SSISubscribedPlanApi.js +537 -0
- package/lib/SubscribedPlanManager.d.ts +380 -0
- package/lib/SubscribedPlanManager.js +288 -0
- package/lib/UsageApi.d.ts +128 -0
- package/lib/UsageApi.js +224 -0
- package/lib/auth.server.d.ts +192 -0
- package/lib/auth.server.js +579 -0
- package/lib/cache/SSICache.d.ts +40 -0
- package/lib/cache/SSICache.js +134 -0
- package/lib/cache/backends/MemoryCacheBackend.d.ts +15 -0
- package/lib/cache/backends/MemoryCacheBackend.js +46 -0
- package/lib/cache/backends/RedisCacheBackend.d.ts +27 -0
- package/lib/cache/backends/RedisCacheBackend.js +95 -0
- package/lib/cache/constants.d.ts +7 -0
- package/lib/cache/constants.js +10 -0
- package/lib/cache/types.d.ts +27 -0
- package/lib/cache/types.js +2 -0
- package/lib/frontend/index.d.ts +1 -0
- package/lib/frontend/index.js +6 -0
- package/lib/frontend/session/SessionClient.d.ts +24 -0
- package/lib/frontend/session/SessionClient.js +145 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.js +38 -0
- package/lib/lib/session/SessionClient.d.ts +11 -0
- package/lib/lib/session/SessionClient.js +47 -0
- package/lib/lib/session/index.d.ts +3 -0
- package/lib/lib/session/index.js +3 -0
- package/lib/lib/session/stores/MemoryStore.d.ts +7 -0
- package/lib/lib/session/stores/MemoryStore.js +23 -0
- package/lib/lib/session/stores/index.d.ts +1 -0
- package/lib/lib/session/stores/index.js +1 -0
- package/lib/lib/session/types.d.ts +37 -0
- package/lib/lib/session/types.js +1 -0
- package/lib/session/SessionClient.d.ts +19 -0
- package/lib/session/SessionClient.js +132 -0
- package/lib/session/SessionManager.d.ts +139 -0
- package/lib/session/SessionManager.js +443 -0
- package/lib/stateStore.d.ts +5 -0
- package/lib/stateStore.js +9 -0
- package/lib/storage/SSIStorage.d.ts +24 -0
- package/lib/storage/SSIStorage.js +117 -0
- package/lib/storage/backends/MemoryBackend.d.ts +10 -0
- package/lib/storage/backends/MemoryBackend.js +44 -0
- package/lib/storage/backends/PostgresBackend.d.ts +24 -0
- package/lib/storage/backends/PostgresBackend.js +106 -0
- package/lib/storage/backends/RedisBackend.d.ts +19 -0
- package/lib/storage/backends/RedisBackend.js +78 -0
- package/lib/storage/types.d.ts +27 -0
- package/lib/storage/types.js +2 -0
- package/package.json +71 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
* SubscribedPlanManager - High-level manager for subscribed plans with features and limits
|
|
4
|
+
*
|
|
5
|
+
* This manager wires together the three JSON:API clients (SSISubscribedPlanApi,
|
|
6
|
+
* SSISubscribedFeatureApi, SSISubscribedLimitApi) and provides a convenient interface
|
|
7
|
+
* for fetching subscribed plans with their associated features and limits already attached.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const domain = 'https://account.serialsubscriptionsdev.com';
|
|
13
|
+
* const manager = new SubscribedPlanManager(domain);
|
|
14
|
+
*
|
|
15
|
+
* // Optional: set OAuth/JWT Bearer token if needed
|
|
16
|
+
* manager.setBearerToken('YOUR_ACCESS_TOKEN');
|
|
17
|
+
*
|
|
18
|
+
* // Get all plans with features and limits
|
|
19
|
+
* const allPlans = await manager.getAllPlans();
|
|
20
|
+
*
|
|
21
|
+
* // Get a single plan by internal numeric ID
|
|
22
|
+
* const plan = await manager.getPlanById(1);
|
|
23
|
+
*
|
|
24
|
+
* // Get plans by project ID
|
|
25
|
+
* const projectPlans = await manager.getPlansByProjectId(42);
|
|
26
|
+
*
|
|
27
|
+
* // Get plans by user internal ID
|
|
28
|
+
* const userPlans = await manager.getPlansByUserId(3);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import { type JsonApiResource } from './SSISubscribedPlanApi';
|
|
32
|
+
/**
|
|
33
|
+
* Attributes for a subscribed_plan--subscribed_plan resource.
|
|
34
|
+
* Based on your JSON sample; extra fields will still be present in the raw JSON:API resource.
|
|
35
|
+
*/
|
|
36
|
+
export interface SubscribedPlanAttributes {
|
|
37
|
+
drupal_internal__id: number;
|
|
38
|
+
name: string;
|
|
39
|
+
status: string;
|
|
40
|
+
subscription_period: string;
|
|
41
|
+
name_subscript: string | null;
|
|
42
|
+
most_popular: boolean;
|
|
43
|
+
free_trial: boolean;
|
|
44
|
+
free_trial_days: number;
|
|
45
|
+
description: string | null;
|
|
46
|
+
description_features: {
|
|
47
|
+
value: string | null;
|
|
48
|
+
format: string | null;
|
|
49
|
+
processed: string | null;
|
|
50
|
+
} | null;
|
|
51
|
+
order: number;
|
|
52
|
+
price_show: boolean;
|
|
53
|
+
price_monthly: string;
|
|
54
|
+
price_monthly_subscript: string | null;
|
|
55
|
+
price_annually: string;
|
|
56
|
+
price_annually_subscript: string | null;
|
|
57
|
+
price_discounted: boolean;
|
|
58
|
+
price_discount_monthly: string;
|
|
59
|
+
price_discount_annually: string;
|
|
60
|
+
price_discount_months: number;
|
|
61
|
+
price_discount_years: number;
|
|
62
|
+
button_cta: string;
|
|
63
|
+
footer_description: string | null;
|
|
64
|
+
active_date: string | null;
|
|
65
|
+
archive_date: string | null;
|
|
66
|
+
trial_end_date: string | null;
|
|
67
|
+
created: string;
|
|
68
|
+
changed: string;
|
|
69
|
+
organization_id: number;
|
|
70
|
+
project_id: number | null;
|
|
71
|
+
metadata: unknown | null;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Attributes for a subscribed_feature--subscribed_feature resource.
|
|
76
|
+
*/
|
|
77
|
+
export interface SubscribedFeatureAttributes {
|
|
78
|
+
drupal_internal__id: number;
|
|
79
|
+
feature_name: string;
|
|
80
|
+
feature_category: string;
|
|
81
|
+
feature_type: string;
|
|
82
|
+
feature_value: string | null;
|
|
83
|
+
order: number;
|
|
84
|
+
created: string;
|
|
85
|
+
changed: string;
|
|
86
|
+
[key: string]: unknown;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Attributes for a subscribed_limit--subscribed_limit resource.
|
|
90
|
+
*/
|
|
91
|
+
export interface SubscribedLimitAttributes {
|
|
92
|
+
drupal_internal__id: number;
|
|
93
|
+
limit_name: string;
|
|
94
|
+
limit_description: string;
|
|
95
|
+
status: boolean;
|
|
96
|
+
limit_units: string;
|
|
97
|
+
limit_units_plural: string;
|
|
98
|
+
limit_type: string;
|
|
99
|
+
limit_value: number;
|
|
100
|
+
limit_period: string;
|
|
101
|
+
base_fee: string;
|
|
102
|
+
limit_rate: number;
|
|
103
|
+
allow_overage: boolean;
|
|
104
|
+
overage_rate: number;
|
|
105
|
+
overage_limit: number;
|
|
106
|
+
order: number;
|
|
107
|
+
created: string;
|
|
108
|
+
changed: string;
|
|
109
|
+
[key: string]: unknown;
|
|
110
|
+
}
|
|
111
|
+
export interface SubscribedFeature extends SubscribedFeatureAttributes {
|
|
112
|
+
/**
|
|
113
|
+
* UUID of the subscribed_feature entity.
|
|
114
|
+
*/
|
|
115
|
+
uuid: string;
|
|
116
|
+
/**
|
|
117
|
+
* Raw JSON:API relationships if you need them.
|
|
118
|
+
*/
|
|
119
|
+
relationships?: JsonApiResource['relationships'];
|
|
120
|
+
}
|
|
121
|
+
export interface SubscribedLimit extends SubscribedLimitAttributes {
|
|
122
|
+
/**
|
|
123
|
+
* UUID of the subscribed_limit entity.
|
|
124
|
+
*/
|
|
125
|
+
uuid: string;
|
|
126
|
+
relationships?: JsonApiResource['relationships'];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* High-level plan object with its features and limits already attached.
|
|
130
|
+
*/
|
|
131
|
+
export interface SubscribedPlan extends SubscribedPlanAttributes {
|
|
132
|
+
/**
|
|
133
|
+
* UUID of the subscribed_plan entity.
|
|
134
|
+
*/
|
|
135
|
+
uuid: string;
|
|
136
|
+
relationships?: JsonApiResource['relationships'];
|
|
137
|
+
/**
|
|
138
|
+
* Features attached to this subscribed plan.
|
|
139
|
+
*/
|
|
140
|
+
features: SubscribedFeature[];
|
|
141
|
+
/**
|
|
142
|
+
* Limits attached to this subscribed plan.
|
|
143
|
+
*/
|
|
144
|
+
limits: SubscribedLimit[];
|
|
145
|
+
}
|
|
146
|
+
export interface SubscribedPlanManagerOptions {
|
|
147
|
+
/**
|
|
148
|
+
* Override the JSON:API base path for subscribed plans.
|
|
149
|
+
* Default: '/jsonapi/subscribed_plan/subscribed_plan'
|
|
150
|
+
*/
|
|
151
|
+
planApiBasePath?: string;
|
|
152
|
+
/**
|
|
153
|
+
* Override the JSON:API base path for subscribed features.
|
|
154
|
+
* Default: '/jsonapi/subscribed_feature/subscribed_feature'
|
|
155
|
+
*/
|
|
156
|
+
featureApiBasePath?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Override the JSON:API base path for subscribed limits.
|
|
159
|
+
* Default: '/jsonapi/subscribed_limit/subscribed_limit'
|
|
160
|
+
*/
|
|
161
|
+
limitApiBasePath?: string;
|
|
162
|
+
/**
|
|
163
|
+
* Optional request timeout (ms) if your API clients support it.
|
|
164
|
+
*/
|
|
165
|
+
timeout?: number;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* SubscribedPlanManager
|
|
169
|
+
*
|
|
170
|
+
* Fetches subscribed plans and hydrates each with its subscribed features and limits.
|
|
171
|
+
*
|
|
172
|
+
* Filtering conventions (using internal integer IDs):
|
|
173
|
+
* - By plan ID: filter[drupal_internal__id]={subscribed_plan_int_id}
|
|
174
|
+
* - By project ID: filter[project_id]={project_int_id}
|
|
175
|
+
* - By user ID: filter[user_id.meta.drupal_internal__target_id]={user_int_id}
|
|
176
|
+
*
|
|
177
|
+
* For features/limits attached to a plan:
|
|
178
|
+
* - filter[subscribed_plan_id.meta.drupal_internal__target_id]={subscribed_plan_int_id}
|
|
179
|
+
*/
|
|
180
|
+
export declare class SubscribedPlanManager {
|
|
181
|
+
private planApi;
|
|
182
|
+
private featureApi;
|
|
183
|
+
private limitApi;
|
|
184
|
+
private featureCache;
|
|
185
|
+
private limitCache;
|
|
186
|
+
private static readonly CACHE_TTL_SEC;
|
|
187
|
+
constructor(domain: string, options?: SubscribedPlanManagerOptions);
|
|
188
|
+
/**
|
|
189
|
+
* Set a shared Bearer token on all three underlying API clients.
|
|
190
|
+
*/
|
|
191
|
+
setBearerToken(token: string): void;
|
|
192
|
+
/**
|
|
193
|
+
* Get *all* subscribed plans for the current context,
|
|
194
|
+
* each with its features[] and limits[].
|
|
195
|
+
*/
|
|
196
|
+
getAllPlans(): Promise<SubscribedPlan[]>;
|
|
197
|
+
/**
|
|
198
|
+
* Get a single subscribed plan by its internal numeric ID
|
|
199
|
+
* (attributes.drupal_internal__id), with features[] and limits[].
|
|
200
|
+
*/
|
|
201
|
+
getPlanById(subscribedPlanId: number): Promise<SubscribedPlan | null>;
|
|
202
|
+
/**
|
|
203
|
+
* Get all subscribed plans for a given project_id.
|
|
204
|
+
*
|
|
205
|
+
* Uses: ?filter[project_id]={project_int_id}
|
|
206
|
+
*/
|
|
207
|
+
getPlansByProjectId(projectId: number): Promise<SubscribedPlan[]>;
|
|
208
|
+
/**
|
|
209
|
+
* Filter an array of plans by project_id.
|
|
210
|
+
*
|
|
211
|
+
* @param plans - Array of subscribed plans to filter.
|
|
212
|
+
* @param projectId - The project ID to filter by.
|
|
213
|
+
* @returns Array of plans that match the project ID.
|
|
214
|
+
*/
|
|
215
|
+
getPlanByProjectId(plans: SubscribedPlan[], projectId: number): SubscribedPlan[];
|
|
216
|
+
/**
|
|
217
|
+
* Get all subscribed plans for a given user (internal user ID).
|
|
218
|
+
*
|
|
219
|
+
* Uses: ?filter[user_id.meta.drupal_internal__target_id]={user_int_id}
|
|
220
|
+
*/
|
|
221
|
+
getPlansByUserId(userId: number): Promise<SubscribedPlan[]>;
|
|
222
|
+
/**
|
|
223
|
+
* Get the internal numeric ID of a limit by its name from a plan.
|
|
224
|
+
*
|
|
225
|
+
* @param plan - The subscribed plan to search in.
|
|
226
|
+
* @param limit_name - The name of the limit to find.
|
|
227
|
+
* @returns The internal numeric ID (drupal_internal__id) of the limit, or null if not found.
|
|
228
|
+
*/
|
|
229
|
+
getLimitId(plan: SubscribedPlan, limit_name: string): number | null;
|
|
230
|
+
/**
|
|
231
|
+
* Internal helper to find a limit by name or ID.
|
|
232
|
+
*/
|
|
233
|
+
private findLimit;
|
|
234
|
+
/**
|
|
235
|
+
* Check if a limit is enabled (status is true).
|
|
236
|
+
*
|
|
237
|
+
* @param plan - The subscribed plan to search in.
|
|
238
|
+
* @param limit_name - The name of the limit to check.
|
|
239
|
+
* @returns True if the limit exists and is enabled, false otherwise.
|
|
240
|
+
*/
|
|
241
|
+
isLimitEnabled(plan: SubscribedPlan, limit_name: string): boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Check if a limit is enabled (status is true).
|
|
244
|
+
*
|
|
245
|
+
* @param plan - The subscribed plan to search in.
|
|
246
|
+
* @param limit_id - The internal numeric ID of the limit to check.
|
|
247
|
+
* @returns True if the limit exists and is enabled, false otherwise.
|
|
248
|
+
*/
|
|
249
|
+
isLimitEnabled(plan: SubscribedPlan, limit_id: number): boolean;
|
|
250
|
+
/**
|
|
251
|
+
* Get the type of a limit.
|
|
252
|
+
*
|
|
253
|
+
* @param plan - The subscribed plan to search in.
|
|
254
|
+
* @param limit_name - The name of the limit.
|
|
255
|
+
* @returns The limit_type, or null if not found.
|
|
256
|
+
*/
|
|
257
|
+
getLimitType(plan: SubscribedPlan, limit_name: string): string | null;
|
|
258
|
+
/**
|
|
259
|
+
* Get the type of a limit.
|
|
260
|
+
*
|
|
261
|
+
* @param plan - The subscribed plan to search in.
|
|
262
|
+
* @param limit_id - The internal numeric ID of the limit.
|
|
263
|
+
* @returns The limit_type, or null if not found.
|
|
264
|
+
*/
|
|
265
|
+
getLimitType(plan: SubscribedPlan, limit_id: number): string | null;
|
|
266
|
+
/**
|
|
267
|
+
* Get the maximum value (limit_value) of a limit.
|
|
268
|
+
*
|
|
269
|
+
* @param plan - The subscribed plan to search in.
|
|
270
|
+
* @param limit_name - The name of the limit.
|
|
271
|
+
* @returns The limit_value, or null if not found.
|
|
272
|
+
*/
|
|
273
|
+
getLimitMax(plan: SubscribedPlan, limit_name: string): number | null;
|
|
274
|
+
/**
|
|
275
|
+
* Get the maximum value (limit_value) of a limit.
|
|
276
|
+
*
|
|
277
|
+
* @param plan - The subscribed plan to search in.
|
|
278
|
+
* @param limit_id - The internal numeric ID of the limit.
|
|
279
|
+
* @returns The limit_value, or null if not found.
|
|
280
|
+
*/
|
|
281
|
+
getLimitMax(plan: SubscribedPlan, limit_id: number): number | null;
|
|
282
|
+
/**
|
|
283
|
+
* Check if a limit allows overage.
|
|
284
|
+
*
|
|
285
|
+
* @param plan - The subscribed plan to search in.
|
|
286
|
+
* @param limit_name - The name of the limit.
|
|
287
|
+
* @returns True if allow_overage is true, false otherwise.
|
|
288
|
+
*/
|
|
289
|
+
limitAllowsOverage(plan: SubscribedPlan, limit_name: string): boolean;
|
|
290
|
+
/**
|
|
291
|
+
* Check if a limit allows overage.
|
|
292
|
+
*
|
|
293
|
+
* @param plan - The subscribed plan to search in.
|
|
294
|
+
* @param limit_id - The internal numeric ID of the limit.
|
|
295
|
+
* @returns True if allow_overage is true, false otherwise.
|
|
296
|
+
*/
|
|
297
|
+
limitAllowsOverage(plan: SubscribedPlan, limit_id: number): boolean;
|
|
298
|
+
/**
|
|
299
|
+
* Get the maximum overage limit. Returns 0 if overage is not allowed.
|
|
300
|
+
*
|
|
301
|
+
* @param plan - The subscribed plan to search in.
|
|
302
|
+
* @param limit_name - The name of the limit.
|
|
303
|
+
* @returns The overage_limit if allow_overage is true, 0 otherwise.
|
|
304
|
+
*/
|
|
305
|
+
limitOverageMax(plan: SubscribedPlan, limit_name: string): number;
|
|
306
|
+
/**
|
|
307
|
+
* Get the maximum overage limit. Returns 0 if overage is not allowed.
|
|
308
|
+
*
|
|
309
|
+
* @param plan - The subscribed plan to search in.
|
|
310
|
+
* @param limit_id - The internal numeric ID of the limit.
|
|
311
|
+
* @returns The overage_limit if allow_overage is true, 0 otherwise.
|
|
312
|
+
*/
|
|
313
|
+
limitOverageMax(plan: SubscribedPlan, limit_id: number): number;
|
|
314
|
+
/**
|
|
315
|
+
* Get the units for a limit.
|
|
316
|
+
*
|
|
317
|
+
* @param plan - The subscribed plan to search in.
|
|
318
|
+
* @param limit_name - The name of the limit.
|
|
319
|
+
* @returns The limit_units, or null if not found.
|
|
320
|
+
*/
|
|
321
|
+
getLimitUnits(plan: SubscribedPlan, limit_name: string): string | null;
|
|
322
|
+
/**
|
|
323
|
+
* Get the units for a limit.
|
|
324
|
+
*
|
|
325
|
+
* @param plan - The subscribed plan to search in.
|
|
326
|
+
* @param limit_id - The internal numeric ID of the limit.
|
|
327
|
+
* @returns The limit_units, or null if not found.
|
|
328
|
+
*/
|
|
329
|
+
getLimitUnits(plan: SubscribedPlan, limit_id: number): string | null;
|
|
330
|
+
/**
|
|
331
|
+
* Get the plural units for a limit.
|
|
332
|
+
*
|
|
333
|
+
* @param plan - The subscribed plan to search in.
|
|
334
|
+
* @param limit_name - The name of the limit.
|
|
335
|
+
* @returns The limit_units_plural, or null if not found.
|
|
336
|
+
*/
|
|
337
|
+
getLimitUnitsPlural(plan: SubscribedPlan, limit_name: string): string | null;
|
|
338
|
+
/**
|
|
339
|
+
* Get the plural units for a limit.
|
|
340
|
+
*
|
|
341
|
+
* @param plan - The subscribed plan to search in.
|
|
342
|
+
* @param limit_id - The internal numeric ID of the limit.
|
|
343
|
+
* @returns The limit_units_plural, or null if not found.
|
|
344
|
+
*/
|
|
345
|
+
getLimitUnitsPlural(plan: SubscribedPlan, limit_id: number): string | null;
|
|
346
|
+
/**
|
|
347
|
+
* Get the period for a limit.
|
|
348
|
+
*
|
|
349
|
+
* @param plan - The subscribed plan to search in.
|
|
350
|
+
* @param limit_name - The name of the limit.
|
|
351
|
+
* @returns The limit_period, or null if not found.
|
|
352
|
+
*/
|
|
353
|
+
getLimitPeriod(plan: SubscribedPlan, limit_name: string): string | null;
|
|
354
|
+
/**
|
|
355
|
+
* Get the period for a limit.
|
|
356
|
+
*
|
|
357
|
+
* @param plan - The subscribed plan to search in.
|
|
358
|
+
* @param limit_id - The internal numeric ID of the limit.
|
|
359
|
+
* @returns The limit_period, or null if not found.
|
|
360
|
+
*/
|
|
361
|
+
getLimitPeriod(plan: SubscribedPlan, limit_id: number): string | null;
|
|
362
|
+
/**
|
|
363
|
+
* Internal: turn a JSON:API document of subscribed plans into hydrated SubscribedPlan[]
|
|
364
|
+
* with features[] and limits[] attached.
|
|
365
|
+
*/
|
|
366
|
+
private hydratePlans;
|
|
367
|
+
/**
|
|
368
|
+
* Internal: hydrate a single plan resource with its features and limits.
|
|
369
|
+
* Features and limits are cached since they never change.
|
|
370
|
+
*/
|
|
371
|
+
private hydrateSinglePlan;
|
|
372
|
+
/**
|
|
373
|
+
* Internal: map a subscribed_feature JSON:API document to SubscribedFeature[].
|
|
374
|
+
*/
|
|
375
|
+
private mapFeatures;
|
|
376
|
+
/**
|
|
377
|
+
* Internal: map a subscribed_limit JSON:API document to SubscribedLimit[].
|
|
378
|
+
*/
|
|
379
|
+
private mapLimits;
|
|
380
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
* SubscribedPlanManager - High-level manager for subscribed plans with features and limits
|
|
5
|
+
*
|
|
6
|
+
* This manager wires together the three JSON:API clients (SSISubscribedPlanApi,
|
|
7
|
+
* SSISubscribedFeatureApi, SSISubscribedLimitApi) and provides a convenient interface
|
|
8
|
+
* for fetching subscribed plans with their associated features and limits already attached.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const domain = 'https://account.serialsubscriptionsdev.com';
|
|
14
|
+
* const manager = new SubscribedPlanManager(domain);
|
|
15
|
+
*
|
|
16
|
+
* // Optional: set OAuth/JWT Bearer token if needed
|
|
17
|
+
* manager.setBearerToken('YOUR_ACCESS_TOKEN');
|
|
18
|
+
*
|
|
19
|
+
* // Get all plans with features and limits
|
|
20
|
+
* const allPlans = await manager.getAllPlans();
|
|
21
|
+
*
|
|
22
|
+
* // Get a single plan by internal numeric ID
|
|
23
|
+
* const plan = await manager.getPlanById(1);
|
|
24
|
+
*
|
|
25
|
+
* // Get plans by project ID
|
|
26
|
+
* const projectPlans = await manager.getPlansByProjectId(42);
|
|
27
|
+
*
|
|
28
|
+
* // Get plans by user internal ID
|
|
29
|
+
* const userPlans = await manager.getPlansByUserId(3);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.SubscribedPlanManager = void 0;
|
|
34
|
+
const SSISubscribedPlanApi_1 = require("./SSISubscribedPlanApi");
|
|
35
|
+
const SSISubscribedFeatureApi_1 = require("./SSISubscribedFeatureApi");
|
|
36
|
+
const SSISubscribedLimitApi_1 = require("./SSISubscribedLimitApi");
|
|
37
|
+
const SSICache_1 = require("./cache/SSICache");
|
|
38
|
+
/**
|
|
39
|
+
* SubscribedPlanManager
|
|
40
|
+
*
|
|
41
|
+
* Fetches subscribed plans and hydrates each with its subscribed features and limits.
|
|
42
|
+
*
|
|
43
|
+
* Filtering conventions (using internal integer IDs):
|
|
44
|
+
* - By plan ID: filter[drupal_internal__id]={subscribed_plan_int_id}
|
|
45
|
+
* - By project ID: filter[project_id]={project_int_id}
|
|
46
|
+
* - By user ID: filter[user_id.meta.drupal_internal__target_id]={user_int_id}
|
|
47
|
+
*
|
|
48
|
+
* For features/limits attached to a plan:
|
|
49
|
+
* - filter[subscribed_plan_id.meta.drupal_internal__target_id]={subscribed_plan_int_id}
|
|
50
|
+
*/
|
|
51
|
+
class SubscribedPlanManager {
|
|
52
|
+
constructor(domain, options = {}) {
|
|
53
|
+
const mkOptions = (apiBasePath) => apiBasePath || options.timeout
|
|
54
|
+
? {
|
|
55
|
+
apiBasePath,
|
|
56
|
+
timeout: options.timeout,
|
|
57
|
+
}
|
|
58
|
+
: undefined;
|
|
59
|
+
this.planApi = new SSISubscribedPlanApi_1.SSISubscribedPlanApi(domain, mkOptions(options.planApiBasePath));
|
|
60
|
+
this.featureApi = new SSISubscribedFeatureApi_1.SSISubscribedFeatureApi(domain, mkOptions(options.featureApiBasePath));
|
|
61
|
+
this.limitApi = new SSISubscribedLimitApi_1.SSISubscribedLimitApi(domain, mkOptions(options.limitApiBasePath));
|
|
62
|
+
// Initialize cache instances with prefixes for features and limits
|
|
63
|
+
const baseCache = SSICache_1.SSICache.fromEnv();
|
|
64
|
+
this.featureCache = baseCache.withPrefix('features');
|
|
65
|
+
this.limitCache = baseCache.withPrefix('limits');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Set a shared Bearer token on all three underlying API clients.
|
|
69
|
+
*/
|
|
70
|
+
setBearerToken(token) {
|
|
71
|
+
this.planApi.setBearerToken(token);
|
|
72
|
+
this.featureApi.setBearerToken(token);
|
|
73
|
+
this.limitApi.setBearerToken(token);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get *all* subscribed plans for the current context,
|
|
77
|
+
* each with its features[] and limits[].
|
|
78
|
+
*/
|
|
79
|
+
async getAllPlans() {
|
|
80
|
+
const doc = await this.planApi.list();
|
|
81
|
+
return this.hydratePlans(doc);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get a single subscribed plan by its internal numeric ID
|
|
85
|
+
* (attributes.drupal_internal__id), with features[] and limits[].
|
|
86
|
+
*/
|
|
87
|
+
async getPlanById(subscribedPlanId) {
|
|
88
|
+
const doc = await this.planApi.list({
|
|
89
|
+
filters: {
|
|
90
|
+
// ?filter[drupal_internal__id]={subscribedPlanId}
|
|
91
|
+
drupal_internal__id: subscribedPlanId,
|
|
92
|
+
},
|
|
93
|
+
pagination: {
|
|
94
|
+
limit: 1,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
const plans = await this.hydratePlans(doc);
|
|
98
|
+
return plans[0] ?? null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get all subscribed plans for a given project_id.
|
|
102
|
+
*
|
|
103
|
+
* Uses: ?filter[project_id]={project_int_id}
|
|
104
|
+
*/
|
|
105
|
+
async getPlansByProjectId(projectId) {
|
|
106
|
+
const doc = await this.planApi.list({
|
|
107
|
+
filters: {
|
|
108
|
+
project_id: projectId,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
return this.hydratePlans(doc);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Filter an array of plans by project_id.
|
|
115
|
+
*
|
|
116
|
+
* @param plans - Array of subscribed plans to filter.
|
|
117
|
+
* @param projectId - The project ID to filter by.
|
|
118
|
+
* @returns Array of plans that match the project ID.
|
|
119
|
+
*/
|
|
120
|
+
getPlanByProjectId(plans, projectId) {
|
|
121
|
+
return plans.filter((plan) => plan.project_id === projectId);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get all subscribed plans for a given user (internal user ID).
|
|
125
|
+
*
|
|
126
|
+
* Uses: ?filter[user_id.meta.drupal_internal__target_id]={user_int_id}
|
|
127
|
+
*/
|
|
128
|
+
async getPlansByUserId(userId) {
|
|
129
|
+
const doc = await this.planApi.list({
|
|
130
|
+
filters: {
|
|
131
|
+
'user_id.meta.drupal_internal__target_id': userId,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return this.hydratePlans(doc);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get the internal numeric ID of a limit by its name from a plan.
|
|
138
|
+
*
|
|
139
|
+
* @param plan - The subscribed plan to search in.
|
|
140
|
+
* @param limit_name - The name of the limit to find.
|
|
141
|
+
* @returns The internal numeric ID (drupal_internal__id) of the limit, or null if not found.
|
|
142
|
+
*/
|
|
143
|
+
getLimitId(plan, limit_name) {
|
|
144
|
+
const limit = plan.limits.find((l) => l.limit_name === limit_name);
|
|
145
|
+
return limit ? limit.drupal_internal__id : null;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Internal helper to find a limit by name or ID.
|
|
149
|
+
*/
|
|
150
|
+
findLimit(plan, identifier) {
|
|
151
|
+
if (typeof identifier === 'string') {
|
|
152
|
+
return plan.limits.find((l) => l.limit_name === identifier) ?? null;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return plan.limits.find((l) => l.drupal_internal__id === identifier) ?? null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
isLimitEnabled(plan, identifier) {
|
|
159
|
+
const limit = this.findLimit(plan, identifier);
|
|
160
|
+
return limit?.status ?? false;
|
|
161
|
+
}
|
|
162
|
+
getLimitType(plan, identifier) {
|
|
163
|
+
const limit = this.findLimit(plan, identifier);
|
|
164
|
+
return limit?.limit_type ?? null;
|
|
165
|
+
}
|
|
166
|
+
getLimitMax(plan, identifier) {
|
|
167
|
+
const limit = this.findLimit(plan, identifier);
|
|
168
|
+
return limit?.limit_value ?? null;
|
|
169
|
+
}
|
|
170
|
+
limitAllowsOverage(plan, identifier) {
|
|
171
|
+
const limit = this.findLimit(plan, identifier);
|
|
172
|
+
return limit?.allow_overage ?? false;
|
|
173
|
+
}
|
|
174
|
+
limitOverageMax(plan, identifier) {
|
|
175
|
+
const limit = this.findLimit(plan, identifier);
|
|
176
|
+
if (!limit || !limit.allow_overage) {
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
179
|
+
return limit.overage_limit;
|
|
180
|
+
}
|
|
181
|
+
getLimitUnits(plan, identifier) {
|
|
182
|
+
const limit = this.findLimit(plan, identifier);
|
|
183
|
+
return limit?.limit_units ?? null;
|
|
184
|
+
}
|
|
185
|
+
getLimitUnitsPlural(plan, identifier) {
|
|
186
|
+
const limit = this.findLimit(plan, identifier);
|
|
187
|
+
return limit?.limit_units_plural ?? null;
|
|
188
|
+
}
|
|
189
|
+
getLimitPeriod(plan, identifier) {
|
|
190
|
+
const limit = this.findLimit(plan, identifier);
|
|
191
|
+
return limit?.limit_period ?? null;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Internal: turn a JSON:API document of subscribed plans into hydrated SubscribedPlan[]
|
|
195
|
+
* with features[] and limits[] attached.
|
|
196
|
+
*/
|
|
197
|
+
async hydratePlans(doc) {
|
|
198
|
+
const data = Array.isArray(doc.data) ? doc.data : doc.data ? [doc.data] : [];
|
|
199
|
+
const plans = data.filter((resource) => resource.type === 'subscribed_plan--subscribed_plan');
|
|
200
|
+
const hydrated = [];
|
|
201
|
+
for (const planResource of plans) {
|
|
202
|
+
hydrated.push(await this.hydrateSinglePlan(planResource));
|
|
203
|
+
}
|
|
204
|
+
return hydrated;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Internal: hydrate a single plan resource with its features and limits.
|
|
208
|
+
* Features and limits are cached since they never change.
|
|
209
|
+
*/
|
|
210
|
+
async hydrateSinglePlan(planResource) {
|
|
211
|
+
const attributes = (planResource.attributes || {});
|
|
212
|
+
const internalId = attributes.drupal_internal__id;
|
|
213
|
+
if (typeof internalId !== 'number') {
|
|
214
|
+
throw new Error('Subscribed plan is missing attributes.drupal_internal__id (required for feature/limit lookups).');
|
|
215
|
+
}
|
|
216
|
+
// Cache key format: plan:{internalId}
|
|
217
|
+
const featureCacheKey = `plan:${internalId}`;
|
|
218
|
+
const limitCacheKey = `plan:${internalId}`;
|
|
219
|
+
// Fetch features and limits in parallel, using cache with remember() pattern
|
|
220
|
+
const [features, limits] = await Promise.all([
|
|
221
|
+
this.featureCache.remember(featureCacheKey, SubscribedPlanManager.CACHE_TTL_SEC, async () => {
|
|
222
|
+
const featuresDoc = await this.featureApi.list({
|
|
223
|
+
filters: {
|
|
224
|
+
// ?filter[subscribed_plan_id.meta.drupal_internal__target_id]={internalId}
|
|
225
|
+
'subscribed_plan_id.meta.drupal_internal__target_id': internalId,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
return this.mapFeatures(featuresDoc);
|
|
229
|
+
}),
|
|
230
|
+
this.limitCache.remember(limitCacheKey, SubscribedPlanManager.CACHE_TTL_SEC, async () => {
|
|
231
|
+
const limitsDoc = await this.limitApi.list({
|
|
232
|
+
filters: {
|
|
233
|
+
'subscribed_plan_id.meta.drupal_internal__target_id': internalId,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
return this.mapLimits(limitsDoc);
|
|
237
|
+
}),
|
|
238
|
+
]);
|
|
239
|
+
const uuid = planResource.id ?? '';
|
|
240
|
+
const plan = {
|
|
241
|
+
uuid,
|
|
242
|
+
...attributes,
|
|
243
|
+
relationships: planResource.relationships,
|
|
244
|
+
features,
|
|
245
|
+
limits,
|
|
246
|
+
};
|
|
247
|
+
return plan;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Internal: map a subscribed_feature JSON:API document to SubscribedFeature[].
|
|
251
|
+
*/
|
|
252
|
+
mapFeatures(doc) {
|
|
253
|
+
const data = Array.isArray(doc.data) ? doc.data : doc.data ? [doc.data] : [];
|
|
254
|
+
return data
|
|
255
|
+
.filter((resource) => resource.type === 'subscribed_feature--subscribed_feature')
|
|
256
|
+
.map((resource) => {
|
|
257
|
+
const attrs = (resource.attributes || {});
|
|
258
|
+
const uuid = resource.id ?? '';
|
|
259
|
+
const feature = {
|
|
260
|
+
uuid,
|
|
261
|
+
...attrs,
|
|
262
|
+
relationships: resource.relationships,
|
|
263
|
+
};
|
|
264
|
+
return feature;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Internal: map a subscribed_limit JSON:API document to SubscribedLimit[].
|
|
269
|
+
*/
|
|
270
|
+
mapLimits(doc) {
|
|
271
|
+
const data = Array.isArray(doc.data) ? doc.data : doc.data ? [doc.data] : [];
|
|
272
|
+
return data
|
|
273
|
+
.filter((resource) => resource.type === 'subscribed_limit--subscribed_limit')
|
|
274
|
+
.map((resource) => {
|
|
275
|
+
const attrs = (resource.attributes || {});
|
|
276
|
+
const uuid = resource.id ?? '';
|
|
277
|
+
const limit = {
|
|
278
|
+
uuid,
|
|
279
|
+
...attrs,
|
|
280
|
+
relationships: resource.relationships,
|
|
281
|
+
};
|
|
282
|
+
return limit;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
exports.SubscribedPlanManager = SubscribedPlanManager;
|
|
287
|
+
// Cache TTL: 24 hours (86400 seconds) - features and limits never change
|
|
288
|
+
SubscribedPlanManager.CACHE_TTL_SEC = 86400;
|