mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): api layer refactor
*migrate from `openapi-typescript-codegen` to `openapi-typescript` and `openapi-fetch`* `openapi-typescript-codegen` is not very actively maintained - it's been over a year since the last update. `openapi-typescript` and `openapi-fetch` are part of the actively maintained repo. key differences: - provides a `fetch` client instead of `axios`, which means we need to be a bit more verbose with typing thunks - fetch client is created at runtime and has a very nice typescript DX - generates a single file with all types in it, from which we then extract individual types. i don't like how verbose this is, but i do like how it is more explicit. - removed npm api generation scripts - now we have a single `typegen` script overall i have more confidence in this new library. *use nanostores for api base and token* very simple reactive store for api base url and token. this was suggested in the `openapi-fetch` docs and i quite like the strategy. *organise rtk-query api* split out each endpoint (models, images, boards, boardImages) into their own api extensions. tidy!
This commit is contained in:
@ -0,0 +1,66 @@
|
||||
import { OffsetPaginatedResults_ImageDTO_ } from 'services/api/types';
|
||||
import { api } from '..';
|
||||
import { paths } from '../schema';
|
||||
|
||||
type ListBoardImagesArg =
|
||||
paths['/api/v1/board_images/{board_id}']['get']['parameters']['path'] &
|
||||
paths['/api/v1/board_images/{board_id}']['get']['parameters']['query'];
|
||||
|
||||
type AddImageToBoardArg =
|
||||
paths['/api/v1/board_images/']['post']['requestBody']['content']['application/json'];
|
||||
|
||||
type RemoveImageFromBoardArg =
|
||||
paths['/api/v1/board_images/']['delete']['requestBody']['content']['application/json'];
|
||||
|
||||
export const boardImagesApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
/**
|
||||
* Board Images Queries
|
||||
*/
|
||||
|
||||
listBoardImages: build.query<
|
||||
OffsetPaginatedResults_ImageDTO_,
|
||||
ListBoardImagesArg
|
||||
>({
|
||||
query: ({ board_id, offset, limit }) => ({
|
||||
url: `board_images/${board_id}`,
|
||||
method: 'DELETE',
|
||||
body: { offset, limit },
|
||||
}),
|
||||
}),
|
||||
|
||||
/**
|
||||
* Board Images Mutations
|
||||
*/
|
||||
|
||||
addImageToBoard: build.mutation<void, AddImageToBoardArg>({
|
||||
query: ({ board_id, image_name }) => ({
|
||||
url: `board_images/`,
|
||||
method: 'POST',
|
||||
body: { board_id, image_name },
|
||||
}),
|
||||
invalidatesTags: (result, error, arg) => [
|
||||
{ type: 'Board', id: arg.board_id },
|
||||
{ type: 'Image', id: arg.image_name },
|
||||
],
|
||||
}),
|
||||
|
||||
removeImageFromBoard: build.mutation<void, RemoveImageFromBoardArg>({
|
||||
query: ({ board_id, image_name }) => ({
|
||||
url: `board_images/`,
|
||||
method: 'DELETE',
|
||||
body: { board_id, image_name },
|
||||
}),
|
||||
invalidatesTags: (result, error, arg) => [
|
||||
{ type: 'Board', id: arg.board_id },
|
||||
{ type: 'Image', id: arg.image_name },
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useAddImageToBoardMutation,
|
||||
useRemoveImageFromBoardMutation,
|
||||
useListBoardImagesQuery,
|
||||
} = boardImagesApi;
|
99
invokeai/frontend/web/src/services/api/endpoints/boards.ts
Normal file
99
invokeai/frontend/web/src/services/api/endpoints/boards.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { BoardDTO, OffsetPaginatedResults_BoardDTO_ } from 'services/api/types';
|
||||
import { ApiFullTagDescription, LIST_TAG, api } from '..';
|
||||
import { paths } from '../schema';
|
||||
|
||||
type ListBoardsArg = NonNullable<
|
||||
paths['/api/v1/boards/']['get']['parameters']['query']
|
||||
>;
|
||||
|
||||
type UpdateBoardArg =
|
||||
paths['/api/v1/boards/{board_id}']['patch']['parameters']['path'] & {
|
||||
changes: paths['/api/v1/boards/{board_id}']['patch']['requestBody']['content']['application/json'];
|
||||
};
|
||||
|
||||
export const boardsApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
/**
|
||||
* Boards Queries
|
||||
*/
|
||||
listBoards: build.query<OffsetPaginatedResults_BoardDTO_, ListBoardsArg>({
|
||||
query: (arg) => ({ url: 'boards/', params: arg }),
|
||||
providesTags: (result, error, arg) => {
|
||||
// any list of boards
|
||||
const tags: ApiFullTagDescription[] = [{ id: 'Board', type: LIST_TAG }];
|
||||
|
||||
if (result) {
|
||||
// and individual tags for each board
|
||||
tags.push(
|
||||
...result.items.map(({ board_id }) => ({
|
||||
type: 'Board' as const,
|
||||
id: board_id,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
return tags;
|
||||
},
|
||||
}),
|
||||
|
||||
listAllBoards: build.query<Array<BoardDTO>, void>({
|
||||
query: () => ({
|
||||
url: 'boards/',
|
||||
params: { all: true },
|
||||
}),
|
||||
providesTags: (result, error, arg) => {
|
||||
// any list of boards
|
||||
const tags: ApiFullTagDescription[] = [{ id: 'Board', type: LIST_TAG }];
|
||||
|
||||
if (result) {
|
||||
// and individual tags for each board
|
||||
tags.push(
|
||||
...result.map(({ board_id }) => ({
|
||||
type: 'Board' as const,
|
||||
id: board_id,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
return tags;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Boards Mutations
|
||||
*/
|
||||
|
||||
createBoard: build.mutation<BoardDTO, string>({
|
||||
query: (board_name) => ({
|
||||
url: `boards/`,
|
||||
method: 'POST',
|
||||
params: { board_name },
|
||||
}),
|
||||
invalidatesTags: [{ id: 'Board', type: LIST_TAG }],
|
||||
}),
|
||||
|
||||
updateBoard: build.mutation<BoardDTO, UpdateBoardArg>({
|
||||
query: ({ board_id, changes }) => ({
|
||||
url: `boards/${board_id}`,
|
||||
method: 'PATCH',
|
||||
body: changes,
|
||||
}),
|
||||
invalidatesTags: (result, error, arg) => [
|
||||
{ type: 'Board', id: arg.board_id },
|
||||
],
|
||||
}),
|
||||
|
||||
deleteBoard: build.mutation<void, string>({
|
||||
query: (board_id) => ({ url: `boards/${board_id}`, method: 'DELETE' }),
|
||||
invalidatesTags: (result, error, arg) => [{ type: 'Board', id: arg }],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useListBoardsQuery,
|
||||
useListAllBoardsQuery,
|
||||
useCreateBoardMutation,
|
||||
useUpdateBoardMutation,
|
||||
useDeleteBoardMutation,
|
||||
} = boardsApi;
|
22
invokeai/frontend/web/src/services/api/endpoints/images.ts
Normal file
22
invokeai/frontend/web/src/services/api/endpoints/images.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ApiFullTagDescription, api } from '..';
|
||||
import { ImageDTO } from '../types';
|
||||
|
||||
export const imagesApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
/**
|
||||
* Image Queries
|
||||
*/
|
||||
getImageDTO: build.query<ImageDTO, string>({
|
||||
query: (image_name) => ({ url: `images/${image_name}/metadata` }),
|
||||
providesTags: (result, error, arg) => {
|
||||
const tags: ApiFullTagDescription[] = [{ type: 'Image', id: arg }];
|
||||
if (result?.board_id) {
|
||||
tags.push({ type: 'Board', id: result.board_id });
|
||||
}
|
||||
return tags;
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetImageDTOQuery } = imagesApi;
|
52
invokeai/frontend/web/src/services/api/endpoints/models.ts
Normal file
52
invokeai/frontend/web/src/services/api/endpoints/models.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { ModelsList } from 'services/api/types';
|
||||
import { EntityState, createEntityAdapter } from '@reduxjs/toolkit';
|
||||
import { keyBy } from 'lodash-es';
|
||||
|
||||
import { ApiFullTagDescription, LIST_TAG, api } from '..';
|
||||
import { paths } from '../schema';
|
||||
|
||||
type ModelConfig = ModelsList['models'][number];
|
||||
|
||||
type ListModelsArg = NonNullable<
|
||||
paths['/api/v1/models/']['get']['parameters']['query']
|
||||
>;
|
||||
|
||||
const modelsAdapter = createEntityAdapter<ModelConfig>({
|
||||
selectId: (model) => getModelId(model),
|
||||
sortComparer: (a, b) => a.name.localeCompare(b.name),
|
||||
});
|
||||
|
||||
const getModelId = ({ base_model, type, name }: ModelConfig) =>
|
||||
`${base_model}/${type}/${name}`;
|
||||
|
||||
export const modelsApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
listModels: build.query<EntityState<ModelConfig>, ListModelsArg>({
|
||||
query: (arg) => ({ url: 'models/', params: arg }),
|
||||
providesTags: (result, error, arg) => {
|
||||
// any list of boards
|
||||
const tags: ApiFullTagDescription[] = [{ id: 'Model', type: LIST_TAG }];
|
||||
|
||||
if (result) {
|
||||
// and individual tags for each board
|
||||
tags.push(
|
||||
...result.ids.map((id) => ({
|
||||
type: 'Model' as const,
|
||||
id,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
return tags;
|
||||
},
|
||||
transformResponse: (response: ModelsList, meta, arg) => {
|
||||
return modelsAdapter.setAll(
|
||||
modelsAdapter.getInitialState(),
|
||||
keyBy(response.models, getModelId)
|
||||
);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useListModelsQuery } = modelsApi;
|
Reference in New Issue
Block a user