Skip to content

Commit 58cec2f

Browse files
OpenAPI docs: Added schema for /shopping/cart/add endpoint.
1 parent e1765fc commit 58cec2f

File tree

2 files changed

+218
-1
lines changed
  • src/packages/next
    • lib/api/schema/shopping/cart
    • pages/api/v2/shopping/cart

2 files changed

+218
-1
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import { z } from "../../../framework";
2+
3+
import { FailedAPIOperationSchema, OkAPIOperationSchema } from "../../common";
4+
5+
import { ProjectIdSchema } from "../../projects/common";
6+
7+
const LicenseRangeSchema = z
8+
.array(z.string())
9+
.length(2)
10+
.describe(
11+
`Array of two ISO 8601-formatted timestamps. The first element indicates the start
12+
date of the license, and the second indicates the end date. Used when the \`period\`
13+
field is set to \`range\`.`,
14+
);
15+
16+
const LicenseTitleSchema = z
17+
.string()
18+
.describe("Semantic license title.")
19+
.nullish();
20+
21+
const LicenseDescriptionSchema = z
22+
.string()
23+
.describe("Semantic license description")
24+
.nullish();
25+
26+
// OpenAPI spec
27+
//
28+
export const ShoppingCartAddInputSchema = z
29+
.object({
30+
project_id: ProjectIdSchema.nullish().describe(
31+
"If specified, this license is automatically added to an existing project.",
32+
),
33+
id: z
34+
.number()
35+
.min(0)
36+
.describe(
37+
`Existing shopping cart item id. If \`purchased\` is true, this puts a new copy
38+
of the purchased item in the cart. Otherwise, this adds an item to the cart that
39+
was saved for later. If this parameter is not specified, the \`product\` field
40+
must be populated.`,
41+
)
42+
.nullish(),
43+
product: z
44+
.enum(["site-license", "cash-voucher"])
45+
.describe(
46+
"Product type to purchase. Must be populated if the `id` field is empty.",
47+
)
48+
.nullish(),
49+
description: z
50+
.union([
51+
z
52+
.object({
53+
title: LicenseTitleSchema,
54+
description: LicenseDescriptionSchema,
55+
range: LicenseRangeSchema,
56+
period: z.enum(["range", "monthly", "yearly"]).describe(
57+
`Period for which this license is to be applied. If \`range\` is selected,
58+
the \`range\` field must be populated in this request.`,
59+
),
60+
type: z.enum(["quota"]).describe("License type"),
61+
user: z.enum(["academic", "business"]).describe("User type."),
62+
run_limit: z
63+
.number()
64+
.min(0)
65+
.describe(
66+
"Number of projects which may simultaneously use this license",
67+
),
68+
always_running: z
69+
.boolean()
70+
.nullish()
71+
.describe(
72+
`Indicates whether the project(s) this license is applied to should be
73+
allowed to always be running.`,
74+
),
75+
ram: z
76+
.number()
77+
.min(1)
78+
.describe(
79+
"Limits the total memory a project can use. At least 2GB is recommended.",
80+
),
81+
cpu: z
82+
.number()
83+
.min(1)
84+
.describe(
85+
"Limits the total number of vCPUs allocated to a project.",
86+
),
87+
disk: z
88+
.number()
89+
.min(1)
90+
.describe(
91+
`Disk size in GB to be allocated to the project to which this license is
92+
applied.`,
93+
),
94+
member: z.boolean().describe(
95+
`Member hosting significantly reduces competition for resources, and we
96+
prioritize support requests much higher. _Please be aware: licenses of
97+
different member hosting service levels cannot be combined!_`,
98+
),
99+
uptime: z
100+
.enum(["short", "medium", "day", "always_running"])
101+
.describe(
102+
`Determines how long a project runs while not being used before being
103+
automatically stopped. A \`short\` value corresponds to a 30-minute
104+
timeout, and a \`medium\` value to a 2-hour timeout.`,
105+
),
106+
boost: z
107+
.boolean()
108+
.nullish()
109+
.describe(
110+
`If \`true\`, this license is a boost license and allows for a project to
111+
temporarily boost the amount of resources available to a project by the
112+
amount specified in the \`cpu\`, \`memory\`, and \`disk\` fields.`,
113+
),
114+
})
115+
.describe("Project resource quote license."),
116+
z
117+
.object({
118+
title: LicenseTitleSchema,
119+
description: LicenseDescriptionSchema,
120+
range: LicenseRangeSchema,
121+
period: z.enum(["range"]).describe(
122+
`License period for the virtual machine. Note that such licenses may only
123+
be purchased for a particular time period as specified in the \`range\`
124+
field.`,
125+
),
126+
type: z.enum(["vm"]).describe("License type"),
127+
dedicated_vm: z.object({
128+
name: z
129+
.string()
130+
.nullish()
131+
.describe("Virtual machine id (derived from the license id)"),
132+
machine: z
133+
.string()
134+
.describe(
135+
"Google Cloud virtual machine type (e.g., `n2-standard-4`).",
136+
),
137+
}),
138+
})
139+
.describe("Dedicated VM license."),
140+
z
141+
.object({
142+
title: LicenseTitleSchema,
143+
description: LicenseDescriptionSchema,
144+
period: z.enum(["monthly"]).describe(
145+
`License period for the dedicated disk. Note that such licenses may only
146+
be purchased on a monthly basis.`,
147+
),
148+
type: z.enum(["disk"]).describe("License type"),
149+
dedicated_disk: z.union([
150+
z.object({
151+
name: z.string().nullish().describe("Dedicated disk id."),
152+
size_gb: z
153+
.number()
154+
.min(1)
155+
.describe("Size of dedicated disk in GB."),
156+
speed: z
157+
.enum(["standard", "balanced", "ssd"])
158+
.describe("Desired disk speed."),
159+
}),
160+
z
161+
.boolean()
162+
.describe(
163+
"If a boolean value is provided, it must be set to `false`.",
164+
),
165+
]),
166+
})
167+
.describe("Dedicated disk license."),
168+
z
169+
.object({
170+
type: z.enum(["cash-voucher"]),
171+
amount: z.number().min(0),
172+
})
173+
.describe("Used to specify cash voucher amount."),
174+
])
175+
.describe(
176+
`This field is used to specify details appropriate to the product being purchased.
177+
For cash vouchers, this includes the voucher amount and for licenses, this is a
178+
JSON object specifying license details (duration, memory, project count, etc.)`,
179+
),
180+
purchased: z.boolean().nullish(),
181+
})
182+
.describe("Adds a license to the shopping cart.");
183+
184+
export const ShoppingCartAddOutputSchema = z.union([
185+
FailedAPIOperationSchema,
186+
OkAPIOperationSchema,
187+
]);
188+
189+
export type ShoppingCartAddInput = z.infer<typeof ShoppingCartAddInputSchema>;
190+
export type ShoppingCartAddOutput = z.infer<typeof ShoppingCartAddOutputSchema>;

src/packages/next/pages/api/v2/shopping/cart/add.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ import addToCart, {
1515
import getAccountId from "lib/account/get-account";
1616
import getParams from "lib/api/get-params";
1717

18-
export default async function handle(req, res) {
18+
import { apiRoute, apiRouteOperation } from "lib/api";
19+
import {
20+
ShoppingCartAddInputSchema,
21+
ShoppingCartAddOutputSchema,
22+
} from "lib/api/schema/shopping/cart/add";
23+
24+
async function handle(req, res) {
1925
try {
2026
res.json(await add(req));
2127
} catch (err) {
@@ -44,3 +50,24 @@ async function add(req): Promise<number | undefined> {
4450
}
4551
return await addToCart(account_id, product, description, project_id);
4652
}
53+
54+
export default apiRoute({
55+
add: apiRouteOperation({
56+
method: "POST",
57+
openApiOperation: {
58+
tags: ["Shopping"],
59+
},
60+
})
61+
.input({
62+
contentType: "application/json",
63+
body: ShoppingCartAddInputSchema,
64+
})
65+
.outputs([
66+
{
67+
status: 200,
68+
contentType: "application/json",
69+
body: ShoppingCartAddOutputSchema,
70+
},
71+
])
72+
.handler(handle),
73+
});

0 commit comments

Comments
 (0)