Skip to content

Commit fc2bb7f

Browse files
feat: add nestjs-chargebee package (ENG-740) (#9)
1 parent f344919 commit fc2bb7f

File tree

67 files changed

+2645
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2645
-2
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createConfig } from '@voiceflow/dependency-cruiser-config';
2+
3+
export default createConfig();

libs/nestjs-chargebee/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# NestJS Chargebee

libs/nestjs-chargebee/package.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "@voiceflow/nestjs-chargebee",
3+
"version": "2.0.0",
4+
"repository": {
5+
"type": "git",
6+
"url": "git+https://github.com/voiceflow/oss.git"
7+
},
8+
"license": "MIT",
9+
"exports": {
10+
".": {
11+
"types": "./build/esm/main.d.ts",
12+
"import": "./build/esm/main.js",
13+
"default": "./build/cjs/main.js"
14+
},
15+
"./models": {
16+
"types": "./build/esm/models/mod.d.ts",
17+
"import": "./build/esm/models/mod.js",
18+
"default": "./build/cjs/models/mod.js"
19+
}
20+
},
21+
"main": "build/cjs/main.js",
22+
"types": "build/esm/main.d.ts",
23+
"files": [
24+
"build"
25+
],
26+
"scripts": {
27+
"build": "yarn g:turbo run build:cmd --filter=@voiceflow/nestjs-chargebee...",
28+
"build:cjs": "yarn g:build:pkg cjs",
29+
"build:cmd": "yarn g:run-p build:cjs build:esm",
30+
"build:esm": "yarn g:build:pkg esm",
31+
"clean": "yarn g:rimraf build",
32+
"lint": "yarn g:run-p -c lint:eslint lint:prettier",
33+
"lint:eslint": "yarn g:eslint",
34+
"lint:fix": "yarn g:run-p -c \"lint:eslint --fix\" \"lint:prettier --write\"",
35+
"lint:prettier": "yarn g:prettier --check",
36+
"test": "yarn g:run-p -c test:dependencies test:types",
37+
"test:dependencies": "yarn g:depcruise",
38+
"test:types": "yarn g:tsc --noEmit"
39+
},
40+
"dependencies": {
41+
"chargebee-typescript": "2.44.0"
42+
},
43+
"devDependencies": {
44+
"@nestjs/common": "10.1.3",
45+
"@nestjs/core": "10.1.3",
46+
"rxjs": "7.8.1"
47+
},
48+
"peerDependencies": {
49+
"@nestjs/common": "^10",
50+
"rxjs": "^7"
51+
},
52+
"engines": {
53+
"node": "20"
54+
},
55+
"volta": {
56+
"extends": "../../package.json"
57+
}
58+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
sonar.projectName=oss-bidirectional-adapter
2+
sonar.sources=src/
3+
sonar.tests=src/
4+
sonar.exclusions=src/**/*.test.ts
5+
sonar.test.inclusions=src/**/*.test.ts
6+
sonar.cpd.exclusions=src/**/*.test.ts
7+
sonar.typescript.tsconfigPath=tsconfig.json
8+
sonar.javascript.lcov.reportPaths=sonar/coverage/lcov.info
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import type { ChargebeeModuleOptions } from './chargebee.interface';
2+
import { AddonResource } from './resources/addon-resource';
3+
import { AddressResource } from './resources/address-resource';
4+
import { AttachedItemResource } from './resources/attached-item-resource';
5+
import { CardResource } from './resources/card-resource';
6+
import { CommentResource } from './resources/comment-resource';
7+
import { CouponCodeResource } from './resources/coupon-code-resource';
8+
import { CouponResource } from './resources/coupon-resource';
9+
import { CouponSetResource } from './resources/coupon-set-resource';
10+
import { CreditNoteResource } from './resources/credit-note-resource';
11+
import { CustomerResource } from './resources/customer-resource';
12+
import { DifferentialPriceResource } from './resources/differential-price-resource';
13+
import { DownloadResource } from './resources/download.resource';
14+
import { EntitlementOverrideResource } from './resources/entitlement-override-resource';
15+
import { EntitlementResource } from './resources/entitlement-resource';
16+
import { EstimateResource } from './resources/estimate-resource';
17+
import { ExportResource } from './resources/export-resource';
18+
import { FeatureResource } from './resources/feature-resource';
19+
import { GiftResource } from './resources/gift-resource';
20+
import { HostedPageResource } from './resources/hosted-page-resource';
21+
import { InAppSubscriptionResource } from './resources/in-app-subscription-resource';
22+
import { InvoiceResource } from './resources/invoice-resource';
23+
import { ItemEntitlementResource } from './resources/item-entitlement-resource';
24+
import { ItemFamilyResource } from './resources/item-family-resource';
25+
import { ItemPriceResource } from './resources/item-price-resource';
26+
import { ItemResource } from './resources/item-resource';
27+
import { NonSubscriptionResource } from './resources/non-subscription-resource';
28+
import { OrderResource } from './resources/order-resource';
29+
import { PaymentIntentResource } from './resources/payment-intent-resource';
30+
import { PaymentSourceResource } from './resources/payment-source-resource';
31+
import { PaymentVoucherResource } from './resources/payment-voucher-resource';
32+
import { PlanResource } from './resources/plan-resource';
33+
import { PortalSessionResource } from './resources/portal-session-resource';
34+
import { PromotionalCreditResource } from './resources/promotional-credit-resource';
35+
import { PurchaseResource } from './resources/purchase-resource';
36+
import { QuoteResource } from './resources/quote-resource';
37+
import { ResourceMigrationResource } from './resources/resource-migration-resource';
38+
import { SiteMigrationDetailResource } from './resources/site-migration-detail-resource';
39+
import { SubscriptionEntitlementResource } from './resources/subscription-entitlement-resource';
40+
import { SubscriptionResource } from './resources/subscription-resource';
41+
import { TimeMachineResource } from './resources/time-machine-resource';
42+
import { TransactionResource } from './resources/transaction-resource';
43+
import { UnbilledChargeResource } from './resources/unbilled-charge-resource';
44+
import { UsageResource } from './resources/usage-resource';
45+
import { VirtualBankAccountResource } from './resources/virtual-bank-account-resource';
46+
47+
export class ChargebeeResourceWrapper {
48+
constructor(private readonly options: ChargebeeModuleOptions) {}
49+
50+
subscription = new SubscriptionResource(this.options);
51+
52+
customer = new CustomerResource(this.options);
53+
54+
paymentSource = new PaymentSourceResource(this.options);
55+
56+
virtualBankAccount = new VirtualBankAccountResource(this.options);
57+
58+
card = new CardResource(this.options);
59+
60+
promotionalCredit = new PromotionalCreditResource(this.options);
61+
62+
invoice = new InvoiceResource(this.options);
63+
64+
creditNote = new CreditNoteResource(this.options);
65+
66+
unbilledCharge = new UnbilledChargeResource(this.options);
67+
68+
order = new OrderResource(this.options);
69+
70+
gift = new GiftResource(this.options);
71+
72+
transaction = new TransactionResource(this.options);
73+
74+
hostedPage = new HostedPageResource(this.options);
75+
76+
estimate = new EstimateResource(this.options);
77+
78+
quote = new QuoteResource(this.options);
79+
80+
plan = new PlanResource(this.options);
81+
82+
addon = new AddonResource(this.options);
83+
84+
coupon = new CouponResource(this.options);
85+
86+
couponSet = new CouponSetResource(this.options);
87+
88+
couponCode = new CouponCodeResource(this.options);
89+
90+
address = new AddressResource(this.options);
91+
92+
usage = new UsageResource(this.options);
93+
94+
comment = new CommentResource(this.options);
95+
96+
portalSession = new PortalSessionResource(this.options);
97+
98+
siteMigrationDetail = new SiteMigrationDetailResource(this.options);
99+
100+
resourceMigration = new ResourceMigrationResource(this.options);
101+
102+
timeMachine = new TimeMachineResource(this.options);
103+
104+
export = new ExportResource(this.options);
105+
106+
paymentIntent = new PaymentIntentResource(this.options);
107+
108+
itemFamily = new ItemFamilyResource(this.options);
109+
110+
item = new ItemResource(this.options);
111+
112+
itemPrice = new ItemPriceResource(this.options);
113+
114+
attachedItem = new AttachedItemResource(this.options);
115+
116+
differentialPrice = new DifferentialPriceResource(this.options);
117+
118+
feature = new FeatureResource(this.options);
119+
120+
subscriptionEntitlement = new SubscriptionEntitlementResource(this.options);
121+
122+
itemEntitlement = new ItemEntitlementResource(this.options);
123+
124+
inAppSubscription = new InAppSubscriptionResource(this.options);
125+
126+
nonSubscription = new NonSubscriptionResource(this.options);
127+
128+
entitlement = new EntitlementResource(this.options);
129+
130+
entitlementOverride = new EntitlementOverrideResource(this.options);
131+
132+
purchase = new PurchaseResource(this.options);
133+
134+
paymentVoucher = new PaymentVoucherResource(this.options);
135+
136+
download = new DownloadResource(this.options);
137+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { ChargeBee } from 'chargebee-typescript';
2+
import type { ListResult } from 'chargebee-typescript/lib/list_result';
3+
import type { RequestWrapper } from 'chargebee-typescript/lib/request_wrapper';
4+
import type { Result } from 'chargebee-typescript/lib/result';
5+
6+
import type { ChargebeeModuleOptions } from './chargebee.interface';
7+
import { configureChargebee } from './chargebee.utils';
8+
import {
9+
isListOffsetOption,
10+
type ListResultMethodName,
11+
type ProcessWaitMethodName,
12+
type ResolveResultReturn,
13+
type ResourceResult,
14+
type ResultMethodName,
15+
} from './chargebee-resource.types';
16+
17+
export class ChargebeeResource {
18+
protected readonly chargebee = new ChargeBee();
19+
20+
constructor(protected readonly options: ChargebeeModuleOptions) {}
21+
22+
protected request<
23+
TResourceName extends keyof ChargeBee,
24+
TMethodName extends keyof ChargeBee[TResourceName],
25+
TReturning extends ResourceResult,
26+
>(
27+
resourceName: TResourceName,
28+
methodName: TMethodName extends
29+
| ResultMethodName<TResourceName, TMethodName>
30+
| ProcessWaitMethodName<TResourceName, TMethodName>
31+
? TMethodName
32+
: never,
33+
returning: TReturning
34+
) {
35+
type MethodDefinition = ChargeBee[TResourceName][TMethodName] extends (...args: unknown[]) => RequestWrapper<Result>
36+
? ChargeBee[TResourceName][TMethodName]
37+
: never;
38+
39+
const functionDef = this.chargebee[resourceName][methodName] as MethodDefinition;
40+
41+
return async (...args: Parameters<MethodDefinition>) => {
42+
configureChargebee(this.chargebee, this.options);
43+
return functionDef(...args)
44+
.request()
45+
.then(this.resolveResult(returning));
46+
};
47+
}
48+
49+
protected listRequest<
50+
TResourceName extends keyof ChargeBee,
51+
TMethodName extends keyof ChargeBee[TResourceName],
52+
TReturning extends ResourceResult,
53+
>(
54+
resourceName: TResourceName,
55+
methodName: TMethodName extends ListResultMethodName<TResourceName, TMethodName> ? TMethodName : never,
56+
returning: TReturning
57+
) {
58+
type MethodDefinition = ChargeBee[TResourceName][TMethodName] extends (...args: any[]) => RequestWrapper<ListResult>
59+
? ChargeBee[TResourceName][TMethodName]
60+
: never;
61+
62+
const functionDef = this.chargebee[resourceName][methodName] as MethodDefinition;
63+
64+
const method = async (...args: Parameters<MethodDefinition>) => {
65+
configureChargebee(this.chargebee, this.options);
66+
return functionDef(...args)
67+
.request()
68+
.then((listResult: ListResult) => {
69+
const items = listResult.list.map(this.resolveResult(returning));
70+
return {
71+
items,
72+
nextOffset: listResult.next_offset as string | undefined,
73+
};
74+
});
75+
};
76+
77+
const iterate = async function* (...args: Parameters<typeof method>) {
78+
let offset = args.find(isListOffsetOption)?.offset;
79+
80+
do {
81+
// eslint-disable-next-line no-loop-func
82+
const forwardArgs = args.map((arg) => Object.assign(arg, { offset })) as Parameters<typeof functionDef>;
83+
84+
// eslint-disable-next-line no-await-in-loop
85+
const listResult = await method(...forwardArgs);
86+
yield* listResult.items;
87+
offset = listResult.nextOffset;
88+
} while (offset);
89+
};
90+
91+
const all = async (...args: Parameters<typeof method>) => {
92+
const items: ResolveResultReturn<TReturning>[] = [];
93+
94+
for await (const result of iterate(...args)) {
95+
items.push(result);
96+
}
97+
98+
return items;
99+
};
100+
101+
return Object.assign(method, { all, iterate });
102+
}
103+
104+
private resolveResult =
105+
<TReturning extends ResourceResult>(returning: TReturning) =>
106+
(result: Result) =>
107+
Object.fromEntries(
108+
Object.keys(returning).map((key) => [key, result[key as keyof Result]])
109+
) as ResolveResultReturn<TReturning>;
110+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { ChargeBee } from 'chargebee-typescript';
2+
import type { ListResult } from 'chargebee-typescript/lib/list_result';
3+
import type { ProcessWait } from 'chargebee-typescript/lib/process_wait';
4+
import type { RequestWrapper } from 'chargebee-typescript/lib/request_wrapper';
5+
import type { Result } from 'chargebee-typescript/lib/result';
6+
7+
export type ResultMethodName<
8+
TResource extends keyof ChargeBee,
9+
TMethod extends keyof ChargeBee[TResource],
10+
> = ChargeBee[TResource][TMethod] extends (...args: any[]) => RequestWrapper<infer R>
11+
? R extends Result
12+
? TMethod
13+
: 'Method must return a RequestWrapper<Result>'
14+
: never;
15+
16+
export type ListResultMethodName<
17+
TResource extends keyof ChargeBee,
18+
TMethod extends keyof ChargeBee[TResource],
19+
> = ChargeBee[TResource][TMethod] extends (...args: any[]) => RequestWrapper<infer R>
20+
? R extends ListResult
21+
? TMethod
22+
: 'Method must return a RequestWrapper<ListResult>'
23+
: never;
24+
25+
export type ProcessWaitMethodName<
26+
TResource extends keyof ChargeBee,
27+
TMethod extends keyof ChargeBee[TResource],
28+
> = ChargeBee[TResource][TMethod] extends (...args: any[]) => ProcessWait
29+
? TMethod
30+
: 'Method must return a ProcessWait';
31+
32+
export type ResourceResult = {
33+
[K in keyof Result]?: {
34+
optional: boolean;
35+
};
36+
};
37+
38+
export type ResolveResultReturn<T extends ResourceResult> = {
39+
[K in keyof T]: K extends keyof Result
40+
? T[K] extends { optional: true }
41+
? Result[K] | undefined
42+
: Result[K]
43+
: never;
44+
};
45+
46+
export const isListOffsetOption = (arg: unknown): arg is { offset: string } =>
47+
!!arg && typeof arg === 'object' && 'offset' in arg && typeof arg.offset === 'string';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface ChargebeeModuleOptions {
2+
site: string;
3+
apiKey: string;
4+
override?: {
5+
url?: string;
6+
timeout?: number;
7+
};
8+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ConfigurableModuleBuilder } from '@nestjs/common';
2+
3+
import type { ChargebeeModuleOptions } from './chargebee.interface';
4+
5+
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN: CHARGEBEE_MODULE_OPTIONS_TOKEN } =
6+
new ConfigurableModuleBuilder<ChargebeeModuleOptions>()
7+
.setExtras(
8+
{
9+
isGlobal: true,
10+
},
11+
(definition, extras) => ({
12+
...definition,
13+
global: extras.isGlobal,
14+
})
15+
)
16+
.build();

0 commit comments

Comments
 (0)