Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade openapi-typescript to 7.0.2 + swap to openapi-fetch #18532

Merged
merged 149 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
edad54d
Upgrade openapi-typescript to 7.0.2
davelopez Jul 11, 2024
4d9f44f
Update makefile to use default openapi-typescript without transform
davelopez Jul 11, 2024
d73af22
Rebuild client API schema
davelopez Jul 11, 2024
5ff1926
Move openapi-typescript to dev dependencies
davelopez Jul 11, 2024
8867f90
Replace openapi-typescript-fetch with openapi-fetch in Toolshed
davelopez Jul 12, 2024
9aefbd1
Add middleware for error handling in Toolshed API client
davelopez Jul 12, 2024
3ac83e9
Add openapi-fetch dependency
davelopez Jul 15, 2024
f1d63d7
Refactor use openapi-fetch in some invocation related components
davelopez Jul 17, 2024
ac89413
Replace fetcher in api/datasetCollections
davelopez Jul 17, 2024
ef73d65
Replace fetcher in api/datasets.ts
davelopez Jul 17, 2024
39707df
Replace fetcher in api/datatypes.ts
davelopez Jul 17, 2024
91b7e0c
Replace fetcher in api/dbKeys.ts
davelopez Jul 17, 2024
c65cba9
Replace fetcher in api/forms.ts
davelopez Jul 17, 2024
0d7ee45
Replace fetcher in FileSources/Instances/CreateForm.vue
davelopez Jul 17, 2024
caade36
Replace fetcher in ToolHelpForum.vue
davelopez Jul 17, 2024
e3acbec
Remove remaining references to ApiResponse
davelopez Jul 17, 2024
96c1ee1
Update VariableValueType type to include undefined
davelopez Jul 17, 2024
a563024
Replace fetchJobDestinationParams
davelopez Jul 17, 2024
050acfb
Replace job lock fetchers
davelopez Jul 18, 2024
24765b1
Replace fetcher in PageEditor/ObjectPermissions.vue
davelopez Jul 18, 2024
721391f
Replace fetcher in DatasetError and DatasetDetails
davelopez Jul 19, 2024
966144a
Replace fetcher in api/groups.ts
davelopez Jul 19, 2024
197b3ea
Replace fetcher in api/histories.archived.ts
davelopez Jul 23, 2024
9957a25
Replace fetcher in api/histories.export.ts + refactor type imports
davelopez Jul 24, 2024
98da14c
Replace fetcher in api/histories.ts
davelopez Jul 24, 2024
a1bf3d5
Replace fetcher in api/invocations.ts
davelopez Jul 25, 2024
cc012ee
Replace fetcher in api/notifications.broadcast.ts
davelopez Jul 25, 2024
bf80046
Replace fetcher in api/notifications.preferences.ts
davelopez Jul 25, 2024
4eb2d8a
Replace fetcher in api/notifications.ts
davelopez Jul 25, 2024
d577417
Replace fetcher in api/objectStores.ts
davelopez Jul 26, 2024
05a219e
Replace fetcher in api/pages.ts
davelopez Jul 26, 2024
2b580f3
Replace fetcher in api/quotas.ts
davelopez Jul 26, 2024
39aeb82
Replace fetcher in api/remoteFiles.ts
davelopez Jul 26, 2024
fb72a0f
Replace fetcher in api/roles.ts
davelopez Jul 26, 2024
e8863a2
Replace fetcher in api/tags.ts
davelopez Jul 26, 2024
45348f3
Replace fetcher in api/users.ts
davelopez Jul 26, 2024
9865241
Replace fetcher in api/workflows.ts
davelopez Jul 26, 2024
1de016c
Refactor QuotaUsage
davelopez Jul 26, 2024
fd2d6a8
Replace fetcher in DiskUsage Management services
davelopez Jul 26, 2024
cf24fc1
Replace fetcher in ObjectStore Instances services
davelopez Jul 26, 2024
2287dd6
Replace fetcher in FileSources Instances services
davelopez Jul 26, 2024
e63ae3f
Replace fetcher in objectStoreTemplatesStore
davelopez Jul 26, 2024
02b7dd9
Replace fetcher in objectStoreInstancesStore
davelopez Jul 26, 2024
6a87d78
Replace fetcher in fileSourceTemplatesStore
davelopez Jul 26, 2024
80f63ac
Replace fetcher in fileSourceInstancesStore
davelopez Jul 26, 2024
05bcaab
Replace fetcher in configurationStore
davelopez Jul 26, 2024
be9c40f
Replace fetcher in taskMonitor
davelopez Jul 26, 2024
f9ac44b
Replace fetcher in shortTermStorageMonitor
davelopez Jul 26, 2024
accca70
Replace fetcher in handlesMappingJobs
davelopez Jul 26, 2024
317276a
Remove unused fetcher
davelopez Jul 26, 2024
7d06ffd
Replace fetcher in shortTermStorage composable
davelopez Jul 26, 2024
0e28b51
Replace fetcher in visualizations grid config
davelopez Jul 26, 2024
652ee6b
Replace fetcher in visualizationsPublished grid config
davelopez Jul 26, 2024
7af07c6
Replace fetcher in pagesPublished grid config
davelopez Jul 26, 2024
ea9cbdd
Refactor notifications preferences components and utils
davelopez Jul 27, 2024
6b21741
Replace fetcher in WorkflowList component
davelopez Jul 27, 2024
4f399ad
Fix some type inconsistencies
davelopez Jul 27, 2024
5d9455f
Consolidate imports
davelopez Jul 27, 2024
bdc51d3
Remove openapi-typescript-fetch dependency
davelopez Jul 29, 2024
7266a31
Replace some axios calls with openapi-fetch client
davelopez Jul 29, 2024
877f3e0
Add `Mock Service Worker` to mock client API responses
davelopez Jul 30, 2024
5df52c2
Simplify client api exports
davelopez Jul 30, 2024
5e3e4d6
Refactor client API mocks and imports
davelopez Aug 1, 2024
b35cf2e
Refactor Datatypes/index.test.js to use TypeScript + new client mock
davelopez Aug 1, 2024
0ea8512
Refactor client API and mock server usage
davelopez Aug 2, 2024
53a1891
Refactor client API calls to use GalaxyApi factory
davelopez Aug 2, 2024
38b9da4
Expose HttpResponse for mocking untyped responses in tests
davelopez Aug 2, 2024
c73c2ec
Refactor HistoryArchiveExportSelector tests
davelopez Aug 2, 2024
6bed58e
Refactor HistoryExport component tests
davelopez Aug 2, 2024
8bf929e
Improve error handling in simple-error.ts
davelopez Aug 2, 2024
0816a10
Refactor CreateForm test to use server mock
davelopez Aug 2, 2024
3967ce2
Refactor HistoryImport test to use server mock
davelopez Aug 2, 2024
418be52
Refactor BroadcastForm test to use server mock
davelopez Aug 2, 2024
bfa4030
Refactor StsDownloadButton test to use server mock
davelopez Aug 2, 2024
d89fdf3
Refactor fileSources tests to use server mock
davelopez Aug 2, 2024
a809778
Refactor BroadcastsList test to use server mock
davelopez Aug 2, 2024
f69f614
Refactor NotificationsManagement test to use server mock
davelopez Aug 2, 2024
c2b7c59
Refactor ExportRDMForm test to use server mock
davelopez Aug 2, 2024
b4b5c2a
Refactor DatasetStorage test to use TypeScript and server mock
davelopez Aug 2, 2024
86ac6db
Refactor DatasetError.test.ts to use server mock
davelopez Aug 3, 2024
648fd77
Refactor FilesDialog.test.ts to use server mock
davelopez Aug 3, 2024
d142a4f
Refactor FormDirectory.test.js to use server mock
davelopez Aug 3, 2024
fc6dcf5
Refactor HistoryArchiveWizard.test.ts to use server mock
davelopez Aug 3, 2024
c68c684
Refactor ContentItem.test.js to use server mock
davelopez Aug 3, 2024
edda373
Refactor SelectionOperations tests to use server mock
davelopez Aug 3, 2024
cd1db99
Fix HistoryExport/Index.test.js
davelopez Aug 3, 2024
11b2bca
Add jest polyfill for clearImmediate
davelopez Aug 3, 2024
e4cee28
Fix loadDbKeys function to remove unnecessary destructuring
davelopez Aug 3, 2024
869cc21
Refactor UploadModal.test.js to use server mock
davelopez Aug 3, 2024
9473f4c
Refactor JobDestinationParams.test.ts to use server mock
davelopez Aug 3, 2024
0dad124
Refactor Masthead.test.js to use server mock
davelopez Aug 4, 2024
7e0c815
Refactor ShowSelectedObjectStore.test.js to use server mock
davelopez Aug 4, 2024
15bb9ff
Refactor UpgradeForm.test.ts to use server mock
davelopez Aug 4, 2024
d101f90
Refactor ToolForm.test.js to use server mock
davelopez Aug 4, 2024
13a9fdb
Refactor RepositoryDetails/Index.test.js to use server mock
davelopez Aug 4, 2024
c71e38b
Refactor user queries to use GalaxyApi for fetching user data
davelopez Aug 6, 2024
00c4957
Refactor WorkflowList.test.ts to use server mock
davelopez Aug 6, 2024
1e4a95d
Refactor shortTermStorageMonitor.test.ts to use server mock
davelopez Aug 6, 2024
6d56391
Refactor taskMonitor.test.ts to use server mock
davelopez Aug 6, 2024
e417dfe
Refactor collectionAttributesStore.test.ts to use server mock
davelopez Aug 6, 2024
c30b851
Refactor collectionElementsStore.test.ts to use server mock
davelopez Aug 6, 2024
23819fe
Adapt adminUsers.test.js
davelopez Aug 6, 2024
2c523b0
Refactor object store templates types in the client
davelopez Aug 6, 2024
7df8ff6
Remove leftover logs in tests
davelopez Aug 6, 2024
0b3bcee
Fix type in broadcast notifications tests
davelopez Aug 6, 2024
41fdaf8
Refactor DiskUsageSummary.test.ts to use server mock
davelopez Aug 6, 2024
5619615
Use correct query parameters in api/workflows
davelopez Aug 6, 2024
b3d9fe2
Annotate globalThis in jest polyfills
dannon Aug 6, 2024
cb99eac
Add undici peer dependency
dannon Aug 7, 2024
81e5282
Add performance, readableStream to jsdom
dannon Aug 7, 2024
290bab3
Fix jest fetch polyfill by using whatwg-fetch
davelopez Aug 7, 2024
64cddf5
Use fake-indexeddb in tests
davelopez Aug 7, 2024
17d4e30
Drop undici devDependency
dannon Aug 7, 2024
a2685e1
Drop performance polyfill, no longer necessary
dannon Aug 7, 2024
92ffda2
Restore axios MockAdapter in ToolForm.test.js
davelopez Aug 7, 2024
852a416
Convert axios queries to fetcher + type fixes
davelopez Aug 7, 2024
672cabd
Update openapi-fetch and openapi-typescript dependencies
davelopez Aug 8, 2024
918b5fd
Update types and imports for templates
davelopez Aug 8, 2024
3c6778d
Add server mock to SelectPreferredStore test
davelopez Aug 8, 2024
cd33176
Refactor SwitchToHistoryLink test to use server mock
davelopez Aug 8, 2024
b94909d
Refactor RepositoryDetails tests to typescript + mock api calls
davelopez Aug 8, 2024
56fb93c
Adapt fetchItem mock response in useKeyedCache tests
davelopez Aug 8, 2024
2237070
Replace jest-environment-jsdom with jest-fixed-jsdom
davelopez Aug 8, 2024
0f5fd22
Refactor SelectorModal tests to use server mock
davelopez Aug 8, 2024
2a3547a
Adapt async usage in ObjectStore CreateForm tests
davelopez Aug 8, 2024
d44fe9e
Update disk usage summary test to handle pending state
davelopez Aug 8, 2024
022f738
Refactor UserPreferredObjectStore test to use server mock
davelopez Aug 9, 2024
d46f631
Refactor HistoryView test to use server mock and update imports
davelopez Aug 9, 2024
4700e52
Refactor JobInformation test to use server mock
davelopez Aug 9, 2024
de78a11
Fix header accept value in fetchHistoryExportRecords
davelopez Aug 9, 2024
f280804
Update querying-the-api.md to refer to GalaxyApi client
davelopez Aug 9, 2024
6c7acd2
Update writing-tests.md to include useServerMock
davelopez Aug 9, 2024
90830f6
Update querying-the-api.md to use Toast for error handling
davelopez Aug 12, 2024
eed9d04
Refactor DatasetStorage test to simplify API error simulation
davelopez Aug 12, 2024
e136926
Update WorkflowSelectPreferredObjectStore.test.js
davelopez Aug 12, 2024
cdcb5ea
Update ToolSelectPreferredObjectStore.test.js
davelopez Aug 12, 2024
14f3cd7
Refactor MultipleView.test.js to use server mock and update imports
davelopez Aug 12, 2024
dd77a47
Add more details to querying-the-api.md
davelopez Aug 12, 2024
14b793b
Add rethrowSimple error handling to fetchInvocation
davelopez Aug 12, 2024
f7ad8e4
Update error handling in CitableRepositoryPage.vue
davelopez Aug 12, 2024
00faba3
Refactor Tool Shed ManageApiKey.vue to use async/await syntax
davelopez Aug 12, 2024
e49dee7
Include specific error messages in Tool Shed ManageApiKey.vue
davelopez Aug 12, 2024
1d57754
Refactor EditInstance.vue to improve code readability
davelopez Aug 12, 2024
b8270b6
Remove unnecessary try/catch in NotificationForm.vue
davelopez Aug 12, 2024
bd53019
Refactor schema imports and API calls to use ToolShedApi
davelopez Aug 13, 2024
2505309
Refactor error handling in Tool Shed client
davelopez Aug 13, 2024
df6ced6
Refactor current user API call to use ToolShedApi
davelopez Aug 13, 2024
1e5639a
Remove unused axios dependency
davelopez Aug 13, 2024
30e39ea
Handle errors consistently in Tool Shed client
davelopez Aug 13, 2024
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ remove-api-schema:
rm _shed_schema.yaml

update-client-api-schema: client-node-deps build-api-schema
$(IN_VENV) cd client && node openapi_to_schema.mjs ../_schema.yaml > src/api/schema/schema.ts && npx prettier --write src/api/schema/schema.ts
$(IN_VENV) cd client && node openapi_to_schema.mjs ../_shed_schema.yaml > ../lib/tool_shed/webapp/frontend/src/schema/schema.ts && npx prettier --write ../lib/tool_shed/webapp/frontend/src/schema/schema.ts
$(IN_VENV) cd client && npx openapi-typescript ../_schema.yaml > src/api/schema/schema.ts && npx prettier --write src/api/schema/schema.ts
$(IN_VENV) cd client && npx openapi-typescript ../_shed_schema.yaml > ../lib/tool_shed/webapp/frontend/src/schema/schema.ts && npx prettier --write ../lib/tool_shed/webapp/frontend/src/schema/schema.ts
$(MAKE) remove-api-schema

lint-api-schema: build-api-schema
Expand Down
99 changes: 80 additions & 19 deletions client/docs/querying-the-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,105 @@ If there is no Composable for the API endpoint you are using, try using a (Pinia

### Direct API Calls

- If the type of data you are querying should not be cached, or you just need to update or create new data, you can use the API directly. Make sure to use the **Fetcher** (see below) instead of Axios, as it provides a type-safe interface to the API along with some extra benefits.
- If the type of data you are querying should not be cached, or you just need to update or create new data, you can use the API directly. Make sure to use the **GalaxyApi client** (see below) instead of Axios, as it provides a type-safe interface to the API along with some extra benefits.

## 2. Prefer Fetcher over Axios (when possible)
## 2. Prefer **GalaxyApi client** over Axios (when possible)

- **Use Fetcher with OpenAPI Specs**: If there is an OpenAPI spec for the API endpoint you are using (in other words, there is a FastAPI route defined in Galaxy), always use the Fetcher. It will provide you with a type-safe interface to the API.
- **Use **GalaxyApi client** with OpenAPI Specs**: If there is an OpenAPI spec for the API endpoint you are using (in other words, there is a FastAPI route defined in Galaxy), always use the GalaxyApi client. It will provide you with a type-safe interface to the API.

**Do**

```typescript
import { fetcher } from "@/api/schema";
const datasetsFetcher = fetcher.path("/api/dataset/{id}").method("get").create();
```ts
import { ref, onMounted } from "vue";

const { data: dataset } = await datasetsFetcher({ id: "testID" });
import { GalaxyApi, type HDADetailed } from "@/api";
import { errorMessageAsString } from "@/utils/simple-error";

interface Props {
datasetId: string;
}

const props = defineProps<Props>();

const datasetDetails = ref<HDADetailed>();
const errorMessage = ref<string>();

async function loadDatasetDetails() {
// Your IDE will provide you with autocompletion for the route and all the parameters
const { data, error } = await GalaxyApi().GET("/api/datasets/{dataset_id}", {
params: {
path: {
dataset_id: props.datasetId,
},
query: { view: "detailed" },
},
});

if (error) {
// Handle error here. For example, you can display a message to the user.
errorMessage.value = errorMessageAsString(error);
// Make sure to return here, otherwise `data` will be undefined
return;
}

// Use `data` here. We are casting it to HDADetailed to help the type inference because
// we requested the "detailed" view and this endpoint returns different types depending
// on the view. In general, the correct type will be inferred automatically using the
// API schema so you don't need to (and shouldn't) cast it.
datasetDetails.value = data as HDADetailed;
}

onMounted(() => {
loadDatasetDetails();
});
```

**Don't**

```js
```ts
import { ref, onMounted } from "vue";

import axios from "axios";
import { getAppRoot } from "onload/loadConfig";
import { rethrowSimple } from "utils/simple-error";
import { errorMessageAsString } from "@/utils/simple-error";

interface Props {
datasetId: string;
}

const props = defineProps<Props>();

const datasetDetails = ref<HDADetailed>();
const errorMessage = ref<string>();

async getDataset(datasetId) {
async function loadDatasetDetails() {
// You need to construct the URL yourself
const url = `${getAppRoot()}api/datasets/${datasetId}`;
// You are forced to use a try-catch block to handle errors
// and you may forget to do so.
try {
const response = await axios.get(url);
return response.data;
// This is not type-safe and cannot detect changes in the API schema.
const response = await axios.get(url, {
// You need to know the API parameters, no type inference here.
params: { view: "detailed" },
});
// In this case, you need to cast the response to the correct type
// (as in the previous example), but you will also have to do it in the general
// case because there is no type inference.
datasetDetails = response.data as HDADetailed;
} catch (e) {
rethrowSimple(e);
errorMessage.value = errorMessageAsString(error);
}
}

const dataset = await getDataset("testID");
onMounted(() => {
loadDatasetDetails();
});
```

> **Reason**
>
> The `fetcher` class provides a type-safe interface to the API, and is already configured to use the correct base URL and error handling.
> The `GalaxyApi client` function provides a type-safe interface to the API, and is already configured to use the correct base URL. In addition, it will force you to handle errors properly, and will provide you with a type-safe response object. It uses `openapi-fetch` and you can find more information about it [here](https://openapi-ts.dev/openapi-fetch/).

## 3. Where to put your API queries?

Expand All @@ -79,8 +140,8 @@ If so, you should consider putting the query in a Store. If not, you should cons

If so, you should consider putting it under src/api/<resource>.ts and exporting it from there. This will allow you to reuse the query in multiple places specially if you need to do some extra processing of the data. Also it will help to keep track of what parts of the API are being used and where.

### Should I use the `fetcher` directly or should I write a wrapper function?
### Should I use the `GalaxyApi client` directly or should I write a wrapper function?

- If you **don't need to do any extra processing** of the data, you can use the `fetcher` directly.
- If you **need to do some extra processing**, you should consider writing a wrapper function. Extra processing can be anything from transforming the data to adding extra parameters to the query or omitting some of them, handling conditional types in response data, etc.
- Using a **wrapper function** will help in case we decide to replace the `fetcher` with something else in the future (as we are doing now with _Axios_).
- If you **don't need to do any additional processing** of the data, you **should** use the `GalaxyApi client` directly.
- If you **need to do some additional processing**, then consider writing a wrapper function. Additional processing can be anything from transforming the data to adding extra parameters to the query, providing useful defaults, handling conditional types in response data, etc.
- In any case, please try to avoid modeling your own types if you can use the ones defined in the OpenAPI spec (client/src/api/schema/schema.ts). This will help to keep the codebase consistent and avoid duplication of types or API specification drifting.
47 changes: 47 additions & 0 deletions client/docs/unit-testing/writing-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,50 @@ describe("some module you wrote", () => {

We have created some [common helpers for common testing
scenarios](https://github.com/galaxyproject/galaxy/blob/dev/client/tests/jest/helpers.js).

### Mocking API calls

When testing components that make API calls, you should use [**Mock Service Worker**](https://mswjs.io/docs/getting-started/) in combination with [**openapi-msw**](https://github.com/christoph-fricke/openapi-msw?tab=readme-ov-file#openapi-msw).

If you want to know more about why MSW is a good choice for mocking API calls, you can read [this article](https://mswjs.io/docs/philosophy).

If your component makes an API call, for example to get a particular history, you can mock the response of the API call using the `useServerMock` composable in your test file.

```ts
import { useServerMock } from "@/api/client/__mocks__";

const { server, http } = useServerMock();

describe("MyComponent", () => {
it("should do something with the history", async () => {
// Mock the response of the API call
server.use(
http.get("/api/histories/{history_id}", ({ params, query, response }) => {
// You can use logic to return different responses based on the request
if (query.get("view") === "detailed") {
return response(200).json(TEST_HISTORY_DETAILED);
}

// Or simulate an error
if (params.history_id === "must-fail") {
return response("5XX").json(EXPECTED_500_ERROR, { status: 500 });
}

return response(200).json(TEST_HISTORY_SUMMARY);
})
);

// Your test code here
});
});
```

Using this approach, it will ensure the type safety of the API calls and the responses. If you need to mock API calls that are not defined in the OpenAPI specs, you can use the `http.untyped` variant to mock any API route. Or define an untyped response for a specific route with `HttpResponse`. See the example below:

```ts
const catchAll = http.untyped.all("/resource/*", ({ params }) => {
return HttpResponse.json(/* ... */);
});
```

For more information on how to use `openapi-msw`, you can check the [official documentation](https://github.com/christoph-fricke/openapi-msw?tab=readme-ov-file#handling-unknown-paths).
21 changes: 0 additions & 21 deletions client/openapi_to_schema.mjs

This file was deleted.

8 changes: 6 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@
"markdown-it": "^13.0.2",
"markdown-it-regexp": "^0.4.0",
"object-hash": "^3.0.0",
"openapi-typescript": "^6.7.6",
"openapi-typescript-fetch": "^1.1.3",
"openapi-fetch": "^0.10.6",
"pinia": "^2.1.7",
"popper.js": "^1.16.1",
"pretty-bytes": "^6.1.1",
Expand Down Expand Up @@ -172,15 +171,20 @@
"eslint-plugin-vue": "^9.17.0",
"eslint-plugin-vuejs-accessibility": "^2.2.0",
"expose-loader": "^4.1.0",
"fake-indexeddb": "^6.0.0",
"gulp": "^4.0.2",
"ignore-loader": "^0.1.2",
"imports-loader": "^4.0.1",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-fixed-jsdom": "^0.0.2",
"jest-location-mock": "^2.0.0",
"jsdom-worker": "^0.3.0",
"json-loader": "^0.5.7",
"mini-css-extract-plugin": "^2.7.6",
"msw": "^2.3.4",
"openapi-msw": "^0.7.0",
"openapi-typescript": "^7.3.0",
"postcss-loader": "^7.3.3",
"prettier": "^2.8.8",
"process": "^0.11.10",
Expand Down
68 changes: 68 additions & 0 deletions client/src/api/client/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { HttpResponse } from "msw";
import { setupServer } from "msw/node";
import { createOpenApiHttp } from "openapi-msw";

import { type GalaxyApiPaths } from "@/api/schema";

export { HttpResponse };

function createApiClientMock() {
return createOpenApiHttp<GalaxyApiPaths>({ baseUrl: window.location.origin });
}

let http: ReturnType<typeof createApiClientMock>;
let server: ReturnType<typeof setupServer>;

/**
* Returns a `server` instance that can be used to mock the Galaxy API server
* and make requests to the Galaxy API using the OpenAPI schema.
*
* It is an instance of Mock Service Worker (MSW) server (https://github.com/mswjs/msw).
* And the `http` object is an instance of OpenAPI-MSW (https://github.com/christoph-fricke/openapi-msw)
* that add support for full type inference from OpenAPI schema definitions.
*/
export function useServerMock() {
if (!server) {
server = setupServer();
http = createApiClientMock();
}

beforeAll(() => {
// Enable API mocking before all the tests.
server.listen({
onUnhandledRequest: (request) => {
const method = request.method.toLowerCase();
const apiPath = request.url.replace(window.location.origin, "");
const errorMessage = `
No request handler found for ${request.method} ${request.url}.

Make sure you have added a request handler for this request in your tests.

Example:

const { server, http } = useServerMock();
server.use(
http.${method}('${apiPath}', ({ response }) => {
return response(200).json({});
})
);
`;
throw new Error(errorMessage);
},
});
});

afterEach(() => {
// Reset the request handlers between each test.
// This way the handlers we add on a per-test basis
// do not leak to other, irrelevant tests.
server.resetHandlers();
});

afterAll(() => {
// Finally, disable API mocking after the tests are done.
server.close();
});

return { server, http };
}
32 changes: 32 additions & 0 deletions client/src/api/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import createClient from "openapi-fetch";

import { type GalaxyApiPaths } from "@/api/schema";
import { getAppRoot } from "@/onload/loadConfig";

function getBaseUrl() {
const isTest = process.env.NODE_ENV === "test";
return isTest ? window.location.origin : getAppRoot(undefined, true);
}

function apiClientFactory() {
return createClient<GalaxyApiPaths>({ baseUrl: getBaseUrl() });
}

export type GalaxyApiClient = ReturnType<typeof apiClientFactory>;

let client: GalaxyApiClient;

/**
* Returns the Galaxy API client.
*
* It can be used to make requests to the Galaxy API using the OpenAPI schema.
*
* See: https://openapi-ts.dev/openapi-fetch/
*/
export function GalaxyApi(): GalaxyApiClient {
if (!client) {
client = apiClientFactory();
}

return client;
}
Loading
Loading