@superblocksteam/sdk 1.4.2 → 1.5.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/src/client.ts CHANGED
@@ -1,21 +1,25 @@
1
1
  import * as fs from "fs";
2
2
  import {
3
+ COMPONENT_EVENT_HEADER,
3
4
  ComponentEvent,
4
5
  getBucketeerUrlFromSuperblocksUrl,
5
6
  getContentType,
6
- COMPONENT_EVENT_HEADER,
7
- NotFoundError,
8
7
  LocalGitRepoState,
9
- ValidateGitSetupRequestBody,
8
+ NotFoundError,
10
9
  SuperblocksResourceType,
11
10
  unreachable,
11
+ ValidateGitSetupRequestBody,
12
12
  } from "@superblocksteam/util";
13
13
  import axios, { AxiosError, AxiosRequestConfig } from "axios";
14
- import * as FormData from "form-data";
14
+ import FormData from "form-data";
15
15
  import { isEmpty } from "lodash";
16
16
  import { BranchNotCheckedOutError, CommitAlreadyExistsError } from "./errors";
17
+ import { signingEnabled } from "./flag";
18
+ import { connectToISocketRPCServer } from "./socket";
19
+ import { AgentType, RemoteCommitDto, UserMeDto } from "./types";
20
+ import { getAgentUrls } from "./utils";
17
21
 
18
- const BASE_BUCKETEER_URL = "api/v1";
22
+ const BASE_BUCKETEER_URL = "api";
19
23
  const BASE_SERVER_PUBLIC_API_URL = "api/v1/public";
20
24
 
21
25
  const SUPERBLOCKS_MAX_FILE_SIZE_MB = 10;
@@ -204,7 +208,7 @@ export async function fetchApplicationWithComponents({
204
208
  // fetch files from bucketeer
205
209
  const branchPath = branch ? `/branches/${encodeURIComponent(branch)}` : "";
206
210
  const componentFileURL = new URL(
207
- `${BASE_BUCKETEER_URL}/components/${applicationId}${branchPath}?commit=${commitId}&viewMode=${viewMode}`,
211
+ `${BASE_BUCKETEER_URL}/v1/components/${applicationId}${branchPath}?commit=${commitId}&viewMode=${viewMode}`,
208
212
  bucketeerBaseUrl
209
213
  ).toString();
210
214
 
@@ -408,25 +412,49 @@ export async function registerComponents(
408
412
  token: string,
409
413
  superblocksBaseUrl: string,
410
414
  branch: string | null,
411
- injectedHeaders?: Record<string, string>
415
+ injectedHeaders: Record<string, string>
412
416
  ) {
413
417
  try {
414
418
  const branchPath = branch ? `/branches/${encodeURIComponent(branch)}` : "";
415
- const config: AxiosRequestConfig = {
416
- method: "put",
417
- url: new URL(
418
- `${BASE_SERVER_PUBLIC_API_URL}/application/${applicationId}${branchPath}/components`,
419
- superblocksBaseUrl
420
- ).toString(),
421
- headers: {
422
- Authorization: "Bearer " + token,
423
- [CLI_VERSION_HEADER]: cliVersion,
424
- ...injectedHeaders,
425
- },
426
- data: { components: componentConfigs },
427
- };
428
- const response = await axios(config);
429
- return response.data;
419
+ const userMe = await fetchCurrentUser(
420
+ cliVersion,
421
+ token,
422
+ superblocksBaseUrl
423
+ );
424
+ const organization = userMe.organizations[0];
425
+ if (organization.agentType == AgentType.ONPREMISE && signingEnabled(userMe.flagBootstrap)) {
426
+ const agentUrls = getAgentUrls(userMe.agents, organization.agentType);
427
+ const socket = await connectToISocketRPCServer({
428
+ agentUrls,
429
+ superblocksBaseUrl,
430
+ token,
431
+ });
432
+ const resp = await socket.call.v1.public.application.component.register({
433
+ applicationId,
434
+ branchName: branch || "",
435
+ cliVersion: cliVersion,
436
+ componentEvent: injectedHeaders[COMPONENT_EVENT_HEADER],
437
+ components: componentConfigs,
438
+ });
439
+ socket.close();
440
+ return resp.data;
441
+ } else {
442
+ const config: AxiosRequestConfig = {
443
+ method: "put",
444
+ url: new URL(
445
+ `${BASE_SERVER_PUBLIC_API_URL}/application/${applicationId}${branchPath}/components`,
446
+ superblocksBaseUrl
447
+ ).toString(),
448
+ headers: {
449
+ Authorization: "Bearer " + token,
450
+ [CLI_VERSION_HEADER]: cliVersion,
451
+ ...injectedHeaders,
452
+ },
453
+ data: { components: componentConfigs },
454
+ };
455
+ const response = await axios(config);
456
+ return response.data;
457
+ }
430
458
  } catch (e: any) {
431
459
  if (e instanceof AxiosError) {
432
460
  const message: string =
@@ -496,7 +524,7 @@ export async function uploadComponents({
496
524
  buildFiles,
497
525
  });
498
526
  const postURL = new URL(
499
- `${BASE_BUCKETEER_URL}/components/upload`,
527
+ `${BASE_BUCKETEER_URL}/v2/components/upload`,
500
528
  bucketeerBaseUrl
501
529
  ).toString();
502
530
 
@@ -526,13 +554,42 @@ You can reduce your component bundle size by uploading static assets to a separa
526
554
  [CLI_VERSION_HEADER]: cliVersion,
527
555
  [COMPONENT_EVENT_HEADER]: ComponentEvent.UPLOAD,
528
556
  [SUPERBLOCKS_URL_HEADER]: superblocksBaseUrl,
529
- ...formHeaders,
557
+ ...formHeaders,
530
558
  },
531
559
  };
532
560
 
533
- const response = await axios.post(postURL, formData, config);
561
+ const uploadResponse = await axios.post(postURL, formData, config);
534
562
 
535
- return response.data;
563
+ const userMe = await fetchCurrentUser(
564
+ cliVersion,
565
+ token,
566
+ superblocksBaseUrl
567
+ );
568
+ const organization = userMe.organizations[0];
569
+ const signingRequired = organization.agentType == AgentType.ONPREMISE && signingEnabled(userMe.flagBootstrap);
570
+ let agentUrls: string[] = [];
571
+ if (signingRequired) {
572
+ agentUrls = getAgentUrls(userMe.agents, organization.agentType);
573
+ }
574
+ const socket = await connectToISocketRPCServer({
575
+ agentUrls,
576
+ superblocksBaseUrl,
577
+ token,
578
+ });
579
+ try {
580
+ await socket.call.v1.public.application.component.update({
581
+ applicationId,
582
+ branchName: branch ?? undefined,
583
+ srcFiles: srcFiles.map((file) => file.filename),
584
+ buildFiles: buildFiles.map((file) => file.filename),
585
+ registeredComponents: componentConfigs,
586
+ cliVersion,
587
+ componentBaseUrl: uploadResponse.data.componentBaseUrl,
588
+ signingRequired,
589
+ });
590
+ } finally {
591
+ socket.close();
592
+ }
536
593
  } catch (e: any) {
537
594
  if (e instanceof AxiosError && e.response?.status === 413) {
538
595
  throw new Error(
@@ -559,7 +616,7 @@ export async function fetchCurrentUser(
559
616
  cliVersion: string,
560
617
  token: string,
561
618
  superblocksBaseUrl: string
562
- ) {
619
+ ): Promise<UserMeDto> {
563
620
  try {
564
621
  const config: AxiosRequestConfig = {
565
622
  method: "get",
@@ -574,7 +631,7 @@ export async function fetchCurrentUser(
574
631
  },
575
632
  };
576
633
  const response = await axios(config);
577
- return response.data.data.user;
634
+ return response.data.data as UserMeDto;
578
635
  } catch (e: any) {
579
636
  let message: string;
580
637
  if (e instanceof AxiosError) {
@@ -607,7 +664,7 @@ export async function pushApplication({
607
664
  branch: string;
608
665
  injectedHeaders: Record<string, string>;
609
666
  applicationConfig: PushApplicationWithCommitConfig;
610
- }): Promise<ApplicationWrapper | undefined> {
667
+ }): Promise<RemoteCommitDto | undefined> {
611
668
  const serverURL = new URL(
612
669
  `${BASE_SERVER_PUBLIC_API_URL}/applications/${applicationId}/branches/${encodeURIComponent(
613
670
  branch
@@ -617,17 +674,43 @@ export async function pushApplication({
617
674
  let serverResponse;
618
675
 
619
676
  try {
620
- const config: AxiosRequestConfig = {
621
- method: "post",
622
- url: serverURL.toString(),
623
- headers: {
624
- Authorization: "Bearer " + token,
625
- [CLI_VERSION_HEADER]: cliVersion,
626
- ...injectedHeaders,
627
- },
628
- data: applicationConfig,
629
- };
630
- serverResponse = await axios<ResponseWithMeta<ApplicationWrapper>>(config);
677
+ const userMe = await fetchCurrentUser(
678
+ cliVersion,
679
+ token,
680
+ superblocksBaseUrl
681
+ );
682
+ const organization = userMe.organizations[0];
683
+ if (organization.agentType == AgentType.ONPREMISE && signingEnabled(userMe.flagBootstrap)) {
684
+ const agentUrls = getAgentUrls(userMe.agents, organization.agentType);
685
+ const socket = await connectToISocketRPCServer({
686
+ agentUrls,
687
+ superblocksBaseUrl,
688
+ token,
689
+ });
690
+ const resp = await socket.call.v1.public.application.pushCommit({
691
+ applicationId,
692
+ apis: applicationConfig.apis,
693
+ application: applicationConfig.application,
694
+ branchName: branch,
695
+ commitId: applicationConfig.commitId,
696
+ commitMessage: applicationConfig.commitMessage,
697
+ page: applicationConfig.page,
698
+ });
699
+ socket.close();
700
+ return resp.data;
701
+ } else {
702
+ const config: AxiosRequestConfig = {
703
+ method: "post",
704
+ url: serverURL.toString(),
705
+ headers: {
706
+ Authorization: "Bearer " + token,
707
+ [CLI_VERSION_HEADER]: cliVersion,
708
+ ...injectedHeaders,
709
+ },
710
+ data: applicationConfig,
711
+ };
712
+ serverResponse = await axios<ResponseWithMeta<RemoteCommitDto>>(config);
713
+ }
631
714
  } catch (e) {
632
715
  if (axios.isAxiosError(e)) {
633
716
  const respCode =
@@ -686,19 +769,43 @@ export async function pushApi({
686
769
  let serverResponse;
687
770
 
688
771
  try {
689
- const config: AxiosRequestConfig = {
690
- method: "post",
691
- url: serverURL.toString(),
692
- headers: {
693
- Authorization: "Bearer " + token,
694
- [CLI_VERSION_HEADER]: cliVersion,
695
- ...injectedHeaders,
696
- },
697
- data: apiConfig,
698
- };
699
- serverResponse = await axios<ResponseWithMeta<{ commitId: string }>>(
700
- config
772
+ const userMe = await fetchCurrentUser(
773
+ cliVersion,
774
+ token,
775
+ superblocksBaseUrl
701
776
  );
777
+ const organization = userMe.organizations[0];
778
+ if (organization.agentType == AgentType.ONPREMISE && signingEnabled(userMe.flagBootstrap)) {
779
+ const agentUrls = getAgentUrls(userMe.agents, organization.agentType);
780
+ const socket = await connectToISocketRPCServer({
781
+ agentUrls,
782
+ superblocksBaseUrl,
783
+ token,
784
+ });
785
+ const resp = await socket.call.v1.public.api.pushCommit({
786
+ apiId,
787
+ apiPb: apiConfig.apiPb,
788
+ branchName: branch,
789
+ commitId: apiConfig.commitId,
790
+ commitMessage: apiConfig.commitMessage,
791
+ });
792
+ socket.close();
793
+ return resp.data;
794
+ } else {
795
+ const config: AxiosRequestConfig = {
796
+ method: "post",
797
+ url: serverURL.toString(),
798
+ headers: {
799
+ Authorization: "Bearer " + token,
800
+ [CLI_VERSION_HEADER]: cliVersion,
801
+ ...injectedHeaders,
802
+ },
803
+ data: apiConfig,
804
+ };
805
+ serverResponse = await axios<ResponseWithMeta<{ commitId: string }>>(
806
+ config
807
+ );
808
+ }
702
809
  } catch (e) {
703
810
  if (axios.isAxiosError(e)) {
704
811
  if (e.response?.data?.responseMeta?.status === 405) {
package/src/flag.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { FlagBootstrap } from "./types";
2
+
3
+ export const signingEnabled = (flags: FlagBootstrap): boolean => {
4
+ return flags["ui.enable-resource-signing"] ?? false;
5
+ };
package/src/sdk.ts CHANGED
@@ -7,18 +7,18 @@ import {
7
7
  fetchApi,
8
8
  fetchApis,
9
9
  fetchApplication,
10
- fetchApplicationWithComponents,
10
+ fetchApplicationBranches,
11
11
  fetchApplications,
12
- registerComponents,
13
- uploadComponents,
12
+ fetchApplicationWithComponents,
14
13
  fetchCurrentUser,
15
- ViewMode,
14
+ pushApi,
15
+ PushApiWithCommitConfig,
16
16
  pushApplication,
17
17
  PushApplicationWithCommitConfig,
18
- fetchApplicationBranches,
19
- PushApiWithCommitConfig,
20
- pushApi,
18
+ registerComponents,
19
+ uploadComponents,
21
20
  validateGitSetup,
21
+ ViewMode,
22
22
  } from "./client";
23
23
 
24
24
  // Exporting here instead of index.ts because only sdk.ts is exposed outside in package.json
@@ -144,7 +144,7 @@ export class SuperblocksSdk {
144
144
  applicationId: string,
145
145
  componentConfigs: Record<string, any>,
146
146
  branch: string | null,
147
- injectedHeaders?: Record<string, string>
147
+ injectedHeaders: Record<string, string>
148
148
  ) {
149
149
  return registerComponents(
150
150
  this.cliVersion,
@@ -0,0 +1,228 @@
1
+ import {
2
+ ApiToSign,
3
+ ApiToVerify,
4
+ AppToSign,
5
+ AppToVerify,
6
+ MethodHandlers,
7
+ RemoteCommitDto,
8
+ Signature,
9
+ } from "../types";
10
+ import { signResource, verifyResources } from "./signing";
11
+
12
+ type MethodSchema<Params, Response> = (params: Params) => Promise<Response>;
13
+
14
+ export interface ClientMethods {
15
+ v1: {
16
+ signing: {
17
+ signApplication: MethodSchema<
18
+ { branchName: string; toSign: AppToSign },
19
+ { signature: Signature }
20
+ >;
21
+ signApis: MethodSchema<
22
+ { branchName: string; toSign: ApiToSign[] },
23
+ { signatures: Signature[] }
24
+ >;
25
+ verifyApplication: MethodSchema<
26
+ { branchName: string; toVerify: AppToVerify },
27
+ { ok: boolean }
28
+ >;
29
+ verifyApi: MethodSchema<
30
+ { branchName: string; toVerify: ApiToVerify[] },
31
+ { ok: boolean }
32
+ >;
33
+ };
34
+ };
35
+ }
36
+
37
+ type ServerMethodSchema<Params, Response> = MethodSchema<
38
+ Params,
39
+ ResponseDto<Response>
40
+ >;
41
+
42
+ // This file contains the definition of the protocol used for communication between the SB server and clients
43
+
44
+ type ResponseDto<T> = {
45
+ responseMeta: ResponseMeta;
46
+ data: T;
47
+ };
48
+
49
+ export type ResponseMeta = {
50
+ status: number;
51
+ success: boolean;
52
+ error?: APIResponseError;
53
+ };
54
+
55
+ type APIResponseError = {
56
+ code: number;
57
+ message: string;
58
+ };
59
+
60
+ export interface ServerMethods {
61
+ v1: {
62
+ echo: MethodSchema<{ message: string }, { message: string }>;
63
+ public: {
64
+ application: {
65
+ component: {
66
+ register: ServerMethodSchema<
67
+ {
68
+ applicationId: string;
69
+ branchName: string;
70
+ cliVersion: string;
71
+ componentEvent: string;
72
+ components: Record<string, unknown>;
73
+ },
74
+ { success: boolean }
75
+ >;
76
+ update: ServerMethodSchema<
77
+ {
78
+ applicationId: string;
79
+ branchName?: string;
80
+ srcFiles: string[];
81
+ buildFiles: string[];
82
+ registeredComponents: Record<string, unknown>;
83
+ cliVersion: string | undefined;
84
+ componentBaseUrl: string;
85
+ signingRequired: boolean;
86
+ },
87
+ { success: boolean }
88
+ >;
89
+ };
90
+ pushCommit: ServerMethodSchema<
91
+ {
92
+ applicationId: string;
93
+ branchName: string;
94
+ commitId: string;
95
+ commitMessage: string;
96
+ application: Record<string, unknown>;
97
+ page: Record<string, unknown>;
98
+ apis: Record<string, unknown>[];
99
+ },
100
+ RemoteCommitDto
101
+ >;
102
+ };
103
+ api: {
104
+ pushCommit: ServerMethodSchema<
105
+ {
106
+ apiId: string;
107
+ branchName: string;
108
+ commitId: string;
109
+ commitMessage: string;
110
+ apiPb: Record<string, unknown>;
111
+ },
112
+ RemoteCommitDto
113
+ >;
114
+ };
115
+ };
116
+ };
117
+ }
118
+
119
+ export function createRequestHandlers({
120
+ agentUrls,
121
+ token,
122
+ }: {
123
+ token: string;
124
+ agentUrls: string[];
125
+ }) {
126
+ const requestHandlers: MethodHandlers<ClientMethods, ServerMethods, unknown> =
127
+ {
128
+ v1: {
129
+ signing: {
130
+ signApplication: [
131
+ async ({
132
+ branchName,
133
+ toSign,
134
+ }: {
135
+ branchName: string;
136
+ toSign: AppToSign;
137
+ }) => {
138
+ const signature = await signResource({
139
+ agentUrls,
140
+ token: token,
141
+ branchName,
142
+ resource: {
143
+ literal: {
144
+ data: toSign.rootHash,
145
+ },
146
+ },
147
+ });
148
+ return { signature: signature };
149
+ },
150
+ ],
151
+ signApis: [
152
+ async ({
153
+ branchName,
154
+ toSign,
155
+ }: {
156
+ branchName: string;
157
+ toSign: ApiToSign[];
158
+ }) => {
159
+ const signatures: Signature[] = [];
160
+ for (const { apiPb } of toSign) {
161
+ const signature = await signResource({
162
+ agentUrls,
163
+ token: token,
164
+ branchName,
165
+ resource: { api: apiPb },
166
+ });
167
+ signatures.push(signature);
168
+ }
169
+ return { signatures };
170
+ },
171
+ ],
172
+ verifyApplication: [
173
+ async ({
174
+ branchName,
175
+ toVerify,
176
+ }: {
177
+ branchName: string;
178
+ toVerify: AppToVerify;
179
+ }) => {
180
+ try {
181
+ await verifyResources({
182
+ agentUrls,
183
+ token,
184
+ branchName,
185
+ resources: [
186
+ {
187
+ literal: {
188
+ data: toVerify.rootHash,
189
+ signature: toVerify.signature,
190
+ },
191
+ },
192
+ ],
193
+ });
194
+ return { ok: true };
195
+ } catch {
196
+ return { ok: false };
197
+ }
198
+ },
199
+ ],
200
+
201
+ verifyApi: [
202
+ async ({
203
+ branchName,
204
+ toVerify,
205
+ }: {
206
+ branchName: string;
207
+ toVerify: ApiToVerify[];
208
+ }) => {
209
+ try {
210
+ await verifyResources({
211
+ agentUrls,
212
+ token,
213
+ branchName,
214
+ resources: toVerify.map(({ apiPb }) => ({
215
+ api: apiPb,
216
+ })),
217
+ });
218
+ return { ok: true };
219
+ } catch {
220
+ return { ok: false };
221
+ }
222
+ },
223
+ ],
224
+ },
225
+ },
226
+ };
227
+ return requestHandlers;
228
+ }