Skip to content

Commit ab5887c

Browse files
committed
Add support for redirect=manual
Fixes #613.
1 parent 5868177 commit ab5887c

File tree

2 files changed

+37
-19
lines changed

2 files changed

+37
-19
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,17 @@ for upgrading your code to arangojs v10.
390390
If the `onError` callback throws an error or returns a promise that is
391391
rejected, that error will be thrown instead.
392392

393+
- Added support for `config.fetchOptions.redirect` option ([#613](https://github.com/arangodb/arangojs/issues/613))
394+
395+
This option can now be used to specify the redirect mode for requests.
396+
397+
When set to `"manual"`, arangojs will throw an `HttpError` wrapping the
398+
redirect response instead of automatically following redirects.
399+
400+
Note that when set to `"error"`, the native fetch API will throw a
401+
non-specific error (usually a `TypeError`) that arangojs will wrap in a
402+
`FetchFailedError` instead.
403+
393404
- Added optional `ArangoError#request` property
394405

395406
This property is always present if the error has a `response` property. In

src/connection.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type Host = {
4141
| "hostUrl"
4242
| "expectBinary"
4343
| "isBinary"
44-
>,
44+
>
4545
) => Promise<globalThis.Response & { request: globalThis.Request }>;
4646
/**
4747
* @internal
@@ -134,8 +134,8 @@ function createHost(arangojsHostUrl: string, agentOptions?: any): Host {
134134
headers.set(
135135
"authorization",
136136
`Basic ${btoa(
137-
`${baseUrl.username || "root"}:${baseUrl.password || ""}`,
138-
)}`,
137+
`${baseUrl.username || "root"}:${baseUrl.password || ""}`
138+
)}`
139139
);
140140
}
141141
const abortController = new AbortController();
@@ -171,10 +171,15 @@ function createHost(arangojsHostUrl: string, agentOptions?: any): Host {
171171
request,
172172
arangojsHostUrl,
173173
});
174+
if (fetchOptions?.redirect === "manual" && isRedirect(response)) {
175+
throw new errors.HttpError(response);
176+
}
174177
} catch (e: unknown) {
175178
const cause = e instanceof Error ? e : new Error(String(e));
176179
let error: errors.NetworkError;
177-
if (signal.aborted) {
180+
if (cause instanceof errors.NetworkError) {
181+
error = cause;
182+
} else if (signal.aborted) {
178183
const reason =
179184
typeof signal.reason == "string" ? signal.reason : undefined;
180185
if (reason === REASON_TIMEOUT) {
@@ -276,7 +281,7 @@ const STATUS_CODE_DEFAULT_MESSAGES = {
276281

277282
type KnownStatusCode = keyof typeof STATUS_CODE_DEFAULT_MESSAGES;
278283
const KNOWN_STATUS_CODES = Object.keys(STATUS_CODE_DEFAULT_MESSAGES).map((k) =>
279-
Number(k),
284+
Number(k)
280285
) as KnownStatusCode[];
281286
const REDIRECT_CODES = [301, 302, 303, 307, 308] satisfies KnownStatusCode[];
282287
type RedirectStatusCode = (typeof REDIRECT_CODES)[number];
@@ -292,9 +297,11 @@ function isKnownStatusCode(code: number): code is KnownStatusCode {
292297
}
293298

294299
/**
300+
* @internal
301+
*
295302
* Indicates whether the given status code represents a redirect.
296303
*/
297-
export function isRedirect(response: ProcessedResponse): boolean {
304+
function isRedirect(response: ProcessedResponse): boolean {
298305
return REDIRECT_CODES.includes(response.status as RedirectStatusCode);
299306
}
300307

@@ -333,7 +340,7 @@ export type ArangoApiResponse<T> = T & ArangoResponseMetadata;
333340
* Indicates whether the given value represents an ArangoDB error response.
334341
*/
335342
export function isArangoErrorResponse(
336-
body: unknown,
343+
body: unknown
337344
): body is ArangoErrorResponse {
338345
if (!body || typeof body !== "object") return false;
339346
const obj = body as Record<string, unknown>;
@@ -576,7 +583,7 @@ export type CommonRequestOptions = {
576583
*/
577584
afterResponse?: (
578585
err: errors.NetworkError | null,
579-
res?: globalThis.Response & { request: globalThis.Request },
586+
res?: globalThis.Response & { request: globalThis.Request }
580587
) => void | Promise<void>;
581588
};
582589

@@ -726,11 +733,11 @@ export class Connection {
726733

727734
this._commonFetchOptions.headers.set(
728735
"x-arango-version",
729-
String(arangoVersion),
736+
String(arangoVersion)
730737
);
731738
this._commonFetchOptions.headers.set(
732739
"x-arango-driver",
733-
`arangojs/${process.env.ARANGOJS_VERSION} (cloud)`,
740+
`arangojs/${process.env.ARANGOJS_VERSION} (cloud)`
734741
);
735742

736743
this.addToHostList(URLS);
@@ -908,7 +915,7 @@ export class Connection {
908915
setBasicAuth(auth: configuration.BasicAuthCredentials) {
909916
this.setHeader(
910917
"authorization",
911-
`Basic ${btoa(`${auth.username}:${auth.password}`)}`,
918+
`Basic ${btoa(`${auth.username}:${auth.password}`)}`
912919
);
913920
}
914921

@@ -942,7 +949,7 @@ export class Connection {
942949
*/
943950
database(
944951
databaseName: string,
945-
database: databases.Database,
952+
database: databases.Database
946953
): databases.Database;
947954
/**
948955
* @internal
@@ -956,7 +963,7 @@ export class Connection {
956963
database(databaseName: string, database: null): undefined;
957964
database(
958965
databaseName: string,
959-
database?: databases.Database | null,
966+
database?: databases.Database | null
960967
): databases.Database | undefined {
961968
if (database === null) {
962969
this._databases.delete(databaseName);
@@ -987,7 +994,7 @@ export class Connection {
987994
const i = this._hostUrls.indexOf(url);
988995
if (i !== -1) return this._hosts[i];
989996
return createHost(url);
990-
}),
997+
})
991998
);
992999
this._hostUrls.splice(0, this._hostUrls.length, ...cleanUrls);
9931000
}
@@ -1003,10 +1010,10 @@ export class Connection {
10031010
*/
10041011
addToHostList(urls: string | string[]): string[] {
10051012
const cleanUrls = (Array.isArray(urls) ? urls : [urls]).map((url) =>
1006-
util.normalizeUrl(url),
1013+
util.normalizeUrl(url)
10071014
);
10081015
const newUrls = cleanUrls.filter(
1009-
(url) => this._hostUrls.indexOf(url) === -1,
1016+
(url) => this._hostUrls.indexOf(url) === -1
10101017
);
10111018
this._hostUrls.push(...newUrls);
10121019
this._hosts.push(...newUrls.map((url) => createHost(url)));
@@ -1127,8 +1134,8 @@ export class Connection {
11271134
res: globalThis.Response & {
11281135
request: globalThis.Request;
11291136
parsedBody?: any;
1130-
},
1131-
) => T,
1137+
}
1138+
) => T
11321139
): Promise<T> {
11331140
const {
11341141
hostUrl,
@@ -1146,7 +1153,7 @@ export class Connection {
11461153

11471154
const headers = util.mergeHeaders(
11481155
this._commonFetchOptions.headers,
1149-
requestHeaders,
1156+
requestHeaders
11501157
);
11511158

11521159
let body = requestBody;

0 commit comments

Comments
 (0)