Add graphql logic to RequestManager

This commit is contained in:
schroda
2023-09-01 20:21:59 +02:00
parent 0ee47c872b
commit 181eb6c811
2 changed files with 179 additions and 0 deletions

View File

@@ -9,6 +9,18 @@
import { AxiosInstance, AxiosRequestConfig } from 'axios';
import useSWR, { Middleware, SWRConfiguration, SWRResponse } from 'swr';
import useSWRInfinite, { SWRInfiniteConfiguration, SWRInfiniteResponse } from 'swr/infinite';
import {
FetchResult,
MutationHookOptions,
MutationOptions,
MutationTuple,
QueryHookOptions,
QueryResult,
TypedDocumentNode,
useMutation,
useQuery,
} from '@apollo/client';
import { OperationVariables } from '@apollo/client/core';
import {
BackupValidationResult,
BatchChaptersChange,
@@ -31,6 +43,7 @@ import {
} from '@/typings.ts';
import { HttpMethod as DefaultHttpMethod, IRestClient, RestClient } from '@/lib/requests/client/RestClient.ts';
import storage from '@/util/localStorage.tsx';
import { GraphQLClient } from '@/lib/requests/client/GraphQLClient.ts';
enum SWRHttpMethod {
SWR_GET,
@@ -39,6 +52,12 @@ enum SWRHttpMethod {
SWR_POST_INFINITE,
}
enum GQLMethod {
USE_QUERY = 'USE_QUERY',
USE_MUTATION = 'USE_MUTATION',
MUTATION = 'MUTATION',
}
type HttpMethodType = DefaultHttpMethod | SWRHttpMethod;
const HttpMethod = { ...SWRHttpMethod, ...DefaultHttpMethod };
@@ -64,6 +83,16 @@ export type AbortableSWRInfiniteResponse<Data = any, Error = any> = SWRInfiniteR
AbortableRequest &
SWRInfiniteResponseLoadInfo;
type AbortableApolloUseQueryResponse<
Data = any,
Variables extends OperationVariables = OperationVariables,
> = QueryResult<Data, Variables> & AbortableRequest;
type AbortableApolloUseMutationResponse<Data = any, Variables extends OperationVariables = OperationVariables> = [
MutationTuple<Data, Variables>[0],
MutationTuple<Data, Variables>[1] & AbortableRequest,
];
type AbortableApolloMutationResponse<Data = any> = { response: Promise<FetchResult<Data>> } & AbortableRequest;
const isLoadingMore = (swrResult: SWRInfiniteResponse): boolean => {
const isNextPageMissing = !!swrResult.data && typeof swrResult.data[swrResult.size - 1] === 'undefined';
const isRequestActive = swrResult.isValidating;
@@ -97,6 +126,8 @@ const disableSwrCache: Middleware = (useSWRNext) => (key, fetcher, config) => {
export class RequestManager {
public static readonly API_VERSION = '/api/v1/';
private readonly graphQLClient = new GraphQLClient();
private readonly restClient: RestClient = new RestClient();
public getClient(): IRestClient {
@@ -105,6 +136,7 @@ export class RequestManager {
public updateClient(config: Partial<AxiosInstance['defaults']>): void {
this.restClient.updateConfig(config);
this.graphQLClient.updateConfig({ uri: config.baseURL });
}
public getBaseUrl(): string {
@@ -138,6 +170,100 @@ export class RequestManager {
return `${this.getValidUrlFor(imageUrl, apiVersion)}${useCacheQuery}`;
}
private doRequestNew<Data, Variables extends OperationVariables = OperationVariables>(
method: GQLMethod.USE_QUERY,
operation: TypedDocumentNode<Data, Variables>,
variables: Variables,
options?: Partial<QueryHookOptions<Data, Variables>>,
): AbortableApolloUseQueryResponse<Data, Variables>;
private doRequestNew<Data, Variables extends OperationVariables = OperationVariables>(
method: GQLMethod.USE_MUTATION,
operation: TypedDocumentNode<Data, Variables>,
variables: Variables | undefined,
options?: Partial<MutationHookOptions<Data, Variables>>,
): AbortableApolloUseMutationResponse<Data, Variables>;
private doRequestNew<Data, Variables extends OperationVariables = OperationVariables>(
method: GQLMethod.MUTATION,
operation: TypedDocumentNode<Data, Variables>,
variables: Variables,
options?: Partial<MutationOptions<Data, Variables>>,
): AbortableApolloMutationResponse<Data>;
private doRequestNew<Data, Variables extends OperationVariables = OperationVariables>(
method: GQLMethod,
operation: TypedDocumentNode<Data, Variables>,
variables: Variables,
options?:
| QueryHookOptions<Data, Variables>
| MutationHookOptions<Data, Variables>
| MutationOptions<Data, Variables>,
):
| AbortableApolloUseQueryResponse<Data, Variables>
| AbortableApolloUseMutationResponse<Data, Variables>
| AbortableApolloMutationResponse<Data> {
const abortController = new AbortController();
const abortRequest = (reason?: any): void => {
if (!abortController.signal.aborted) {
abortController.abort(reason);
}
};
switch (method) {
case GQLMethod.USE_QUERY:
return {
...useQuery<Data, Variables>(operation, {
variables,
client: this.graphQLClient.client,
...options,
context: {
...options?.context,
fetchOptions: {
signal: abortController.signal,
...options?.context?.fetchOptions,
},
},
}),
abortRequest,
};
case GQLMethod.USE_MUTATION:
// eslint-disable-next-line no-case-declarations
const mutationResult = useMutation<Data, Variables>(operation, {
variables,
client: this.graphQLClient.client,
...(options as MutationHookOptions<Data, Variables>),
context: {
...options?.context,
fetchOptions: {
signal: abortController.signal,
...options?.context?.fetchOptions,
},
},
});
return [mutationResult[0], { ...mutationResult[1], abortRequest }];
case GQLMethod.MUTATION:
return {
response: this.graphQLClient.client.mutate<Data, Variables>({
mutation: operation,
variables,
...(options as MutationOptions<Data, Variables>),
context: {
...options?.context,
fetchOptions: {
signal: abortController.signal,
...options?.context?.fetchOptions,
},
},
}),
abortRequest,
};
default:
throw new Error(`unexpected GQLRequest type "${method}"`);
}
}
private useSwr<
Data = any,
ErrorResponse = any,

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { ApolloClient, ApolloClientOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
import { createUploadLink } from 'apollo-upload-client';
import { BaseClient } from '@/lib/requests/client/BaseClient.ts';
import { StrictTypedTypePolicies } from '@/lib/graphql/generated/apollo-helpers.ts';
const typePolicies: StrictTypedTypePolicies = {
GlobalMetaType: { keyFields: ['key'] },
ExtensionType: { keyFields: ['apkName'] },
AboutPayload: { keyFields: [] },
};
// eslint-disable-next-line import/prefer-default-export
export class GraphQLClient extends BaseClient<
ApolloClient<NormalizedCacheObject>,
ApolloClientOptions<NormalizedCacheObject>,
null
> {
readonly fetcher = null;
public declare client: ApolloClient<NormalizedCacheObject>;
public override getBaseUrl(): string {
return `${super.getBaseUrl()}/api/graphql`;
}
private createUploadLink() {
return createUploadLink({ uri: () => this.getBaseUrl() });
}
private createLink() {
return this.createUploadLink();
}
protected createClient() {
this.client = new ApolloClient({
cache: new InMemoryCache({
typePolicies,
}),
connectToDevTools: true,
link: this.createLink(),
});
}
public override updateConfig() {}
}