@dk/jolly 0.1.11 → 0.2.0
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 +5 -1
- package/assets/skills/jolly/SKILL.md +115 -0
- package/assets/skills/jolly/recipe.yml +307 -0
- package/bin/jolly +49 -2
- package/package.json +3 -2
- package/src/index.ts +1744 -1983
- package/src/lib/cloud-api.ts +38 -10
package/src/lib/cloud-api.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Saleor Cloud API client (feature 012 — existing Saleor store connection).
|
|
2
2
|
//
|
|
3
3
|
// Pinned by the feature 012 Rule "Existing-store automation principles":
|
|
4
|
-
// - The Cloud API is at https://cloud.saleor.io/platform/api
|
|
4
|
+
// - The Cloud API is at https://cloud.saleor.io/platform/api, optionally
|
|
5
|
+
// overridden by JOLLY_SALEOR_CLOUD_API_URL (feature 018 Rule); every Cloud
|
|
6
|
+
// API request honors the override.
|
|
5
7
|
// Authenticate with `Authorization: Token <token>`.
|
|
6
8
|
// - Organizations: GET /platform/api/organizations/ returns a list with slug
|
|
7
9
|
// and environments URL.
|
|
@@ -25,7 +27,20 @@
|
|
|
25
27
|
// with the Cloud token (Bearer), select an existing local app or create one,
|
|
26
28
|
// and create an app token via the Saleor GraphQL API.
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
const DEFAULT_CLOUD_API_BASE = "https://cloud.saleor.io/platform/api";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The Cloud API base URL for this request: the JOLLY_SALEOR_CLOUD_API_URL
|
|
34
|
+
* override when set (feature 018 Rule — pointing it elsewhere is the
|
|
35
|
+
* customer's explicit choice), otherwise the first-party default.
|
|
36
|
+
*/
|
|
37
|
+
export function cloudApiBase(): string {
|
|
38
|
+
const override = process.env["JOLLY_SALEOR_CLOUD_API_URL"];
|
|
39
|
+
if (override && override.trim().length > 0) {
|
|
40
|
+
return override.trim().replace(/\/+$/, "");
|
|
41
|
+
}
|
|
42
|
+
return DEFAULT_CLOUD_API_BASE;
|
|
43
|
+
}
|
|
29
44
|
|
|
30
45
|
const POLL_INTERVAL_MS = 5_000;
|
|
31
46
|
const POLL_TIMEOUT_MS = 480_000; // stay under the harness's CLI timeout
|
|
@@ -70,7 +85,7 @@ export interface CloudOrganization {
|
|
|
70
85
|
export async function listOrganizations(
|
|
71
86
|
token: string,
|
|
72
87
|
): Promise<CloudOrganization[]> {
|
|
73
|
-
const response = await cloudFetch(`${
|
|
88
|
+
const response = await cloudFetch(`${cloudApiBase()}/organizations/`, token);
|
|
74
89
|
if (!response.ok) {
|
|
75
90
|
throw new CloudApiError(
|
|
76
91
|
`Failed to list organizations: HTTP ${response.status} ${await response.text()}`,
|
|
@@ -97,7 +112,7 @@ export async function listProjects(
|
|
|
97
112
|
organizationSlug: string,
|
|
98
113
|
): Promise<CloudProject[]> {
|
|
99
114
|
const response = await cloudFetch(
|
|
100
|
-
`${
|
|
115
|
+
`${cloudApiBase()}/organizations/${organizationSlug}/projects/`,
|
|
101
116
|
token,
|
|
102
117
|
);
|
|
103
118
|
if (!response.ok) {
|
|
@@ -117,7 +132,7 @@ export async function createProject(
|
|
|
117
132
|
body: { name: string; plan: string; region: string },
|
|
118
133
|
): Promise<CloudProject> {
|
|
119
134
|
const response = await cloudFetch(
|
|
120
|
-
`${
|
|
135
|
+
`${cloudApiBase()}/organizations/${organizationSlug}/projects/`,
|
|
121
136
|
token,
|
|
122
137
|
{ method: "POST", body: JSON.stringify(body) },
|
|
123
138
|
);
|
|
@@ -147,7 +162,7 @@ export async function listProjectServices(
|
|
|
147
162
|
projectSlug: string,
|
|
148
163
|
): Promise<CloudService[]> {
|
|
149
164
|
const response = await cloudFetch(
|
|
150
|
-
`${
|
|
165
|
+
`${cloudApiBase()}/organizations/${organizationSlug}/projects/${projectSlug}/services/`,
|
|
151
166
|
token,
|
|
152
167
|
);
|
|
153
168
|
if (!response.ok) return [];
|
|
@@ -202,12 +217,25 @@ export async function createEnvironment(
|
|
|
202
217
|
},
|
|
203
218
|
): Promise<CloudEnvironment> {
|
|
204
219
|
const response = await cloudFetch(
|
|
205
|
-
`${
|
|
220
|
+
`${cloudApiBase()}/organizations/${organizationSlug}/environments/`,
|
|
206
221
|
token,
|
|
207
222
|
{ method: "POST", body: JSON.stringify(body) },
|
|
208
223
|
);
|
|
209
224
|
if (!response.ok) {
|
|
210
225
|
const text = await response.text();
|
|
226
|
+
if (
|
|
227
|
+
response.status >= 400 &&
|
|
228
|
+
response.status < 500 &&
|
|
229
|
+
/domain/i.test(text) &&
|
|
230
|
+
/taken|exists|already|unique|in use|duplicate/i.test(text)
|
|
231
|
+
) {
|
|
232
|
+
throw new CloudApiError(
|
|
233
|
+
`The Cloud API rejected the environment creation: the domain label ` +
|
|
234
|
+
`"${body.domain_label}" is already taken (HTTP ${response.status}).`,
|
|
235
|
+
"DOMAIN_LABEL_TAKEN",
|
|
236
|
+
response.status,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
211
239
|
if (
|
|
212
240
|
response.status >= 400 &&
|
|
213
241
|
response.status < 500 &&
|
|
@@ -236,7 +264,7 @@ export async function listEnvironments(
|
|
|
236
264
|
organizationSlug: string,
|
|
237
265
|
): Promise<CloudEnvironment[]> {
|
|
238
266
|
const response = await cloudFetch(
|
|
239
|
-
`${
|
|
267
|
+
`${cloudApiBase()}/organizations/${organizationSlug}/environments/`,
|
|
240
268
|
token,
|
|
241
269
|
);
|
|
242
270
|
if (!response.ok) return [];
|
|
@@ -250,7 +278,7 @@ export async function getEnvironment(
|
|
|
250
278
|
environmentKey: string,
|
|
251
279
|
): Promise<CloudEnvironment | undefined> {
|
|
252
280
|
const response = await cloudFetch(
|
|
253
|
-
`${
|
|
281
|
+
`${cloudApiBase()}/organizations/${organizationSlug}/environments/${environmentKey}/`,
|
|
254
282
|
token,
|
|
255
283
|
);
|
|
256
284
|
if (!response.ok) return undefined;
|
|
@@ -266,7 +294,7 @@ export interface TaskStatus {
|
|
|
266
294
|
|
|
267
295
|
/** The poll URL for a task: GET /platform/api/service/task-status/{task_id}. */
|
|
268
296
|
export function taskStatusUrl(taskId: string): string {
|
|
269
|
-
return `${
|
|
297
|
+
return `${cloudApiBase()}/service/task-status/${taskId}/`;
|
|
270
298
|
}
|
|
271
299
|
|
|
272
300
|
/**
|