Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@internxt/sdk",
"author": "Internxt <hello@internxt.com>",
"version": "1.24.0",
"version": "1.25.0",
"description": "An sdk for interacting with Internxt's services",
"repository": {
"type": "git",
Expand Down
9 changes: 5 additions & 4 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ApiSecurity, ApiUrl, AppDetails } from '../shared';
import { basicHeaders, headersWithToken } from '../shared/headers';
import { HttpClient } from '../shared/http/client';
import { HttpClientOptions } from '../shared/http/types';
import { TeamsSettings } from '../shared/types/teams';
import { UserSettings } from '../shared/types/userSettings';
import {
Expand Down Expand Up @@ -29,12 +30,12 @@ export class Auth {
private readonly apiSecurity?: ApiSecurity;
private readonly apiUrl: string;

public static client(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity) {
return new Auth(apiUrl, appDetails, apiSecurity);
public static client(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity, options?: HttpClientOptions) {
return new Auth(apiUrl, appDetails, apiSecurity, options);
}

private constructor(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity) {
this.client = HttpClient.create(apiUrl, apiSecurity?.unauthorizedCallback, apiSecurity?.retryOptions);
private constructor(apiUrl: ApiUrl, appDetails: AppDetails, apiSecurity?: ApiSecurity, options?: HttpClientOptions) {
this.client = HttpClient.create(apiUrl, apiSecurity?.unauthorizedCallback, apiSecurity?.retryOptions, options);
this.appDetails = appDetails;
this.apiSecurity = apiSecurity;
this.apiUrl = apiUrl;
Expand Down
19 changes: 15 additions & 4 deletions src/shared/http/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { AxiosResponseError, AxiosUnknownError } from '../types/errors';
import { Headers, Parameters, RequestCanceler, URL, UnauthorizedCallback } from './types';
import { Headers, HttpClientOptions, Parameters, RequestCanceler, URL, UnauthorizedCallback } from './types';
import { RetryOptions, retryWithBackoff } from './retryWithBackoff';

export { RequestCanceler } from './types';
Expand Down Expand Up @@ -46,16 +46,27 @@ export class HttpClient {
HttpClient.globalRetryOptions = (options ?? {}) as RetryOptions;
}

public static create(baseURL: URL, unauthorizedCallback?: UnauthorizedCallback, retryOptions?: RetryOptions) {
public static create(
baseURL: URL,
unauthorizedCallback?: UnauthorizedCallback,
retryOptions?: RetryOptions,
options?: HttpClientOptions,
) {
if (unauthorizedCallback === undefined) {
unauthorizedCallback = () => null;
}
return new HttpClient(baseURL, unauthorizedCallback, retryOptions);
return new HttpClient(baseURL, unauthorizedCallback, retryOptions, options);
}

private constructor(baseURL: URL, unauthorizedCallback: UnauthorizedCallback, retryOptions?: RetryOptions) {
private constructor(
baseURL: URL,
unauthorizedCallback: UnauthorizedCallback,
retryOptions?: RetryOptions,
options?: HttpClientOptions,
) {
this.axios = axios.create({
baseURL: baseURL,
withCredentials: options?.withCredentials ?? false,
});
this.unauthorizedCallback = unauthorizedCallback;
this.retryOptions = retryOptions;
Expand Down
7 changes: 7 additions & 0 deletions src/shared/http/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ export interface RequestCanceler {
}

export type UnauthorizedCallback = () => void;

export interface HttpClientOptions {
/**
* Sends and stores cookies on cross-origin browser requests. The API must allow credentials on CORS
*/
withCredentials?: boolean;
}
21 changes: 21 additions & 0 deletions test/auth/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ describe('# auth service tests', () => {
vi.restoreAllMocks();
});

describe('-> client options', () => {
it('when credentials are enabled, then requests are sent with credentials', () => {
const auth = Auth.client('url', { clientName: 'c-name', clientVersion: '0.1' }, undefined, {
withCredentials: true,
});

expect(getAxiosDefaults(auth).withCredentials).toBe(true);
});

it('when no options are given, then requests are sent without credentials', () => {
const auth = Auth.client('url', { clientName: 'c-name', clientVersion: '0.1' });

expect(getAxiosDefaults(auth).withCredentials).toBe(false);
});
});

describe('-> register use case', () => {
it('Should have all the correct params on call', async () => {
// Arrange
Expand Down Expand Up @@ -1158,3 +1174,8 @@ function forbiddenResponseError(responseBody: object): AxiosResponseError {
const response = { status: 403, data: responseBody, headers: {} } as AxiosResponse;
return new AxiosResponseError('Request failed with status code 403', 'post /auth/login', response);
}

function getAxiosDefaults(auth: Auth) {
const httpClient = (auth as unknown as { client: HttpClient }).client;
return (httpClient as unknown as { axios: { defaults: { withCredentials?: boolean } } }).axios.defaults;
}
11 changes: 11 additions & 0 deletions test/shared/http/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ describe('HttpClient', () => {
// Assert
expect(createSpy).toHaveBeenCalledWith({
baseURL: 'my-url',
withCredentials: false,
});
});
it('when credentials are enabled, then the axios instance is created with withCredentials', () => {
const createSpy = vi.spyOn(axios, 'create');

HttpClient.create('my-url', undefined, undefined, { withCredentials: true });

expect(createSpy).toHaveBeenCalledWith({
baseURL: 'my-url',
withCredentials: true,
});
});
});
Expand Down
Loading