@embeddable.com/sdk-core 3.1.4 → 3.1.6

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/lib/push.d.ts CHANGED
@@ -2,6 +2,11 @@ export declare const YAML_OR_JS_FILES: RegExp;
2
2
  declare const _default: () => Promise<void>;
3
3
  export default _default;
4
4
  export declare function archive(ctx: any, yamlFiles: [string, string][], includeBuild?: boolean): Promise<unknown>;
5
+ export declare function sendBuildByApiKey(ctx: any, { apiKey, email, message }: any): Promise<{
6
+ bundleId: any;
7
+ email: any;
8
+ message: any;
9
+ }>;
5
10
  export declare function sendBuild(ctx: any, { workspaceId, token }: {
6
11
  workspaceId: string;
7
12
  token: string;
package/lib/utils.d.ts CHANGED
@@ -1 +1,8 @@
1
1
  export declare const checkNodeVersion: () => Promise<void>;
2
+ /**
3
+ * Get the value of a process argument by key
4
+ * Example: getArgumentByKey("--email") or getArgumentByKey(["--email", "-e"])
5
+ * @param key The key to search for in the process arguments
6
+ * @returns
7
+ */
8
+ export declare const getArgumentByKey: (key: string | string[]) => string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embeddable.com/sdk-core",
3
- "version": "3.1.4",
3
+ "version": "3.1.6",
4
4
  "description": "Core Embeddable SDK module responsible for web-components bundling and publishing.",
5
5
  "keywords": [
6
6
  "embeddable",
package/src/push.ts CHANGED
@@ -10,7 +10,7 @@ import reportErrorToRollbar from "./rollbar.mjs";
10
10
 
11
11
  import { findFiles } from "@embeddable.com/sdk-utils";
12
12
  import { getToken } from "./login";
13
- import { checkNodeVersion } from "./utils";
13
+ import { checkNodeVersion, getArgumentByKey } from "./utils";
14
14
 
15
15
  // grab .cube.yml|js and .sc.yml|js files
16
16
  export const YAML_OR_JS_FILES = /^(.*)\.(cube|sc)\.(ya?ml|js)$/;
@@ -27,34 +27,73 @@ export default async () => {
27
27
 
28
28
  const token = await verify(config);
29
29
 
30
+ if (process.argv.includes("--api-key") || process.argv.includes("-k")) {
31
+ spinnerPushing = ora("Using API key...").start();
32
+ await pushByApiKey(config, spinnerPushing);
33
+
34
+ spinnerPushing.succeed("Published using API key");
35
+
36
+ return;
37
+ }
38
+
30
39
  const { workspaceId, name: workspaceName } = await selectWorkspace(
31
40
  config,
32
41
  token,
33
42
  );
34
43
 
35
- const spinnerArchive = ora("Building...").start();
36
-
37
- const filesList = await findFiles(config.client.srcDir, YAML_OR_JS_FILES);
38
-
39
- await archive(config, filesList);
40
- spinnerArchive.succeed("Bundling completed");
44
+ const workspacePreviewUrl = `${config.previewBaseUrl}/workspace/${workspaceId}`
41
45
 
46
+ await buildArchive(config);
42
47
  spinnerPushing = ora(
43
- `Publishing to ${workspaceName} using ${config.pushBaseUrl}...`,
48
+ `Publishing to ${workspaceName} using ${workspacePreviewUrl}...`,
44
49
  ).start();
45
50
 
46
51
  await sendBuild(config, { workspaceId, token });
47
52
  spinnerPushing.succeed(
48
- `Published to ${workspaceName} using ${config.pushBaseUrl}`,
53
+ `Published to ${workspaceName} using ${workspacePreviewUrl}`,
49
54
  );
50
55
  } catch (error: any) {
51
56
  spinnerPushing?.fail("Publishing failed");
52
- console.error(error.response?.data || error?.message || error);
57
+ if (error.response?.statusText === "Unauthorized") {
58
+ console.error("Unauthorized. Please check your credentials.");
59
+ } else {
60
+ console.error(error.response?.data || error?.message || error);
61
+ }
62
+
53
63
  await reportErrorToRollbar(error);
54
64
  process.exit(1);
55
65
  }
56
66
  };
57
67
 
68
+ async function pushByApiKey(config: any, spinner: any) {
69
+ const apiKey = getArgumentByKey(["--api-key", "-k"]);
70
+
71
+ if (!apiKey) {
72
+ spinner.fail("No API key provided");
73
+ process.exit(1);
74
+ }
75
+
76
+ const email = getArgumentByKey(["--email", "-e"]);
77
+
78
+ if (!email || !/\S+@\S+\.\S+/.test(email)) {
79
+ spinner.fail(
80
+ "Invalid email provided. Please provide a valid email using --email (-e) flag",
81
+ );
82
+ process.exit(1);
83
+ }
84
+
85
+ // message is optional
86
+ const message = getArgumentByKey(["--message", "-m"]);
87
+
88
+ await buildArchive(config);
89
+
90
+ return sendBuildByApiKey(config, {
91
+ apiKey,
92
+ email,
93
+ message,
94
+ });
95
+ }
96
+
58
97
  async function selectWorkspace(ctx: any, token: string) {
59
98
  const workspaceSpinner = ora({
60
99
  text: `Fetching workspaces using ${ctx.pushBaseUrl}...`,
@@ -112,6 +151,15 @@ async function verify(ctx: any) {
112
151
  return token;
113
152
  }
114
153
 
154
+ async function buildArchive(config: any) {
155
+ const spinnerArchive = ora("Building...").start();
156
+
157
+ const filesList = await findFiles(config.client.srcDir, YAML_OR_JS_FILES);
158
+
159
+ await archive(config, filesList);
160
+ return spinnerArchive.succeed("Bundling completed");
161
+ }
162
+
115
163
  export async function archive(
116
164
  ctx: any,
117
165
  yamlFiles: [string, string][],
@@ -143,6 +191,37 @@ export async function archive(
143
191
  });
144
192
  }
145
193
 
194
+ export async function sendBuildByApiKey(
195
+ ctx: any,
196
+ { apiKey, email, message }: any,
197
+ ) {
198
+ const { FormData, Blob } = await import("formdata-node");
199
+ const { fileFromPath } = await import("formdata-node/file-from-path");
200
+
201
+ const file = await fileFromPath(
202
+ ctx.client.archiveFile,
203
+ "embeddable-build.zip",
204
+ );
205
+
206
+ const form = new FormData();
207
+ form.set("file", file, "embeddable-build.zip");
208
+
209
+ const metadataBlob = new Blob(
210
+ [JSON.stringify({ authorEmail: email, description: message })],
211
+ { type: "application/json" },
212
+ );
213
+ form.set("metadata", metadataBlob, "metadata.json");
214
+
215
+ const response = await uploadFile(
216
+ form,
217
+ `${ctx.pushBaseUrl}/api/v1/bundle/upload`,
218
+ apiKey,
219
+ );
220
+ await fs.rm(ctx.client.archiveFile);
221
+
222
+ return { bundleId: response.data?.bundleId, email, message };
223
+ }
224
+
146
225
  export async function sendBuild(
147
226
  ctx: any,
148
227
  { workspaceId, token }: { workspaceId: string; token: string },
@@ -158,7 +237,17 @@ export async function sendBuild(
158
237
  const form = new FormData();
159
238
  form.set("file", file, "embeddable-build.zip");
160
239
 
161
- await axios.post(`${ctx.pushBaseUrl}/bundle/${workspaceId}/upload`, form, {
240
+ await uploadFile(
241
+ form,
242
+ `${ctx.pushBaseUrl}/bundle/${workspaceId}/upload`,
243
+ token,
244
+ );
245
+
246
+ await fs.rm(ctx.client.archiveFile);
247
+ }
248
+
249
+ async function uploadFile(formData: any, url: string, token: string) {
250
+ return axios.post(url, formData, {
162
251
  headers: {
163
252
  "Content-Type": "multipart/form-data",
164
253
  Authorization: `Bearer ${token}`,
@@ -166,8 +255,6 @@ export async function sendBuild(
166
255
  maxContentLength: Infinity,
167
256
  maxBodyLength: Infinity,
168
257
  });
169
-
170
- await fs.rm(ctx.client.archiveFile);
171
258
  }
172
259
 
173
260
  async function getWorkspaces(ctx: any, token: string, workspaceSpinner: any) {
package/src/utils.ts CHANGED
@@ -22,3 +22,25 @@ export const checkNodeVersion = async () => {
22
22
  process.exit(1);
23
23
  }
24
24
  };
25
+
26
+ /**
27
+ * Get the value of a process argument by key
28
+ * Example: getArgumentByKey("--email") or getArgumentByKey(["--email", "-e"])
29
+ * @param key The key to search for in the process arguments
30
+ * @returns
31
+ */
32
+ export const getArgumentByKey = (key: string | string[]) => {
33
+ if (Array.isArray(key)) {
34
+ for (const k of key) {
35
+ if (process.argv.includes(k)) {
36
+ const index = process.argv.indexOf(k);
37
+ return index !== -1 ? process.argv[index + 1] : undefined;
38
+ }
39
+ }
40
+
41
+ return undefined;
42
+ }
43
+
44
+ const index = process.argv.indexOf(key);
45
+ return index !== -1 ? process.argv[index + 1] : undefined;
46
+ };
package/src/validate.ts CHANGED
@@ -75,9 +75,21 @@ export async function dataModelsValidation(filesList: [string, string][]) {
75
75
 
76
76
  const cube = YAML.parse(fileContentRaw);
77
77
 
78
- const safeParse = cubeModelSchema.safeParse(cube);
79
- if (!safeParse.success) {
80
- errorFormatter(safeParse.error.issues).forEach((error) => {
78
+ if (!cube?.cubes && !cube?.views) {
79
+ return [`${filePath}: At least one cubes or views must be defined`];
80
+ }
81
+
82
+ const cubeModelSafeParse = cubeModelSchema.safeParse(cube);
83
+ const viewModelSafeParse = viewModelSchema.safeParse(cube);
84
+
85
+ if (cube.cubes && !cubeModelSafeParse.success) {
86
+ errorFormatter(cubeModelSafeParse.error.issues).forEach((error) => {
87
+ errors.push(`${filePath}: ${error}`);
88
+ });
89
+ }
90
+
91
+ if (cube.views && !viewModelSafeParse.success) {
92
+ errorFormatter(viewModelSafeParse.error.issues).forEach((error) => {
81
93
  errors.push(`${filePath}: ${error}`);
82
94
  });
83
95
  }
@@ -172,6 +184,19 @@ const cubeModelSchema = z
172
184
  },
173
185
  );
174
186
 
187
+ const viewModelSchema = z.object({
188
+ views: z.object({
189
+ name: z.string(),
190
+ cubes: z
191
+ .object({
192
+ join_path: z.string(),
193
+ })
194
+ .array()
195
+ })
196
+ .array()
197
+ .min(1),
198
+ });
199
+
175
200
  const securityContextSchema = z.array(
176
201
  z.object({
177
202
  name: z.string(),