Skip to content

Commit 5c60264

Browse files
authored
Merge pull request #88 from thomashacker/feature/handle-scheme-in-host
Handle existing scheme in host
2 parents 4513685 + e01bd61 commit 5c60264

File tree

6 files changed

+123
-9
lines changed

6 files changed

+123
-9
lines changed

src/connection/gqlClient.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ export interface GraphQLClient {
77
}
88

99
export const gqlClient = (config: ConnectionParams): GraphQLClient => {
10-
const scheme = config.scheme;
11-
const host = config.host;
1210
const defaultHeaders = config.headers;
11+
const version = '/v1/graphql';
12+
const baseUri = `${config.host}${version}`;
13+
1314
return {
1415
// for backward compatibility with replaced graphql-client lib,
1516
// results are wrapped into { data: data }
1617
query: (query: TQuery, variables?: Variables, headers?: HeadersInit) => {
17-
return new Client(`${scheme}://${host}/v1/graphql`, {
18+
return new Client(baseUri, {
1819
headers: {
1920
...defaultHeaders,
2021
...headers,

src/connection/httpClient.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export interface HttpClient {
1414
}
1515

1616
export const httpClient = (config: ConnectionParams): HttpClient => {
17-
const baseUri = `${config.scheme}://${config.host}/v1`;
17+
const version = '/v1';
18+
const baseUri = `${config.host}${version}`;
1819
const url = makeUrl(baseUri);
1920

2021
return {

src/connection/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ export default class Connection {
1010
private apiKey?: string;
1111
private authEnabled: boolean;
1212
private gql: GraphQLClient;
13+
public readonly host: string;
1314
public readonly http: HttpClient;
1415
public oidcAuth?: OidcAuthenticator;
1516

1617
constructor(params: ConnectionParams) {
1718
params = this.sanitizeParams(params);
19+
this.host = params.host;
1820
this.http = httpClient(params);
1921
this.gql = gqlClient(params);
2022
this.authEnabled = this.parseAuthParams(params);
@@ -36,10 +38,32 @@ export default class Connection {
3638
}
3739

3840
private sanitizeParams(params: ConnectionParams) {
41+
// Remove trailing slashes from the host
3942
while (params.host.endsWith('/')) {
4043
params.host = params.host.slice(0, -1);
4144
}
4245

46+
const protocolPattern = /^(https?|ftp|file)(?::\/\/)/;
47+
const extractedSchemeMatch = params.host.match(protocolPattern);
48+
49+
// Check for the existence of scheme in params
50+
if (params.scheme) {
51+
// If the host contains a scheme different than provided scheme, replace it and throw a warning
52+
if (extractedSchemeMatch && extractedSchemeMatch[1] !== `${params.scheme}`) {
53+
throw new Error(
54+
`The host contains a different protocol than specified in the scheme (scheme: ${params.scheme} != host: ${extractedSchemeMatch[1]})`
55+
);
56+
} else if (!extractedSchemeMatch) {
57+
// If no scheme in the host, simply prefix with the provided scheme
58+
params.host = `${params.scheme}://${params.host}`;
59+
}
60+
// If there's no scheme in params, ensure the host starts with a recognized protocol
61+
} else if (!extractedSchemeMatch) {
62+
throw new Error(
63+
'The host must start with a recognized protocol (e.g., http or https) if no scheme is provided.'
64+
);
65+
}
66+
4367
return params;
4468
}
4569

src/connection/unit.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,75 @@ describe('mock server auth tests', () => {
124124
await conn.login().then((key) => expect(key).toEqual(apiKey));
125125
});
126126

127+
it('should construct the correct url when host contains scheme', () => {
128+
const apiKey = 'abcd123';
129+
130+
const conn = new Connection({
131+
scheme: 'http',
132+
host: 'http://localhost:' + server.port,
133+
apiKey: new ApiKey(apiKey),
134+
});
135+
const expectedPath = 'http://localhost:' + server.port;
136+
137+
expect(conn.host).toEqual(expectedPath);
138+
});
139+
140+
it('should construct the correct url when scheme specified and host does not contain scheme', () => {
141+
const apiKey = 'abcd123';
142+
143+
const conn = new Connection({
144+
scheme: 'http',
145+
host: 'localhost:' + server.port,
146+
apiKey: new ApiKey(apiKey),
147+
});
148+
const expectedPath = 'http://localhost:' + server.port;
149+
150+
expect(conn.host).toEqual(expectedPath);
151+
});
152+
153+
it('should construct the correct url when no scheme is specified but host contains scheme', () => {
154+
const apiKey = 'abcd123';
155+
156+
const conn = new Connection({
157+
host: 'http://localhost:' + server.port,
158+
apiKey: new ApiKey(apiKey),
159+
});
160+
const expectedPath = 'http://localhost:' + server.port;
161+
162+
expect(conn.host).toEqual(expectedPath);
163+
});
164+
165+
it('should throw error when host contains different scheme than specified', () => {
166+
const apiKey = 'abcd123';
167+
168+
const createConnection = () => {
169+
return new Connection({
170+
scheme: 'https',
171+
host: 'http://localhost:' + server.port,
172+
apiKey: new ApiKey(apiKey),
173+
});
174+
};
175+
176+
expect(createConnection).toThrow(
177+
'The host contains a different protocol than specified in the scheme (scheme: https != host: http)'
178+
);
179+
});
180+
181+
it('should throw error when scheme not specified and included in host', () => {
182+
const apiKey = 'abcd123';
183+
184+
const createConnection = () => {
185+
return new Connection({
186+
host: 'localhost:' + server.port,
187+
apiKey: new ApiKey(apiKey),
188+
});
189+
};
190+
191+
expect(createConnection).toThrow(
192+
'The host must start with a recognized protocol (e.g., http or https) if no scheme is provided.'
193+
);
194+
});
195+
127196
it('shuts down the server', () => {
128197
return server.close();
129198
});

src/index.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface ConnectionParams {
2222
authClientSecret?: AuthClientCredentials | AuthAccessTokenCredentials | AuthUserPasswordCredentials;
2323
apiKey?: ApiKey;
2424
host: string;
25-
scheme: string;
25+
scheme?: string;
2626
headers?: HeadersInit;
2727
}
2828

@@ -44,9 +44,6 @@ const app = {
4444
// check if the URL is set
4545
if (!params.host) throw new Error('Missing `host` parameter');
4646

47-
// check if the scheme is set
48-
if (!params.scheme) throw new Error('Missing `scheme` parameter');
49-
5047
// check if headers are set
5148
if (!params.headers) params.headers = {};
5249

test/server.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function testServer() {
2727
lastRequest = ctx.request;
2828
return next();
2929
});
30-
app.use(getLocalOidcConfig, getRemoteOidcConfig, issueToken);
30+
app.use(getLocalOidcConfig, getRemoteOidcConfig, issueToken, mockGetEndpoint, mockGraphQLResponse);
3131
const server = app.listen(port);
3232

3333
serverCache = {
@@ -46,6 +46,28 @@ export function testServer() {
4646
return serverCache;
4747
}
4848

49+
const mockGetEndpoint = (ctx: any, next: any) => {
50+
if (ctx.path !== '/v1/testEndpoint') {
51+
return next();
52+
}
53+
54+
ctx.response.status = 200;
55+
ctx.response.body = { message: 'test endpoint' };
56+
};
57+
58+
const mockGraphQLResponse = (ctx: any, next: any) => {
59+
if (ctx.path !== '/v1/graphql') {
60+
return next();
61+
}
62+
63+
ctx.response.status = 200;
64+
ctx.response.body = {
65+
data: {
66+
someField: 'someValue',
67+
},
68+
};
69+
};
70+
4971
const getLocalOidcConfig = (ctx: any, next: any) => {
5072
if (ctx.path !== '/v1/.well-known/openid-configuration') {
5173
return next();

0 commit comments

Comments
 (0)