Skip to content

Commit 9a9ea62

Browse files
feat: Expose method for making custom HTTP requests, convert FetchOptions into a class (box/box-codegen#610) (#431)
1 parent 69857f5 commit 9a9ea62

File tree

84 files changed

+5748
-4675
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+5748
-4675
lines changed

.codegen.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "1c0a1c0", "specHash": "544d370", "version": "1.7.0" }
1+
{ "engineHash": "35ca680", "specHash": "544d370", "version": "1.7.0" }

docs/client.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ divided across resource managers.
66
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
77
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
88

9-
- [Client](#client)
9+
- [Make custom HTTP request](#make-custom-http-request)
10+
- [JSON request](#json-request)
11+
- [Multi-part request](#multi-part-request)
12+
- [Binary response](#binary-response)
1013
- [Additional headers](#additional-headers)
1114
- [As-User header](#as-user-header)
1215
- [Suppress notifications](#suppress-notifications)
@@ -18,6 +21,51 @@ divided across resource managers.
1821

1922
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2023

24+
# Make custom HTTP request
25+
26+
You can make custom HTTP requests using the `client.makeRequest()` method.
27+
This method allows you to make any HTTP request to the Box API. It will automatically use authentication and
28+
network configuration settings from the client.
29+
The method accepts a `FetchOptions` object as an argument and returns a `FetchResponse` object.
30+
31+
## JSON request
32+
33+
The following example demonstrates how to make a custom POST request to create a new folder in the root folder.
34+
35+
```js
36+
const response: FetchResponse = await client.makeRequest(
37+
{ method: "POST",
38+
url: "https://api.box.com/2.0/folders",
39+
data: {name: newFolderName, parent: {id: "0"}}
40+
} satisfies FetchOptionsInput
41+
);
42+
console.log('Received status code: ', response.status)
43+
console.log('Created folder name: ', getSdValueByKey(response.content, "name"))
44+
```
45+
46+
## Multi-part request
47+
48+
The following example demonstrates how to make a custom multipart request that uploads a file to a folder.
49+
50+
```js
51+
const multipartAttributes = {name: "newFileName", parent: { id: "0" }};
52+
const uploadFileResponse: FetchResponse = await client.makeRequest({ method: "POST", url: "https://upload.box.com/api/2.0/files/content", contentType: "multipart/form-data", multipartData: [{ partName: "attributes", data: multipartAttributes } satisfies MultipartItem, { partName: "file", fileStream: fileContentStream } satisfies MultipartItem] } satisfies FetchOptionsInput);
53+
54+
console.log('Received status code: ', response.status)
55+
```
56+
57+
## Binary response
58+
59+
The following example demonstrates how to make a custom request that expects a binary response.
60+
It is required to specify the `responseFormat` parameter in the `FetchOptions` object to "binary".
61+
62+
```js
63+
const response: FetchResponse = await client.makeRequest({ method: "GET", url: "".concat("https://api.box.com/2.0/files/", uploadedFile.id, "/content") as string, responseFormat: "binary" as ResponseFormat } satisfies FetchOptionsInput);
64+
console.log('Received status code: ', response.status)
65+
const fileWriteStream = fs.createWriteStream('file.pdf');
66+
response.content.pipe(fileWriteStream);
67+
```
68+
2169
# Additional headers
2270

2371
BoxClient provides a convenient methods, which allow passing additional headers, which will be included

src/client.generated.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { serializeBaseUrls } from './networking/baseUrls.generated.js';
22
import { deserializeBaseUrls } from './networking/baseUrls.generated.js';
3+
import { FetchOptionsInput } from './networking/fetchOptions.generated.js';
34
import { BaseUrlsInput } from './networking/baseUrls.generated.js';
45
import { AuthorizationManager } from './managers/authorization.generated.js';
56
import { FilesManager } from './managers/files.generated.js';
@@ -77,6 +78,9 @@ import { BaseUrls } from './networking/baseUrls.generated.js';
7778
import { ProxyConfig } from './networking/proxyConfig.generated.js';
7879
import { AgentOptions } from './internal/utils.js';
7980
import { Interceptor } from './networking/interceptors.generated.js';
81+
import { FetchOptions } from './networking/fetchOptions.generated.js';
82+
import { FetchResponse } from './networking/fetchResponse.generated.js';
83+
import { fetch } from './networking/fetch.js';
8084
import { SerializedData } from './serialization/json.js';
8185
import { sdIsEmpty } from './serialization/json.js';
8286
import { sdIsBoolean } from './serialization/json.js';
@@ -233,6 +237,7 @@ export class BoxClient {
233237
| 'integrationMappings'
234238
| 'ai'
235239
| 'networkSession'
240+
| 'makeRequest'
236241
| 'withAsUserHeader'
237242
| 'withSuppressedNotifications'
238243
| 'withExtraHeaders'
@@ -536,6 +541,49 @@ export class BoxClient {
536541
networkSession: this.networkSession,
537542
});
538543
}
544+
/**
545+
* Make a custom http request using the client authentication and network session.
546+
* @param {FetchOptionsInput} fetchOptionsInput Options to be passed to the fetch call
547+
* @returns {Promise<FetchResponse>}
548+
*/
549+
async makeRequest(
550+
fetchOptionsInput: FetchOptionsInput,
551+
): Promise<FetchResponse> {
552+
const fetchOptions: FetchOptions = new FetchOptions({
553+
url: fetchOptionsInput.url,
554+
method: fetchOptionsInput.method,
555+
params: fetchOptionsInput.params,
556+
headers: fetchOptionsInput.headers,
557+
data: fetchOptionsInput.data,
558+
fileStream: fetchOptionsInput.fileStream,
559+
multipartData: fetchOptionsInput.multipartData,
560+
contentType: fetchOptionsInput.contentType,
561+
responseFormat: fetchOptionsInput.responseFormat,
562+
auth: fetchOptionsInput.auth,
563+
networkSession: fetchOptionsInput.networkSession,
564+
cancellationToken: fetchOptionsInput.cancellationToken,
565+
});
566+
const auth: Authentication =
567+
fetchOptions.auth == void 0 ? this.auth : fetchOptions.auth!;
568+
const networkSession: NetworkSession =
569+
fetchOptions.networkSession == void 0
570+
? this.networkSession
571+
: fetchOptions.networkSession!;
572+
const enrichedFetchOptions: FetchOptions = new FetchOptions({
573+
auth: auth,
574+
networkSession: networkSession,
575+
url: fetchOptions.url,
576+
method: fetchOptions.method,
577+
params: fetchOptions.params,
578+
headers: fetchOptions.headers,
579+
data: fetchOptions.data,
580+
fileStream: fetchOptions.fileStream,
581+
multipartData: fetchOptions.multipartData,
582+
contentType: fetchOptions.contentType,
583+
responseFormat: fetchOptions.responseFormat,
584+
});
585+
return (await fetch(enrichedFetchOptions)) as FetchResponse;
586+
}
539587
/**
540588
* Create a new client to impersonate user with the provided ID. All calls made with the new client will be made in context of the impersonated user, leaving the original client unmodified.
541589
* @param {string} userId ID of an user to impersonate

src/internal/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Buffer } from 'buffer';
22
import type { Readable } from 'stream';
33
import { v4 as uuidv4 } from 'uuid';
44
import { SignJWT, importPKCS8 } from 'jose';
5+
import { sdIsMap, SerializedData } from '../serialization/json';
56

67
export function isBrowser() {
78
return (

src/managers/ai.generated.ts

Lines changed: 92 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { serializeAiExtractResponse } from '../schemas/aiExtractResponse.generat
1616
import { deserializeAiExtractResponse } from '../schemas/aiExtractResponse.generated.js';
1717
import { serializeAiExtractStructured } from '../schemas/aiExtractStructured.generated.js';
1818
import { deserializeAiExtractStructured } from '../schemas/aiExtractStructured.generated.js';
19+
import { ResponseFormat } from '../networking/fetchOptions.generated.js';
1920
import { AiResponseFull } from '../schemas/aiResponseFull.generated.js';
2021
import { ClientError } from '../schemas/clientError.generated.js';
2122
import { AiAsk } from '../schemas/aiAsk.generated.js';
@@ -31,8 +32,8 @@ import { prepareParams } from '../internal/utils.js';
3132
import { toString } from '../internal/utils.js';
3233
import { ByteStream } from '../internal/utils.js';
3334
import { CancellationToken } from '../internal/utils.js';
34-
import { FetchOptions } from '../networking/fetch.js';
35-
import { FetchResponse } from '../networking/fetch.js';
35+
import { FetchOptions } from '../networking/fetchOptions.generated.js';
36+
import { FetchResponse } from '../networking/fetchResponse.generated.js';
3637
import { fetch } from '../networking/fetch.js';
3738
import { SerializedData } from '../serialization/json.js';
3839
import { sdToJson } from '../serialization/json.js';
@@ -331,23 +332,25 @@ export class AiManager {
331332
const headersMap: {
332333
readonly [key: string]: string;
333334
} = prepareParams({ ...{}, ...headers.extraHeaders });
334-
const response: FetchResponse = (await fetch({
335-
url: ''.concat(
336-
this.networkSession.baseUrls.baseUrl,
337-
'/2.0/ai/ask',
338-
) as string,
339-
method: 'POST',
340-
headers: headersMap,
341-
data: serializeAiAsk(requestBody),
342-
contentType: 'application/json',
343-
responseFormat: 'json',
344-
auth: this.auth,
345-
networkSession: this.networkSession,
346-
cancellationToken: cancellationToken,
347-
} satisfies FetchOptions)) as FetchResponse;
335+
const response: FetchResponse = (await fetch(
336+
new FetchOptions({
337+
url: ''.concat(
338+
this.networkSession.baseUrls.baseUrl,
339+
'/2.0/ai/ask',
340+
) as string,
341+
method: 'POST',
342+
headers: headersMap,
343+
data: serializeAiAsk(requestBody),
344+
contentType: 'application/json',
345+
responseFormat: 'json' as ResponseFormat,
346+
auth: this.auth,
347+
networkSession: this.networkSession,
348+
cancellationToken: cancellationToken,
349+
}),
350+
)) as FetchResponse;
348351
return {
349-
...deserializeAiResponseFull(response.data),
350-
rawData: response.data,
352+
...deserializeAiResponseFull(response.data!),
353+
rawData: response.data!,
351354
};
352355
}
353356
/**
@@ -369,23 +372,25 @@ export class AiManager {
369372
const headersMap: {
370373
readonly [key: string]: string;
371374
} = prepareParams({ ...{}, ...headers.extraHeaders });
372-
const response: FetchResponse = (await fetch({
373-
url: ''.concat(
374-
this.networkSession.baseUrls.baseUrl,
375-
'/2.0/ai/text_gen',
376-
) as string,
377-
method: 'POST',
378-
headers: headersMap,
379-
data: serializeAiTextGen(requestBody),
380-
contentType: 'application/json',
381-
responseFormat: 'json',
382-
auth: this.auth,
383-
networkSession: this.networkSession,
384-
cancellationToken: cancellationToken,
385-
} satisfies FetchOptions)) as FetchResponse;
375+
const response: FetchResponse = (await fetch(
376+
new FetchOptions({
377+
url: ''.concat(
378+
this.networkSession.baseUrls.baseUrl,
379+
'/2.0/ai/text_gen',
380+
) as string,
381+
method: 'POST',
382+
headers: headersMap,
383+
data: serializeAiTextGen(requestBody),
384+
contentType: 'application/json',
385+
responseFormat: 'json' as ResponseFormat,
386+
auth: this.auth,
387+
networkSession: this.networkSession,
388+
cancellationToken: cancellationToken,
389+
}),
390+
)) as FetchResponse;
386391
return {
387-
...deserializeAiResponse(response.data),
388-
rawData: response.data,
392+
...deserializeAiResponse(response.data!),
393+
rawData: response.data!,
389394
};
390395
}
391396
/**
@@ -415,24 +420,26 @@ export class AiManager {
415420
const headersMap: {
416421
readonly [key: string]: string;
417422
} = prepareParams({ ...{}, ...headers.extraHeaders });
418-
const response: FetchResponse = (await fetch({
419-
url: ''.concat(
420-
this.networkSession.baseUrls.baseUrl,
421-
'/2.0/ai_agent_default',
422-
) as string,
423-
method: 'GET',
424-
params: queryParamsMap,
425-
headers: headersMap,
426-
responseFormat: 'json',
427-
auth: this.auth,
428-
networkSession: this.networkSession,
429-
cancellationToken: cancellationToken,
430-
} satisfies FetchOptions)) as FetchResponse;
423+
const response: FetchResponse = (await fetch(
424+
new FetchOptions({
425+
url: ''.concat(
426+
this.networkSession.baseUrls.baseUrl,
427+
'/2.0/ai_agent_default',
428+
) as string,
429+
method: 'GET',
430+
params: queryParamsMap,
431+
headers: headersMap,
432+
responseFormat: 'json' as ResponseFormat,
433+
auth: this.auth,
434+
networkSession: this.networkSession,
435+
cancellationToken: cancellationToken,
436+
}),
437+
)) as FetchResponse;
431438
return {
432439
...deserializeAiAgentAskOrAiAgentExtractOrAiAgentExtractStructuredOrAiAgentTextGen(
433-
response.data,
440+
response.data!,
434441
),
435-
rawData: response.data,
442+
rawData: response.data!,
436443
};
437444
}
438445
/**
@@ -455,23 +462,25 @@ export class AiManager {
455462
const headersMap: {
456463
readonly [key: string]: string;
457464
} = prepareParams({ ...{}, ...headers.extraHeaders });
458-
const response: FetchResponse = (await fetch({
459-
url: ''.concat(
460-
this.networkSession.baseUrls.baseUrl,
461-
'/2.0/ai/extract',
462-
) as string,
463-
method: 'POST',
464-
headers: headersMap,
465-
data: serializeAiExtract(requestBody),
466-
contentType: 'application/json',
467-
responseFormat: 'json',
468-
auth: this.auth,
469-
networkSession: this.networkSession,
470-
cancellationToken: cancellationToken,
471-
} satisfies FetchOptions)) as FetchResponse;
465+
const response: FetchResponse = (await fetch(
466+
new FetchOptions({
467+
url: ''.concat(
468+
this.networkSession.baseUrls.baseUrl,
469+
'/2.0/ai/extract',
470+
) as string,
471+
method: 'POST',
472+
headers: headersMap,
473+
data: serializeAiExtract(requestBody),
474+
contentType: 'application/json',
475+
responseFormat: 'json' as ResponseFormat,
476+
auth: this.auth,
477+
networkSession: this.networkSession,
478+
cancellationToken: cancellationToken,
479+
}),
480+
)) as FetchResponse;
472481
return {
473-
...deserializeAiResponse(response.data),
474-
rawData: response.data,
482+
...deserializeAiResponse(response.data!),
483+
rawData: response.data!,
475484
};
476485
}
477486
/**
@@ -497,23 +506,25 @@ export class AiManager {
497506
const headersMap: {
498507
readonly [key: string]: string;
499508
} = prepareParams({ ...{}, ...headers.extraHeaders });
500-
const response: FetchResponse = (await fetch({
501-
url: ''.concat(
502-
this.networkSession.baseUrls.baseUrl,
503-
'/2.0/ai/extract_structured',
504-
) as string,
505-
method: 'POST',
506-
headers: headersMap,
507-
data: serializeAiExtractStructured(requestBody),
508-
contentType: 'application/json',
509-
responseFormat: 'json',
510-
auth: this.auth,
511-
networkSession: this.networkSession,
512-
cancellationToken: cancellationToken,
513-
} satisfies FetchOptions)) as FetchResponse;
509+
const response: FetchResponse = (await fetch(
510+
new FetchOptions({
511+
url: ''.concat(
512+
this.networkSession.baseUrls.baseUrl,
513+
'/2.0/ai/extract_structured',
514+
) as string,
515+
method: 'POST',
516+
headers: headersMap,
517+
data: serializeAiExtractStructured(requestBody),
518+
contentType: 'application/json',
519+
responseFormat: 'json' as ResponseFormat,
520+
auth: this.auth,
521+
networkSession: this.networkSession,
522+
cancellationToken: cancellationToken,
523+
}),
524+
)) as FetchResponse;
514525
return {
515-
...deserializeAiExtractResponse(response.data),
516-
rawData: response.data,
526+
...deserializeAiExtractResponse(response.data!),
527+
rawData: response.data!,
517528
};
518529
}
519530
}

0 commit comments

Comments
 (0)