Skip to content

Commit e636b25

Browse files
authored
[ts-sdk] Fix relative URL (#1617)
1 parent 92f8692 commit e636b25

File tree

4 files changed

+71
-48
lines changed

4 files changed

+71
-48
lines changed

ts/smelter-node/src/fetch.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import http from 'http';
33
import https from 'https';
44
import { Stream } from 'stream';
55
import { promisify } from 'util';
6+
import path from 'path';
67

78
import fetch from 'node-fetch';
89
import type FormData from 'form-data';
@@ -13,7 +14,7 @@ const httpAgent = new http.Agent({ keepAlive: true });
1314
const httpsAgent = new https.Agent({ keepAlive: true });
1415

1516
export async function sendRequest(baseUrl: string | URL, request: ApiRequest): Promise<object> {
16-
const response = await fetch(new URL(request.route, baseUrl), {
17+
const response = await fetch(joinUrl(baseUrl, request.route), {
1718
method: request.method,
1819
body: request.body && JSON.stringify(request.body),
1920
headers: {
@@ -24,11 +25,7 @@ export async function sendRequest(baseUrl: string | URL, request: ApiRequest): P
2425
if (response.status >= 400) {
2526
const err: any = new Error(`Request to Smelter server failed.`);
2627
err.response = response;
27-
try {
28-
err.body = await response.json();
29-
} catch {
30-
err.body = await response.text();
31-
}
28+
err.body = await readErrorBody(response);
3229
throw err;
3330
}
3431
return (await response.json()) as object;
@@ -38,19 +35,15 @@ export async function sendMultipartRequest(
3835
baseUrl: string | URL,
3936
request: MultipartRequest
4037
): Promise<object> {
41-
const response = await fetch(new URL(request.route, baseUrl), {
38+
const response = await fetch(joinUrl(baseUrl, request.route), {
4239
method: request.method,
4340
body: request.body as FormData,
4441
agent: url => (url.protocol === 'http:' ? httpAgent : httpsAgent),
4542
});
4643
if (response.status >= 400) {
4744
const err: any = new Error(`Request to Smelter server failed.`);
4845
err.response = response;
49-
try {
50-
err.body = await response.json();
51-
} catch {
52-
err.body = await response.text();
53-
}
46+
err.body = await readErrorBody(response);
5447
throw err;
5548
}
5649
return (await response.json()) as object;
@@ -69,3 +62,22 @@ export async function download(url: string, destination: string): Promise<void>
6962
throw Error(`Response with empty body.`);
7063
}
7164
}
65+
66+
/*
67+
* new URL(relative, base) overrides pathname part of base URL, this function
68+
* appends instead
69+
*/
70+
export function joinUrl(base: URL | string, relative: string): URL {
71+
const url = new URL(base);
72+
url.pathname = path.join(url.pathname, relative);
73+
return url;
74+
}
75+
76+
async function readErrorBody(response: fetch.Response): Promise<object | string> {
77+
const body = await response.text();
78+
try {
79+
return JSON.parse(body);
80+
} catch {
81+
return body;
82+
}
83+
}

ts/smelter-node/src/manager/existingInstance.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type {
55
SetupInstanceOptions,
66
} from '@swmansion/smelter-core';
77

8-
import { sendRequest, sendMultipartRequest } from '../fetch';
8+
import { sendRequest, sendMultipartRequest, joinUrl } from '../fetch';
99
import { retry, sleep } from '../utils';
1010
import { WebSocketConnection } from '../ws';
1111
import { getSmelterStatus } from '../getSmelterStatus';
@@ -35,7 +35,7 @@ class ExistingInstanceManager implements SmelterManager {
3535

3636
this.url = url;
3737

38-
const wsUrl = new URL('ws', url);
38+
const wsUrl = joinUrl(url, 'ws');
3939
wsUrl.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
4040
this.wsConnection = new WebSocketConnection(wsUrl);
4141
}
@@ -69,16 +69,12 @@ class ExistingInstanceManager implements SmelterManager {
6969
return smelterStatus;
7070
}, 10);
7171

72-
try {
73-
await this.sendRequest({
74-
method: 'POST',
75-
route: '/api/reset',
76-
body: {},
77-
});
78-
opts.logger.info('Sent reset request to existing Smelter instance.');
79-
} catch (err) {
80-
opts.logger.warn({ err }, 'Failed to reset existing Smelter instance.');
81-
}
72+
await this.sendRequest({
73+
method: 'POST',
74+
route: '/api/reset',
75+
body: {},
76+
});
77+
opts.logger.info('Sent reset request to the Smelter instance.');
8278

8379
await this.wsConnection.connect(opts.logger);
8480
}

ts/smelter-web-client/src/fetch.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ApiRequest, MultipartRequest } from '@swmansion/smelter-core';
22

33
export async function sendRequest(baseUrl: URL, request: ApiRequest): Promise<object> {
4-
const response = await fetch(new URL(request.route, baseUrl), {
4+
const response = await fetch(joinUrl(baseUrl, request.route), {
55
method: request.method,
66
body: request.body && JSON.stringify(request.body),
77
headers: {
@@ -11,11 +11,7 @@ export async function sendRequest(baseUrl: URL, request: ApiRequest): Promise<ob
1111
if (response.status >= 400) {
1212
const err: any = new Error(`Request to Smelter server failed.`);
1313
err.response = response;
14-
try {
15-
err.body = await response.json();
16-
} catch {
17-
err.body = await response.text();
18-
}
14+
err.body = await readErrorBody(response);
1915
throw err;
2016
}
2117
return (await response.json()) as object;
@@ -25,19 +21,42 @@ export async function sendMultipartRequest(
2521
baseUrl: URL,
2622
request: MultipartRequest
2723
): Promise<object> {
28-
const response = await fetch(new URL(request.route, baseUrl), {
24+
const response = await fetch(joinUrl(baseUrl, request.route), {
2925
method: request.method,
3026
body: request.body as FormData,
3127
});
3228
if (response.status >= 400) {
3329
const err: any = new Error(`Request to Smelter server failed.`);
3430
err.response = response;
35-
try {
36-
err.body = await response.json();
37-
} catch {
38-
err.body = await response.text();
39-
}
31+
err.body = await readErrorBody(response);
4032
throw err;
4133
}
4234
return (await response.json()) as object;
4335
}
36+
37+
/*
38+
* new URL(relative, base) overrides pathname part of base URL, this function
39+
* appends instead
40+
*/
41+
export function joinUrl(base: URL | string, relative: string): URL {
42+
const url = new URL(base);
43+
44+
if (url.pathname.endsWith('/') != relative.startsWith('/')) {
45+
url.pathname = url.pathname + relative;
46+
} else if (url.pathname.endsWith('/') && relative.startsWith('/')) {
47+
url.pathname = url.pathname + relative.slice(1);
48+
} else {
49+
url.pathname = url.pathname + '/' + relative;
50+
}
51+
52+
return url;
53+
}
54+
55+
async function readErrorBody(response: Response): Promise<object | string> {
56+
const body = await response.text();
57+
try {
58+
return JSON.parse(body);
59+
} catch {
60+
return body;
61+
}
62+
}

ts/smelter-web-client/src/manager.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type {
55
SetupInstanceOptions,
66
} from '@swmansion/smelter-core';
77

8-
import { sendRequest, sendMultipartRequest } from './fetch';
8+
import { sendRequest, sendMultipartRequest, joinUrl } from './fetch';
99
import { retry, sleep } from './utils';
1010
import { WebSocketConnection } from './ws';
1111
import { getSmelterStatus } from './getSmelterStatus';
@@ -32,7 +32,7 @@ class RemoteInstanceManager implements SmelterManager {
3232

3333
this.url = url;
3434

35-
const wsUrl = new URL('ws', url);
35+
const wsUrl = joinUrl(url, 'ws');
3636
wsUrl.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
3737
this.wsConnection = new WebSocketConnection(wsUrl);
3838
}
@@ -57,16 +57,12 @@ class RemoteInstanceManager implements SmelterManager {
5757
return smelterStatus;
5858
}, 10);
5959

60-
try {
61-
await this.sendRequest({
62-
method: 'POST',
63-
route: '/api/reset',
64-
body: {},
65-
});
66-
opts.logger.info('Sent reset request to existing Smelter instance.');
67-
} catch (err) {
68-
opts.logger.warn({ err }, 'Failed to reset existing Smelter instance.');
69-
}
60+
await this.sendRequest({
61+
method: 'POST',
62+
route: '/api/reset',
63+
body: {},
64+
});
65+
opts.logger.info('Sent reset request to the Smelter instance.');
7066

7167
await this.wsConnection.connect(opts.logger);
7268
}

0 commit comments

Comments
 (0)