@superblocksteam/sdk-api 2.0.165-next.0 → 2.0.166-next.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 +1 -2
- package/dist/errors.d.ts +36 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +244 -8
- package/dist/errors.js.map +1 -1
- package/dist/errors.test.d.ts +2 -0
- package/dist/errors.test.d.ts.map +1 -0
- package/dist/errors.test.js +189 -0
- package/dist/errors.test.js.map +1 -0
- package/dist/integrations/base/decode-worker-binary-response.d.ts.map +1 -1
- package/dist/integrations/base/decode-worker-binary-response.js +2 -5
- package/dist/integrations/base/decode-worker-binary-response.js.map +1 -1
- package/dist/integrations/base/index.d.ts +2 -1
- package/dist/integrations/base/index.d.ts.map +1 -1
- package/dist/integrations/base/index.js +1 -0
- package/dist/integrations/base/index.js.map +1 -1
- package/dist/integrations/base/request-body.d.ts +29 -0
- package/dist/integrations/base/request-body.d.ts.map +1 -0
- package/dist/integrations/base/request-body.js +238 -0
- package/dist/integrations/base/request-body.js.map +1 -0
- package/dist/integrations/base/request-body.test.d.ts +2 -0
- package/dist/integrations/base/request-body.test.d.ts.map +1 -0
- package/dist/integrations/base/request-body.test.js +466 -0
- package/dist/integrations/base/request-body.test.js.map +1 -0
- package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -1
- package/dist/integrations/base/rest-api-client-base.js +15 -14
- package/dist/integrations/base/rest-api-client-base.js.map +1 -1
- package/dist/integrations/base/rest-api-integration-client.d.ts +2 -2
- package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
- package/dist/integrations/base/rest-api-integration-client.js +5 -6
- package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
- package/dist/integrations/base/types.d.ts +55 -11
- package/dist/integrations/base/types.d.ts.map +1 -1
- package/dist/integrations/base/types.js +7 -0
- package/dist/integrations/base/types.js.map +1 -1
- package/dist/integrations/cosmosdb/client.d.ts.map +1 -1
- package/dist/integrations/cosmosdb/client.js +2 -5
- package/dist/integrations/cosmosdb/client.js.map +1 -1
- package/dist/integrations/documentation-resolver.test.js +106 -1
- package/dist/integrations/documentation-resolver.test.js.map +1 -1
- package/dist/integrations/dynamodb/client.d.ts.map +1 -1
- package/dist/integrations/dynamodb/client.js +2 -5
- package/dist/integrations/dynamodb/client.js.map +1 -1
- package/dist/integrations/gcs/client.d.ts.map +1 -1
- package/dist/integrations/gcs/client.js +2 -5
- package/dist/integrations/gcs/client.js.map +1 -1
- package/dist/integrations/gsheets/client.d.ts.map +1 -1
- package/dist/integrations/gsheets/client.js +2 -5
- package/dist/integrations/gsheets/client.js.map +1 -1
- package/dist/integrations/mongodb/client.d.ts.map +1 -1
- package/dist/integrations/mongodb/client.js +2 -5
- package/dist/integrations/mongodb/client.js.map +1 -1
- package/dist/integrations/restapiintegration/client.body-types.test.d.ts +2 -0
- package/dist/integrations/restapiintegration/client.body-types.test.d.ts.map +1 -0
- package/dist/integrations/restapiintegration/client.body-types.test.js +227 -0
- package/dist/integrations/restapiintegration/client.body-types.test.js.map +1 -0
- package/dist/integrations/restapiintegration/client.test.js +56 -30
- package/dist/integrations/restapiintegration/client.test.js.map +1 -1
- package/dist/integrations/s3/client.d.ts.map +1 -1
- package/dist/integrations/s3/client.js +2 -5
- package/dist/integrations/s3/client.js.map +1 -1
- package/dist/integrations/salesforce/client.d.ts.map +1 -1
- package/dist/integrations/salesforce/client.js +2 -5
- package/dist/integrations/salesforce/client.js.map +1 -1
- package/dist/integrations/slack/client.d.ts.map +1 -1
- package/dist/integrations/slack/client.js +4 -13
- package/dist/integrations/slack/client.js.map +1 -1
- package/dist/integrations/slack/client.test.js +20 -0
- package/dist/integrations/slack/client.test.js.map +1 -1
- package/dist/integrations/utils.d.ts +8 -3
- package/dist/integrations/utils.d.ts.map +1 -1
- package/dist/integrations/utils.js +42 -1
- package/dist/integrations/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/errors.test.ts +257 -0
- package/src/errors.ts +304 -10
- package/src/integrations/base/decode-worker-binary-response.ts +5 -7
- package/src/integrations/base/index.ts +3 -0
- package/src/integrations/base/request-body.test.ts +565 -0
- package/src/integrations/base/request-body.ts +392 -0
- package/src/integrations/base/rest-api-client-base.ts +24 -19
- package/src/integrations/base/rest-api-integration-client.ts +14 -10
- package/src/integrations/base/types.ts +66 -11
- package/src/integrations/box/README.md +4 -60
- package/src/integrations/cosmosdb/client.ts +8 -7
- package/src/integrations/documentation-resolver.test.ts +130 -1
- package/src/integrations/dropbox/README.md +1 -71
- package/src/integrations/dropbox/docs.manifest.json +10 -1
- package/src/integrations/dropbox/overlays/upload-unsupported.md +3 -0
- package/src/integrations/dropbox/overlays/upload.md +89 -0
- package/src/integrations/dynamodb/client.ts +8 -7
- package/src/integrations/elasticsearch/README.md +0 -80
- package/src/integrations/elasticsearch/docs.manifest.json +10 -1
- package/src/integrations/elasticsearch/overlays/bulk-unsupported.md +3 -0
- package/src/integrations/elasticsearch/overlays/bulk.md +80 -0
- package/src/integrations/gcs/client.ts +8 -7
- package/src/integrations/googledrive/README.md +10 -36
- package/src/integrations/gsheets/client.ts +8 -7
- package/src/integrations/jira/README.md +0 -26
- package/src/integrations/jira/docs.manifest.json +10 -1
- package/src/integrations/jira/overlays/attachments-unsupported.md +3 -0
- package/src/integrations/jira/overlays/attachments.md +30 -0
- package/src/integrations/lakebase/README.md +1 -3
- package/src/integrations/mongodb/client.ts +8 -7
- package/src/integrations/postgres/README.md +1 -3
- package/src/integrations/restapiintegration/client.body-types.test.ts +323 -0
- package/src/integrations/restapiintegration/client.test.ts +68 -32
- package/src/integrations/restapiintegration/docs.manifest.json +8 -0
- package/src/integrations/restapiintegration/overlays/request-body-types-unsupported.md +7 -0
- package/src/integrations/restapiintegration/overlays/request-body-types.md +115 -0
- package/src/integrations/restapiintegration/overlays/response-types-binary.md +4 -8
- package/src/integrations/s3/client.ts +8 -7
- package/src/integrations/salesforce/client.ts +9 -7
- package/src/integrations/slack/client.test.ts +30 -0
- package/src/integrations/slack/client.ts +13 -19
- package/src/integrations/snowflakepostgres/README.md +1 -3
- package/src/integrations/stabilityai/README.md +0 -87
- package/src/integrations/stabilityai/docs.manifest.json +10 -1
- package/src/integrations/stabilityai/overlays/image-uploads-unsupported.md +3 -0
- package/src/integrations/stabilityai/overlays/image-uploads.md +120 -0
- package/src/integrations/stripe/README.md +0 -201
- package/src/integrations/stripe/docs.manifest.json +10 -1
- package/src/integrations/stripe/overlays/request-bodies-unsupported.md +3 -0
- package/src/integrations/stripe/overlays/request-bodies.md +204 -0
- package/src/integrations/utils.ts +51 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Box Client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Download and manage files in Box. File uploads are not available through this integration; see [File Uploads Are Not Available](#file-uploads-are-not-available).
|
|
4
4
|
|
|
5
5
|
## Methods
|
|
6
6
|
|
|
@@ -163,37 +163,6 @@ const content = await ctx.integrations.box.apiRequest(
|
|
|
163
163
|
);
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
### Upload a File
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
const UploadResponseSchema = z.object({
|
|
170
|
-
entries: z.array(FileInfoSchema),
|
|
171
|
-
total_count: z.number(),
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// For files < 50MB, use direct upload
|
|
175
|
-
const result = await ctx.integrations.box.apiRequest(
|
|
176
|
-
{
|
|
177
|
-
method: "POST",
|
|
178
|
-
path: "/files/content",
|
|
179
|
-
headers: {
|
|
180
|
-
"Content-Type": "multipart/form-data",
|
|
181
|
-
},
|
|
182
|
-
body: {
|
|
183
|
-
attributes: JSON.stringify({
|
|
184
|
-
name: "new-file.txt",
|
|
185
|
-
parent: { id: folderId },
|
|
186
|
-
}),
|
|
187
|
-
file: fileContent,
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
{ response: UploadResponseSchema },
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const uploadedFile = result.entries[0];
|
|
194
|
-
console.log(`Uploaded: ${uploadedFile.name} (${uploadedFile.id})`);
|
|
195
|
-
```
|
|
196
|
-
|
|
197
166
|
### Create a Folder
|
|
198
167
|
|
|
199
168
|
```typescript
|
|
@@ -399,7 +368,7 @@ All methods accept an optional `metadata` parameter as the last argument for dia
|
|
|
399
368
|
```typescript
|
|
400
369
|
// WRONG - These methods do not exist
|
|
401
370
|
await box.listFiles({ ... });
|
|
402
|
-
await box.
|
|
371
|
+
await box.createFolder({ ... });
|
|
403
372
|
|
|
404
373
|
// CORRECT - Use apiRequest
|
|
405
374
|
await ctx.integrations.box.apiRequest(
|
|
@@ -457,20 +426,9 @@ const result = await ctx.integrations.box.apiRequest(
|
|
|
457
426
|
);
|
|
458
427
|
```
|
|
459
428
|
|
|
460
|
-
###
|
|
461
|
-
|
|
462
|
-
File uploads return an array in `entries`:
|
|
429
|
+
### File Uploads Are Not Available
|
|
463
430
|
|
|
464
|
-
|
|
465
|
-
// Response structure
|
|
466
|
-
const schema = z.object({
|
|
467
|
-
entries: z.array(FileInfoSchema), // Array even for single file
|
|
468
|
-
total_count: z.number(),
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
// Access the uploaded file
|
|
472
|
-
const uploadedFile = result.entries[0];
|
|
473
|
-
```
|
|
431
|
+
Box serves every upload endpoint (`/files/content`, `/files/upload_sessions`) from `https://upload.box.com/api/2.0`. This integration's base URL is fixed by the Box integration template to `https://api.box.com/2.0` and `apiRequest` cannot target another host, so uploads through `ctx.integrations.box` fail. Do not generate upload code for this integration. If an app must upload to Box, ask the user to configure a REST API integration with base URL `https://upload.box.com/api/2.0` using the same OAuth client, then call it through that integration.
|
|
474
432
|
|
|
475
433
|
### If-Match Header for Updates
|
|
476
434
|
|
|
@@ -521,20 +479,6 @@ async function getAllItems(box: BoxClient, folderId: string) {
|
|
|
521
479
|
}
|
|
522
480
|
```
|
|
523
481
|
|
|
524
|
-
### File Size Limits
|
|
525
|
-
|
|
526
|
-
Different upload methods for different file sizes:
|
|
527
|
-
|
|
528
|
-
```typescript
|
|
529
|
-
// < 50MB - Use /files/content (simple upload)
|
|
530
|
-
// > 50MB - Use chunked upload session
|
|
531
|
-
|
|
532
|
-
// Chunked upload for large files:
|
|
533
|
-
// 1. POST /files/upload_sessions - Create session
|
|
534
|
-
// 2. PUT /files/upload_sessions/:id - Upload parts
|
|
535
|
-
// 3. POST /files/upload_sessions/:id/commit - Finalize
|
|
536
|
-
```
|
|
537
|
-
|
|
538
482
|
### Shared Link Access Levels
|
|
539
483
|
|
|
540
484
|
```typescript
|
|
@@ -11,7 +11,10 @@ import type { z } from "zod";
|
|
|
11
11
|
|
|
12
12
|
import type { Plugin as CosmosDBPlugin } from "@superblocksteam/types/dist/src/plugins/cosmosdb/v1/plugin_pb";
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
RestApiValidationError,
|
|
16
|
+
restApiValidationErrorFromZodError,
|
|
17
|
+
} from "../../errors.js";
|
|
15
18
|
import { IntegrationError } from "../../runtime/errors.js";
|
|
16
19
|
import type { QueryExecutor, TraceMetadata } from "../registry.js";
|
|
17
20
|
import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
|
|
@@ -80,12 +83,10 @@ export class CosmosDBClientImpl
|
|
|
80
83
|
const parseResult = schema.safeParse(result);
|
|
81
84
|
|
|
82
85
|
if (!parseResult.success) {
|
|
83
|
-
throw
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
data: result,
|
|
88
|
-
},
|
|
86
|
+
throw restApiValidationErrorFromZodError(
|
|
87
|
+
"Result validation failed",
|
|
88
|
+
parseResult.error,
|
|
89
|
+
result,
|
|
89
90
|
);
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -859,6 +859,42 @@ describe("resolveIntegrationDocumentation", () => {
|
|
|
859
859
|
expect(docs).not.toContain("does not support the `responseType`");
|
|
860
860
|
});
|
|
861
861
|
|
|
862
|
+
it("documents request bodyType only for agents whose sdk-api supports it", async () => {
|
|
863
|
+
const docs = await resolveIntegrationDocumentation("restapiintegration", {
|
|
864
|
+
sdkVersion: "0.0.7",
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
expect(docs).toContain('responseType: "binary"');
|
|
868
|
+
expect(docs).toContain('bodyType: "formUrlencoded"');
|
|
869
|
+
expect(docs).toContain('bodyType: "multipartForm"');
|
|
870
|
+
expect(docs).toContain('bodyType: "binary"');
|
|
871
|
+
expect(docs).not.toContain("does not support the `bodyType`");
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
it("documents binary request bodies with direct raw file bytes", async () => {
|
|
875
|
+
const docs = await resolveIntegrationDocumentation("restapiintegration", {
|
|
876
|
+
sdkVersion: "0.0.7",
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
expect(docs).not.toContain("Buffer.from(raw");
|
|
880
|
+
expect(docs).not.toContain("Buffer.isBuffer(raw");
|
|
881
|
+
expect(docs).toContain(
|
|
882
|
+
'pass the `Uint8Array` returned by `await file.readContentsAsync("raw")` directly as the body',
|
|
883
|
+
);
|
|
884
|
+
expect(docs).toContain('bodyType: "binary"');
|
|
885
|
+
expect(docs).toContain('body: await file.readContentsAsync("raw")');
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
it("notes bodyType non-support before sdk-api request body support", async () => {
|
|
889
|
+
const docs = await resolveIntegrationDocumentation("restapiintegration", {
|
|
890
|
+
sdkVersion: "0.0.6",
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
expect(docs).toContain('responseType: "binary"');
|
|
894
|
+
expect(docs).toContain("does not support the `bodyType` option");
|
|
895
|
+
expect(docs).not.toContain('bodyType: "formUrlencoded"');
|
|
896
|
+
});
|
|
897
|
+
|
|
862
898
|
it("notes non-support instead of documenting responseType on older agents", async () => {
|
|
863
899
|
const docs = await resolveIntegrationDocumentation("restapiintegration", {
|
|
864
900
|
sdkVersion: "0.0.2",
|
|
@@ -869,13 +905,106 @@ describe("resolveIntegrationDocumentation", () => {
|
|
|
869
905
|
expect(docs).not.toContain('responseType: "binary"');
|
|
870
906
|
});
|
|
871
907
|
|
|
872
|
-
it("stays silent about responseType when the agent reports no sdk-api version", async () => {
|
|
908
|
+
it("stays silent about responseType and request bodyType when the agent reports no sdk-api version", async () => {
|
|
873
909
|
const docs = await resolveIntegrationDocumentation("restapiintegration");
|
|
874
910
|
|
|
875
911
|
expect(docs).not.toContain("responseType");
|
|
912
|
+
expect(docs).not.toContain("bodyType");
|
|
876
913
|
});
|
|
877
914
|
});
|
|
878
915
|
|
|
916
|
+
describe("non-JSON request body gating (real docs)", () => {
|
|
917
|
+
it.each([
|
|
918
|
+
{
|
|
919
|
+
pluginId: "jira",
|
|
920
|
+
supportedMarker: 'bodyType: "multipartForm"',
|
|
921
|
+
unsupportedMarker: "cannot upload",
|
|
922
|
+
expectFileRead: true,
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
pluginId: "stabilityai",
|
|
926
|
+
supportedMarker: 'bodyType: "multipartForm"',
|
|
927
|
+
unsupportedMarker: "cannot upload",
|
|
928
|
+
expectFileRead: true,
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
pluginId: "dropbox",
|
|
932
|
+
supportedMarker: 'bodyType: "binary"',
|
|
933
|
+
unsupportedMarker: "cannot upload",
|
|
934
|
+
expectFileRead: true,
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
pluginId: "elasticsearch",
|
|
938
|
+
supportedMarker: 'bodyType: "raw"',
|
|
939
|
+
unsupportedMarker: "cannot call `/_bulk`",
|
|
940
|
+
expectFileRead: false,
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
pluginId: "stripe",
|
|
944
|
+
supportedMarker: 'bodyType: "formUrlencoded"',
|
|
945
|
+
unsupportedMarker: "cannot send that encoding",
|
|
946
|
+
expectFileRead: false,
|
|
947
|
+
},
|
|
948
|
+
])(
|
|
949
|
+
"gates $pluginId docs on sdk-api body type support",
|
|
950
|
+
async ({
|
|
951
|
+
pluginId,
|
|
952
|
+
supportedMarker,
|
|
953
|
+
unsupportedMarker,
|
|
954
|
+
expectFileRead,
|
|
955
|
+
}) => {
|
|
956
|
+
const supported = await resolveIntegrationDocumentation(pluginId, {
|
|
957
|
+
sdkVersion: "0.0.7",
|
|
958
|
+
});
|
|
959
|
+
expect(supported).toContain(supportedMarker);
|
|
960
|
+
expect(supported).not.toContain(unsupportedMarker);
|
|
961
|
+
if (expectFileRead) {
|
|
962
|
+
expect(supported).toContain('readContentsAsync("raw")');
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
const unsupported = await resolveIntegrationDocumentation(pluginId, {
|
|
966
|
+
sdkVersion: "0.0.6",
|
|
967
|
+
});
|
|
968
|
+
expect(unsupported).toContain(unsupportedMarker);
|
|
969
|
+
expect(unsupported).not.toContain("bodyType");
|
|
970
|
+
|
|
971
|
+
const unversioned = await resolveIntegrationDocumentation(pluginId);
|
|
972
|
+
expect(unversioned).not.toContain("bodyType");
|
|
973
|
+
expect(unversioned).not.toContain(unsupportedMarker);
|
|
974
|
+
|
|
975
|
+
for (const docs of [supported, unsupported, unversioned]) {
|
|
976
|
+
expect(docs).not.toContain("body: fileContent");
|
|
977
|
+
expect(docs).not.toContain("file: fileContent");
|
|
978
|
+
expect(docs).not.toContain('"Content-Type": "multipart/form-data"');
|
|
979
|
+
expect(docs).not.toContain('body: "File content here"');
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
it.each([
|
|
985
|
+
{ pluginId: "box", host: "upload.box.com/api/2.0" },
|
|
986
|
+
{ pluginId: "googledrive", host: "googleapis.com/upload/drive/v3" },
|
|
987
|
+
])(
|
|
988
|
+
"tells $pluginId callers uploads are unavailable at every sdk-api version",
|
|
989
|
+
async ({ pluginId, host }) => {
|
|
990
|
+
for (const options of [
|
|
991
|
+
{ sdkVersion: "0.0.7" as const },
|
|
992
|
+
{ sdkVersion: "0.0.6" as const },
|
|
993
|
+
undefined,
|
|
994
|
+
]) {
|
|
995
|
+
const docs = await resolveIntegrationDocumentation(pluginId, options);
|
|
996
|
+
expect(docs).toContain("Uploads Are Not Available");
|
|
997
|
+
expect(docs).toContain(host);
|
|
998
|
+
expect(docs).not.toContain("bodyType");
|
|
999
|
+
expect(docs).not.toContain('readContentsAsync("raw")');
|
|
1000
|
+
expect(docs).not.toContain("file: fileContent");
|
|
1001
|
+
expect(docs).not.toContain('body: "File content here"');
|
|
1002
|
+
expect(docs).not.toContain('path: "/upload/drive/v3/files"');
|
|
1003
|
+
}
|
|
1004
|
+
},
|
|
1005
|
+
);
|
|
1006
|
+
});
|
|
1007
|
+
|
|
879
1008
|
it("blocks overlay paths outside the plugin directory", async () => {
|
|
880
1009
|
const integrationsDirectory = createPluginDocsFixture("dropbox", {
|
|
881
1010
|
"README.md": "base",
|
|
@@ -123,41 +123,6 @@ const result = await ctx.integrations.dropbox.apiRequest(
|
|
|
123
123
|
);
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
### Upload a File
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
const UploadResponseSchema = z.object({
|
|
130
|
-
name: z.string(),
|
|
131
|
-
path_lower: z.string(),
|
|
132
|
-
path_display: z.string(),
|
|
133
|
-
id: z.string(),
|
|
134
|
-
client_modified: z.string(),
|
|
135
|
-
server_modified: z.string(),
|
|
136
|
-
size: z.number(),
|
|
137
|
-
content_hash: z.string(),
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const result = await ctx.integrations.dropbox.apiRequest(
|
|
141
|
-
{
|
|
142
|
-
method: "POST",
|
|
143
|
-
path: "/2/files/upload",
|
|
144
|
-
headers: {
|
|
145
|
-
"Dropbox-API-Arg": JSON.stringify({
|
|
146
|
-
path: "/Documents/new-file.txt",
|
|
147
|
-
mode: "add", // add, overwrite, or update
|
|
148
|
-
autorename: true,
|
|
149
|
-
mute: false,
|
|
150
|
-
}),
|
|
151
|
-
"Content-Type": "application/octet-stream",
|
|
152
|
-
},
|
|
153
|
-
body: "File content here",
|
|
154
|
-
},
|
|
155
|
-
{ response: UploadResponseSchema },
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
console.log(`Uploaded: ${result.path_display}`);
|
|
159
|
-
```
|
|
160
|
-
|
|
161
126
|
### Create a Folder
|
|
162
127
|
|
|
163
128
|
```typescript
|
|
@@ -369,27 +334,9 @@ const path = "/Documents/file.txt";
|
|
|
369
334
|
const path = "";
|
|
370
335
|
```
|
|
371
336
|
|
|
372
|
-
### Upload Headers
|
|
373
|
-
|
|
374
|
-
File uploads require special headers:
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
// Upload requires Dropbox-API-Arg header
|
|
378
|
-
const headers = {
|
|
379
|
-
"Dropbox-API-Arg": JSON.stringify({
|
|
380
|
-
path: "/path/to/file.txt",
|
|
381
|
-
mode: "add",
|
|
382
|
-
}),
|
|
383
|
-
"Content-Type": "application/octet-stream",
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
// The body is raw file content, not JSON
|
|
387
|
-
const body = "Raw file content";
|
|
388
|
-
```
|
|
389
|
-
|
|
390
337
|
### Download Headers
|
|
391
338
|
|
|
392
|
-
Downloads
|
|
339
|
+
Downloads use special headers, not the body, to carry parameters:
|
|
393
340
|
|
|
394
341
|
```typescript
|
|
395
342
|
// Download parameters go in header, not body
|
|
@@ -453,23 +400,6 @@ async function listAllFiles(dropbox: DropboxClient, path: string) {
|
|
|
453
400
|
}
|
|
454
401
|
```
|
|
455
402
|
|
|
456
|
-
### Write Conflict Modes
|
|
457
|
-
|
|
458
|
-
Choose the right mode for uploads:
|
|
459
|
-
|
|
460
|
-
```typescript
|
|
461
|
-
const modes = {
|
|
462
|
-
add: "Never overwrite, fail if file exists",
|
|
463
|
-
overwrite: "Always overwrite existing file",
|
|
464
|
-
update: "Update only if rev matches (optimistic locking)",
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
const body = {
|
|
468
|
-
path: "/file.txt",
|
|
469
|
-
mode: "add", // or { ".tag": "update", "update": revisionId }
|
|
470
|
-
};
|
|
471
|
-
```
|
|
472
|
-
|
|
473
403
|
### Case Sensitivity
|
|
474
404
|
|
|
475
405
|
Dropbox paths are case-insensitive but preserve case:
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
## Upload a File
|
|
2
|
+
|
|
3
|
+
This agent's sdk-api cannot upload files to Dropbox through `apiRequest()` because it serializes every body as JSON, and `/2/files/upload` expects the raw file bytes. Do not generate upload code until the agent is upgraded. Listing, metadata, download, folder, and sharing calls work as documented above.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
## Upload a File
|
|
2
|
+
|
|
3
|
+
`/2/files/upload` takes the file bytes as the request body and the upload parameters in the `Dropbox-API-Arg` header. Pass `bodyType: "binary"` with the `Uint8Array` returned by `await file.readContentsAsync("raw")`. The SDK adds `Content-Type: application/octet-stream`, which Dropbox requires. `Dropbox-API-Arg` must be ASCII-only, so escape non-ASCII filenames before building it (Dropbox rejects a raw UTF-8 header).
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
const UploadResponseSchema = z.object({
|
|
7
|
+
name: z.string(),
|
|
8
|
+
path_lower: z.string(),
|
|
9
|
+
path_display: z.string(),
|
|
10
|
+
id: z.string(),
|
|
11
|
+
client_modified: z.string(),
|
|
12
|
+
server_modified: z.string(),
|
|
13
|
+
size: z.number(),
|
|
14
|
+
content_hash: z.string(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const file = input.upload.files[0];
|
|
18
|
+
|
|
19
|
+
// Dropbox-API-Arg is an HTTP header value, which must be ASCII-only.
|
|
20
|
+
// Escape any non-ASCII character (e.g. accented letters) as \uXXXX.
|
|
21
|
+
function toDropboxApiArg(value: unknown): string {
|
|
22
|
+
return JSON.stringify(value).replace(
|
|
23
|
+
/[\u0080-\uffff]/g,
|
|
24
|
+
(char) => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = await ctx.integrations.dropbox.apiRequest(
|
|
29
|
+
{
|
|
30
|
+
method: "POST",
|
|
31
|
+
path: "/2/files/upload",
|
|
32
|
+
headers: {
|
|
33
|
+
"Dropbox-API-Arg": toDropboxApiArg({
|
|
34
|
+
path: `/Documents/${file.name}`,
|
|
35
|
+
mode: "add", // add, overwrite, or update
|
|
36
|
+
autorename: true,
|
|
37
|
+
mute: false,
|
|
38
|
+
}),
|
|
39
|
+
},
|
|
40
|
+
bodyType: "binary",
|
|
41
|
+
body: await file.readContentsAsync("raw"),
|
|
42
|
+
},
|
|
43
|
+
{ response: UploadResponseSchema },
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
console.log(`Uploaded: ${result.path_display}`);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For text generated in the API rather than an uploaded file, use `bodyType: "raw"` with a string body and set `Content-Type: application/octet-stream` yourself, since raw mode adds no headers:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const notesText = ["# Meeting notes", "", ...input.notes].join("\n");
|
|
53
|
+
|
|
54
|
+
await ctx.integrations.dropbox.apiRequest(
|
|
55
|
+
{
|
|
56
|
+
method: "POST",
|
|
57
|
+
path: "/2/files/upload",
|
|
58
|
+
headers: {
|
|
59
|
+
"Dropbox-API-Arg": JSON.stringify({
|
|
60
|
+
path: "/Documents/notes.txt",
|
|
61
|
+
mode: "overwrite",
|
|
62
|
+
}),
|
|
63
|
+
"Content-Type": "application/octet-stream",
|
|
64
|
+
},
|
|
65
|
+
bodyType: "raw",
|
|
66
|
+
body: notesText,
|
|
67
|
+
},
|
|
68
|
+
{ response: UploadResponseSchema },
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Do not send the file as a JSON body. Without `bodyType`, `apiRequest()` rejects a `Uint8Array`/`Buffer` body outright, and JSON-encodes a string body instead of sending it raw, so Dropbox would store the quoted JSON text instead of the file content. `readContentsAsync("binary")` returns base64 text, not bytes; use `readContentsAsync("raw")` for uploads.
|
|
73
|
+
|
|
74
|
+
### Write Conflict Modes
|
|
75
|
+
|
|
76
|
+
Choose the right `mode` for uploads:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const modes = {
|
|
80
|
+
add: "Never overwrite, fail if file exists",
|
|
81
|
+
overwrite: "Always overwrite existing file",
|
|
82
|
+
update: "Update only if rev matches (optimistic locking)",
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const dropboxApiArg = {
|
|
86
|
+
path: "/file.txt",
|
|
87
|
+
mode: "add", // or { ".tag": "update", "update": revisionId }
|
|
88
|
+
};
|
|
89
|
+
```
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import type { z } from "zod";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
RestApiValidationError,
|
|
13
|
+
restApiValidationErrorFromZodError,
|
|
14
|
+
} from "../../errors.js";
|
|
12
15
|
import { IntegrationError } from "../../runtime/errors.js";
|
|
13
16
|
import type { QueryExecutor, TraceMetadata } from "../registry.js";
|
|
14
17
|
import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
|
|
@@ -85,12 +88,10 @@ export class DynamoDBClientImpl
|
|
|
85
88
|
const parseResult = schema.safeParse(result);
|
|
86
89
|
|
|
87
90
|
if (!parseResult.success) {
|
|
88
|
-
throw
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
data: result,
|
|
93
|
-
},
|
|
91
|
+
throw restApiValidationErrorFromZodError(
|
|
92
|
+
"Result validation failed",
|
|
93
|
+
parseResult.error,
|
|
94
|
+
result,
|
|
94
95
|
);
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -226,70 +226,6 @@ const result = await ctx.integrations.es.apiRequest(
|
|
|
226
226
|
);
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
### Bulk Operations
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
const BulkResponseSchema = z.object({
|
|
233
|
-
took: z.number(),
|
|
234
|
-
errors: z.boolean(),
|
|
235
|
-
items: z.array(
|
|
236
|
-
z.object({
|
|
237
|
-
index: z
|
|
238
|
-
.object({
|
|
239
|
-
_index: z.string(),
|
|
240
|
-
_id: z.string(),
|
|
241
|
-
status: z.number(),
|
|
242
|
-
result: z.string().optional(),
|
|
243
|
-
error: z
|
|
244
|
-
.object({
|
|
245
|
-
type: z.string(),
|
|
246
|
-
reason: z.string(),
|
|
247
|
-
})
|
|
248
|
-
.optional(),
|
|
249
|
-
})
|
|
250
|
-
.optional(),
|
|
251
|
-
delete: z
|
|
252
|
-
.object({
|
|
253
|
-
_index: z.string(),
|
|
254
|
-
_id: z.string(),
|
|
255
|
-
status: z.number(),
|
|
256
|
-
result: z.string().optional(),
|
|
257
|
-
})
|
|
258
|
-
.optional(),
|
|
259
|
-
}),
|
|
260
|
-
),
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// Bulk index
|
|
264
|
-
const result = await ctx.integrations.es.apiRequest(
|
|
265
|
-
{
|
|
266
|
-
method: "POST",
|
|
267
|
-
path: "/_bulk",
|
|
268
|
-
headers: {
|
|
269
|
-
"Content-Type": "application/x-ndjson",
|
|
270
|
-
},
|
|
271
|
-
body:
|
|
272
|
-
[
|
|
273
|
-
{ index: { _index: "products", _id: "1" } },
|
|
274
|
-
{ name: "Product 1", price: 10 },
|
|
275
|
-
{ index: { _index: "products", _id: "2" } },
|
|
276
|
-
{ name: "Product 2", price: 20 },
|
|
277
|
-
]
|
|
278
|
-
.map(JSON.stringify)
|
|
279
|
-
.join("\n") + "\n",
|
|
280
|
-
},
|
|
281
|
-
{ response: BulkResponseSchema },
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
if (result.errors) {
|
|
285
|
-
result.items.forEach((item) => {
|
|
286
|
-
if (item.index?.error) {
|
|
287
|
-
console.error(`Failed to index: ${item.index.error.reason}`);
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
229
|
### Aggregations
|
|
294
230
|
|
|
295
231
|
```typescript
|
|
@@ -493,22 +429,6 @@ const body = {
|
|
|
493
429
|
};
|
|
494
430
|
```
|
|
495
431
|
|
|
496
|
-
### Bulk Format
|
|
497
|
-
|
|
498
|
-
Bulk operations require NDJSON format:
|
|
499
|
-
|
|
500
|
-
```typescript
|
|
501
|
-
// WRONG - Regular JSON array
|
|
502
|
-
const body = [{ index: { _index: "products" } }, { name: "Product" }];
|
|
503
|
-
|
|
504
|
-
// CORRECT - Newline-delimited JSON
|
|
505
|
-
const body = '{"index":{"_index":"products"}}\n{"name":"Product"}\n';
|
|
506
|
-
|
|
507
|
-
// Or build programmatically
|
|
508
|
-
const operations = [{ index: { _index: "products" } }, { name: "Product" }];
|
|
509
|
-
const body = operations.map(JSON.stringify).join("\n") + "\n";
|
|
510
|
-
```
|
|
511
|
-
|
|
512
432
|
### Total Hits Accuracy
|
|
513
433
|
|
|
514
434
|
For large result sets, total is approximate by default:
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"pluginId": "elasticsearch",
|
|
3
3
|
"base": "README.md",
|
|
4
|
-
"overlays": [
|
|
4
|
+
"overlays": [
|
|
5
|
+
{
|
|
6
|
+
"file": "overlays/bulk.md",
|
|
7
|
+
"sdkVersionRange": ">=0.0.7"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"file": "overlays/bulk-unsupported.md",
|
|
11
|
+
"sdkVersionRange": "<0.0.7"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
5
14
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
## Bulk Operations
|
|
2
|
+
|
|
3
|
+
This agent's sdk-api cannot call `/_bulk` through `apiRequest()` because it serializes every body as JSON, and the bulk API requires newline-delimited JSON. Do not generate bulk code until the agent is upgraded; index, update, or delete documents one call at a time as documented above.
|