Skip to content

Commit 0694f71

Browse files
committed
server: release createSubscription db client on validation errors
1 parent fa5a76f commit 0694f71

File tree

1 file changed

+62
-55
lines changed

1 file changed

+62
-55
lines changed

src/packages/server/purchases/create-subscription.ts

Lines changed: 62 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,64 +16,71 @@ export default async function createSubscription(
1616
opts: Options,
1717
client: PoolClient | null // useful to allow null for unit testing, but must be explicit
1818
): Promise<number> {
19-
const db = client ?? (await getPoolClient());
19+
let db: PoolClient | null = client;
2020
// some consistency checks below. It's very likely this should always hold,
2121
// since data isn't user supplied, but it's still good to be careful.
2222

23-
if (!(await isValidAccount(opts.account_id))) {
24-
throw Error("account_id must be valid");
25-
}
26-
const costValue = toDecimal(opts.cost);
27-
if (costValue.eq(0) || costValue.lt(0)) {
28-
throw Error("cost must be positive");
29-
}
30-
if (opts.interval != "month" && opts.interval != "year") {
31-
throw Error("interval must be month or year");
32-
}
33-
if (!isDate(opts.current_period_start)) {
34-
throw Error("current_period_start must be a Date");
35-
}
36-
if (!isDate(opts.current_period_end)) {
37-
throw Error("current_period_end must be a Date");
38-
}
39-
if (opts.current_period_start >= opts.current_period_end) {
40-
throw Error("start must be before end");
41-
}
42-
if (
43-
opts.latest_purchase_id != null &&
44-
(!is_integer(opts.latest_purchase_id) || opts.latest_purchase_id < 0)
45-
) {
46-
throw Error(
47-
"if specified, latest_purchase_id must be a nonnegative integer"
48-
);
49-
}
50-
const metadata = opts.metadata as Metadata;
51-
const metadataType = (opts.metadata as { type?: string })?.type;
52-
if (typeof metadata != "object" || !metadataType) {
53-
throw Error("metadata must be a nontrivial object with type field");
54-
}
55-
if (metadataType != "membership") {
56-
throw Error(`unsupported subscription metadata type "${metadataType}"`);
57-
}
58-
if (metadataType == "membership" && !(metadata as MembershipMetadata).class) {
59-
throw Error("membership metadata must include class");
60-
}
23+
try {
24+
if (!(await isValidAccount(opts.account_id))) {
25+
throw Error("account_id must be valid");
26+
}
27+
const costValue = toDecimal(opts.cost);
28+
if (costValue.eq(0) || costValue.lt(0)) {
29+
throw Error("cost must be positive");
30+
}
31+
if (opts.interval != "month" && opts.interval != "year") {
32+
throw Error("interval must be month or year");
33+
}
34+
if (!isDate(opts.current_period_start)) {
35+
throw Error("current_period_start must be a Date");
36+
}
37+
if (!isDate(opts.current_period_end)) {
38+
throw Error("current_period_end must be a Date");
39+
}
40+
if (opts.current_period_start >= opts.current_period_end) {
41+
throw Error("start must be before end");
42+
}
43+
if (
44+
opts.latest_purchase_id != null &&
45+
(!is_integer(opts.latest_purchase_id) || opts.latest_purchase_id < 0)
46+
) {
47+
throw Error(
48+
"if specified, latest_purchase_id must be a nonnegative integer"
49+
);
50+
}
51+
const metadata = opts.metadata as Metadata;
52+
const metadataType = (opts.metadata as { type?: string })?.type;
53+
if (typeof metadata != "object" || !metadataType) {
54+
throw Error("metadata must be a nontrivial object with type field");
55+
}
56+
if (metadataType != "membership") {
57+
throw Error(`unsupported subscription metadata type "${metadataType}"`);
58+
}
59+
if (
60+
metadataType == "membership" &&
61+
!(metadata as MembershipMetadata).class
62+
) {
63+
throw Error("membership metadata must include class");
64+
}
6165

62-
const { rows } = await db.query(
63-
"INSERT INTO subscriptions (account_id,created,cost,interval,current_period_start,current_period_end,latest_purchase_id,status,metadata) VALUES($1,NOW(),$2,$3,$4,$5,$6,'active',$7) RETURNING id",
64-
[
65-
opts.account_id,
66-
moneyToDbString(costValue),
67-
opts.interval,
68-
opts.current_period_start,
69-
opts.current_period_end,
70-
opts.latest_purchase_id,
71-
opts.metadata,
72-
]
73-
);
74-
const { id } = rows[0];
75-
if (client == null) {
76-
db.release();
66+
db = db ?? (await getPoolClient());
67+
const { rows } = await db.query(
68+
"INSERT INTO subscriptions (account_id,created,cost,interval,current_period_start,current_period_end,latest_purchase_id,status,metadata) VALUES($1,NOW(),$2,$3,$4,$5,$6,'active',$7) RETURNING id",
69+
[
70+
opts.account_id,
71+
moneyToDbString(costValue),
72+
opts.interval,
73+
opts.current_period_start,
74+
opts.current_period_end,
75+
opts.latest_purchase_id,
76+
opts.metadata,
77+
]
78+
);
79+
const { id } = rows[0];
80+
return id;
81+
} finally {
82+
if (client == null && db != null) {
83+
db.release();
84+
}
7785
}
78-
return id;
7986
}

0 commit comments

Comments
 (0)