Skip to content

Commit

Permalink
Merge pull request #371 from huwshimi/axios-migration
Browse files Browse the repository at this point in the history
WD-13813 - refactor: migrate API calls to Axios
  • Loading branch information
huwshimi committed Aug 7, 2024
2 parents 191e1a4 + 5fefbd7 commit f78cf85
Show file tree
Hide file tree
Showing 19 changed files with 209 additions and 275 deletions.
4 changes: 2 additions & 2 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@typescript-eslint/parser": "7.3.1",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "10.4.19",
"axios-mock-adapter": "2.0.0",
"babel-plugin-transform-es2015-modules-commonjs": "6.26.2",
"concurrently": "8.2.2",
"eslint": "8.57.0",
Expand All @@ -75,8 +76,7 @@
"typescript": "5.4.3",
"vite": "5.2.8",
"vite-tsconfig-paths": "4.3.2",
"vitest": "1.2.1",
"vitest-fetch-mock": "0.3.0"
"vitest": "1.2.1"
},
"lint-staged": {
"src/**/*.{json,jsx,ts,tsx}": [
Expand Down
15 changes: 10 additions & 5 deletions ui/src/api/auth.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { fetchMe } from "./auth";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";

import { fetchMe, authURLs } from "./auth";

const mock = new MockAdapter(axios);

beforeEach(() => {
fetchMock.resetMocks();
mock.reset();
});

test("fetches a user", async () => {
Expand All @@ -12,17 +17,17 @@ test("fetches a user", async () => {
sid: "sid",
sub: "sub",
};
fetchMock.mockResponse(JSON.stringify(user), { status: 200 });
mock.onGet(authURLs.me).reply(200, user);
await expect(fetchMe()).resolves.toStrictEqual(user);
});

test("handles a non-authenticated user", async () => {
fetchMock.mockResponseOnce(JSON.stringify({}), { status: 401 });
mock.onGet(authURLs.me).reply(401, {});
await expect(fetchMe()).resolves.toBeNull();
});

test("catches errors", async () => {
const error = "Uh oh!";
fetchMock.mockRejectedValue(error);
mock.onGet(authURLs.me).reply(500, { error });
await expect(fetchMe()).rejects.toBe(error);
});
25 changes: 15 additions & 10 deletions ui/src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import { apiBasePath } from "util/basePaths";
import axios from "axios";

import type { UserPrincipal } from "types/auth";
import { handleResponse } from "util/api";
import { ErrorResponse } from "types/api";
import { AxiosError } from "axios";

const BASE = `${apiBasePath}auth`;
const BASE = "auth";

export const authURLs = {
login: BASE,
me: `${BASE}/me`,
};

export const fetchMe = (): Promise<UserPrincipal> => {
export const fetchMe = (): Promise<UserPrincipal | null> => {
return new Promise((resolve, reject) => {
fetch(authURLs.me)
.then((response: Response) =>
axios
.get<UserPrincipal>(authURLs.me)
.then(({ data }) => resolve(data))
.catch(({ response }: AxiosError<ErrorResponse>) => {
// If the user is not authenticated then return null instead of throwing an
// error. This is necessary so that a login screen can be displayed instead of displaying
// the error.
[401, 403].includes(response.status) ? null : handleResponse(response),
)
.then((result: UserPrincipal) => resolve(result))
.catch(reject);
if (response?.status && [401, 403].includes(response.status)) {
resolve(null);
}
return reject(response?.data?.error ?? response?.data?.message);
});
});
};
71 changes: 22 additions & 49 deletions ui/src/api/client.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,34 @@
import { Client } from "types/client";
import { ApiResponse, PaginatedResponse } from "types/api";
import { handleResponse, PAGE_SIZE } from "util/api";
import { apiBasePath } from "util/basePaths";
import { PaginatedResponse, ApiResponse } from "types/api";
import { handleRequest, PAGE_SIZE } from "util/api";
import axios from "axios";

export const fetchClients = (
pageToken: string,
): Promise<PaginatedResponse<Client[]>> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}clients?page_token=${pageToken}&size=${PAGE_SIZE}`)
.then(handleResponse)
.then((result: PaginatedResponse<Client[]>) => resolve(result))
.catch(reject);
});
};
): Promise<PaginatedResponse<Client[]>> =>
handleRequest(() =>
axios.get<PaginatedResponse<Client[]>>(
`/clients?page_token=${pageToken}&size=${PAGE_SIZE}`,
),
);

export const fetchClient = (clientId: string): Promise<Client> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}clients/${clientId}`)
.then(handleResponse)
.then((result: ApiResponse<Client>) => resolve(result.data))
.catch(reject);
});
};
export const fetchClient = (clientId: string): Promise<ApiResponse<Client>> =>
handleRequest(() => axios.get<ApiResponse<Client>>(`/clients/${clientId}`));

export const createClient = (values: string): Promise<Client> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}clients`, {
method: "POST",
body: values,
})
.then(handleResponse)
.then((result: ApiResponse<Client>) => resolve(result.data))
.catch(reject);
});
};
export const createClient = (values: string): Promise<ApiResponse<Client>> =>
handleRequest(() => axios.post<ApiResponse<Client>>("/clients", values));

export const updateClient = (
clientId: string,
values: string,
): Promise<Client> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}clients/${clientId}`, {
method: "PUT",
body: values,
})
.then(handleResponse)
.then((result: ApiResponse<Client>) => resolve(result.data))
.catch(reject);
});
};
): Promise<ApiResponse<Client>> =>
handleRequest(() =>
axios.post<ApiResponse<Client>>(`/clients/${clientId}`, values),
);

export const deleteClient = (client: string) => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}clients/${client}`, {
export const deleteClient = (client: string) =>
handleRequest(() =>
axios.get<ApiResponse<string>>(`/clients/${client}`, {
method: "DELETE",
})
.then(resolve)
.catch(reject);
});
};
}),
);
71 changes: 22 additions & 49 deletions ui/src/api/identities.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,34 @@
import { ApiResponse, PaginatedResponse } from "types/api";
import { handleResponse, PAGE_SIZE } from "util/api";
import { handleRequest, PAGE_SIZE } from "util/api";
import { Identity } from "types/identity";
import { apiBasePath } from "util/basePaths";
import axios from "axios";

export const fetchIdentities = (
pageToken: string,
): Promise<PaginatedResponse<Identity[]>> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}identities?page_token=${pageToken}&size=${PAGE_SIZE}`)
.then(handleResponse)
.then((result: PaginatedResponse<Identity[]>) => resolve(result))
.catch(reject);
});
};
): Promise<PaginatedResponse<Identity[]>> =>
handleRequest(() =>
axios.get<PaginatedResponse<Identity[]>>(
`/identities?page_token=${pageToken}&size=${PAGE_SIZE}`,
),
);

export const fetchIdentity = (identityId: string): Promise<Identity> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}identities/${identityId}`)
.then(handleResponse)
.then((result: ApiResponse<Identity[]>) => resolve(result.data[0]))
.catch(reject);
});
};
export const fetchIdentity = (
identityId: string,
): Promise<ApiResponse<Identity>> =>
handleRequest(() =>
axios.get<ApiResponse<Identity>>(`/identities/${identityId}`),
);

export const createIdentity = (body: string): Promise<void> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}identities`, {
method: "POST",
body: body,
})
.then(handleResponse)
.then(resolve)
.catch(reject);
});
};
export const createIdentity = (body: string): Promise<unknown> =>
handleRequest(() => axios.post("/identities", body));

export const updateIdentity = (
identityId: string,
values: string,
): Promise<Identity> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}identities/${identityId}`, {
method: "PATCH",
body: values,
})
.then(handleResponse)
.then((result: ApiResponse<Identity>) => resolve(result.data))
.catch(reject);
});
};
): Promise<ApiResponse<Identity>> =>
handleRequest(() =>
axios.patch<ApiResponse<Identity>>(`/identities/${identityId}`, values),
);

export const deleteIdentity = (identityId: string) => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}identities/${identityId}`, {
method: "DELETE",
})
.then(resolve)
.catch(reject);
});
};
export const deleteIdentity = (identityId: string): Promise<unknown> =>
handleRequest(() => axios.delete(`/identities/${identityId}`));
70 changes: 22 additions & 48 deletions ui/src/api/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,37 @@
import { ApiResponse } from "types/api";
import { handleResponse } from "util/api";
import { handleRequest } from "util/api";
import { IdentityProvider } from "types/provider";
import { apiBasePath } from "util/basePaths";
import axios from "axios";
import { PaginatedResponse, ApiResponse } from "types/api";

export const fetchProviders = (): Promise<IdentityProvider[]> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}idps`)
.then(handleResponse)
.then((result: ApiResponse<IdentityProvider[]>) => resolve(result.data))
.catch(reject);
});
};
export const fetchProviders = (): Promise<
PaginatedResponse<IdentityProvider[]>
> =>
handleRequest(() =>
axios.get<PaginatedResponse<IdentityProvider[]>>(`/idps`),
);

export const fetchProvider = (
providerId: string,
): Promise<IdentityProvider> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}idps/${providerId}`)
.then(handleResponse)
.then((result: ApiResponse<IdentityProvider[]>) =>
resolve(result.data[0]),
)
.catch(reject);
handleRequest<IdentityProvider[], ApiResponse<IdentityProvider[]>>(() =>
axios.get<ApiResponse<IdentityProvider[]>>(`/idps/${providerId}`),
)
.then(({ data }) => resolve(data[0]))
.catch((error) => reject(error));
});
};

export const createProvider = (body: string): Promise<void> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}idps`, {
method: "POST",
body: body,
})
.then(handleResponse)
.then(resolve)
.catch(reject);
});
};
export const createProvider = (body: string): Promise<unknown> =>
handleRequest(() => axios.post("/idps", body));

export const updateProvider = (
providerId: string,
values: string,
): Promise<IdentityProvider> => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}idps/${providerId}`, {
method: "PATCH",
body: values,
})
.then(handleResponse)
.then((result: ApiResponse<IdentityProvider>) => resolve(result.data))
.catch(reject);
});
};
): Promise<ApiResponse<IdentityProvider>> =>
handleRequest(() =>
axios.patch<ApiResponse<IdentityProvider>>(`/idps/${providerId}`, values),
);

export const deleteProvider = (providerId: string) => {
return new Promise((resolve, reject) => {
fetch(`${apiBasePath}idps/${providerId}`, {
method: "DELETE",
})
.then(resolve)
.catch(reject);
});
};
export const deleteProvider = (providerId: string): Promise<unknown> =>
handleRequest(() => axios.delete(`/idps/${providerId}`));
Loading

0 comments on commit f78cf85

Please sign in to comment.