@stack-spot/portal-network 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.generate-api.failed.json +1 -0
- package/dist/api/account.d.ts +2368 -0
- package/dist/api/account.d.ts.map +1 -0
- package/dist/api/account.js +1521 -0
- package/dist/api/account.js.map +1 -0
- package/dist/api/ai.d.ts +1432 -0
- package/dist/api/ai.d.ts.map +1 -0
- package/dist/api/ai.js +1342 -0
- package/dist/api/ai.js.map +1 -0
- package/dist/api/apiRuntime.d.ts +922 -0
- package/dist/api/apiRuntime.d.ts.map +1 -0
- package/dist/api/apiRuntime.js +599 -0
- package/dist/api/apiRuntime.js.map +1 -0
- package/dist/api/cloudAccount.d.ts +473 -0
- package/dist/api/cloudAccount.d.ts.map +1 -0
- package/dist/api/cloudAccount.js +300 -0
- package/dist/api/cloudAccount.js.map +1 -0
- package/dist/api/cloudServices.d.ts +1233 -0
- package/dist/api/cloudServices.d.ts.map +1 -0
- package/dist/api/cloudServices.js +715 -0
- package/dist/api/cloudServices.js.map +1 -0
- package/dist/api/insights.d.ts +123 -0
- package/dist/api/insights.d.ts.map +1 -0
- package/dist/api/insights.js +112 -0
- package/dist/api/insights.js.map +1 -0
- package/dist/api/plugin.d.ts +368 -0
- package/dist/api/plugin.d.ts.map +1 -0
- package/dist/api/plugin.js +218 -0
- package/dist/api/plugin.js.map +1 -0
- package/dist/api/serviceCatalog.d.ts +737 -0
- package/dist/api/serviceCatalog.d.ts.map +1 -0
- package/dist/api/serviceCatalog.js +611 -0
- package/dist/api/serviceCatalog.js.map +1 -0
- package/dist/api/workflows.d.ts +366 -0
- package/dist/api/workflows.d.ts.map +1 -0
- package/dist/api/workflows.js +175 -0
- package/dist/api/workflows.js.map +1 -0
- package/dist/api/workspace.js +1476 -0
- package/dist/api/workspace.js.map +1 -0
- package/dist/api/workspaceManager.d.ts +1121 -0
- package/dist/api/workspaceManager.d.ts.map +1 -0
- package/dist/api/workspaceManager.js +357 -0
- package/dist/api/workspaceManager.js.map +1 -0
- package/dist/api/workspaceSearchEngine.d.ts +93 -0
- package/dist/api/workspaceSearchEngine.d.ts.map +1 -0
- package/dist/api/workspaceSearchEngine.js +55 -0
- package/dist/api/workspaceSearchEngine.js.map +1 -0
- package/dist/apis.json +129 -0
- package/dist/client/account.d.ts +46 -0
- package/dist/client/account.d.ts.map +1 -0
- package/dist/client/account.js +104 -0
- package/dist/client/account.js.map +1 -0
- package/dist/error/CanceledError.d.ts +5 -0
- package/dist/error/CanceledError.d.ts.map +1 -0
- package/dist/error/CanceledError.js +7 -0
- package/dist/error/CanceledError.js.map +1 -0
- package/dist/error/DefaultAPIError.d.ts +9 -0
- package/dist/error/DefaultAPIError.d.ts.map +1 -0
- package/dist/error/DefaultAPIError.js +58 -0
- package/dist/error/DefaultAPIError.js.map +1 -0
- package/dist/error/StackspotAPIError.d.ts +19 -0
- package/dist/error/StackspotAPIError.d.ts.map +1 -0
- package/dist/error/StackspotAPIError.js +39 -0
- package/dist/error/StackspotAPIError.js.map +1 -0
- package/dist/error/dictionary/account.d.ts +55 -0
- package/dist/error/dictionary/account.d.ts.map +1 -0
- package/dist/error/dictionary/account.js +55 -0
- package/dist/error/dictionary/account.js.map +1 -0
- package/dist/error/dictionary/action.d.ts +163 -0
- package/dist/error/dictionary/action.d.ts.map +1 -0
- package/dist/error/dictionary/action.js +163 -0
- package/dist/error/dictionary/action.js.map +1 -0
- package/dist/error/dictionary/base.d.ts +21 -0
- package/dist/error/dictionary/base.d.ts.map +1 -0
- package/dist/error/dictionary/base.js +21 -0
- package/dist/error/dictionary/base.js.map +1 -0
- package/dist/error/dictionary/cnt-fields.d.ts +13 -0
- package/dist/error/dictionary/cnt-fields.d.ts.map +1 -0
- package/dist/error/dictionary/cnt-fields.js +13 -0
- package/dist/error/dictionary/cnt-fields.js.map +1 -0
- package/dist/error/dictionary/cnt.d.ts +79 -0
- package/dist/error/dictionary/cnt.d.ts.map +1 -0
- package/dist/error/dictionary/cnt.js +79 -0
- package/dist/error/dictionary/cnt.js.map +1 -0
- package/dist/error/dictionary/rte.d.ts +23 -0
- package/dist/error/dictionary/rte.d.ts.map +1 -0
- package/dist/error/dictionary/rte.js +23 -0
- package/dist/error/dictionary/rte.js.map +1 -0
- package/dist/error/dictionary/rtm.d.ts +9 -0
- package/dist/error/dictionary/rtm.d.ts.map +1 -0
- package/dist/error/dictionary/rtm.js +9 -0
- package/dist/error/dictionary/rtm.js.map +1 -0
- package/dist/error/dictionary/workspace-fields.d.ts +9 -0
- package/dist/error/dictionary/workspace-fields.d.ts.map +1 -0
- package/dist/error/dictionary/workspace-fields.js +9 -0
- package/dist/error/dictionary/workspace-fields.js.map +1 -0
- package/dist/error/dictionary/workspace.d.ts +99 -0
- package/dist/error/dictionary/workspace.d.ts.map +1 -0
- package/dist/error/dictionary/workspace.js +99 -0
- package/dist/error/dictionary/workspace.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/network/AutoMutation.d.ts +10 -0
- package/dist/network/AutoMutation.d.ts.map +1 -0
- package/dist/network/AutoMutation.js +20 -0
- package/dist/network/AutoMutation.js.map +1 -0
- package/dist/network/AutoOperation.d.ts +19 -0
- package/dist/network/AutoOperation.d.ts.map +1 -0
- package/dist/network/AutoOperation.js +99 -0
- package/dist/network/AutoOperation.js.map +1 -0
- package/dist/network/AutoQuery.d.ts +19 -0
- package/dist/network/AutoQuery.d.ts.map +1 -0
- package/dist/network/AutoQuery.js +70 -0
- package/dist/network/AutoQuery.js.map +1 -0
- package/dist/network/ManualMutation.d.ts +11 -0
- package/dist/network/ManualMutation.d.ts.map +1 -0
- package/dist/network/ManualMutation.js +32 -0
- package/dist/network/ManualMutation.js.map +1 -0
- package/dist/network/ManualOperation.d.ts +13 -0
- package/dist/network/ManualOperation.d.ts.map +1 -0
- package/dist/network/ManualOperation.js +53 -0
- package/dist/network/ManualOperation.js.map +1 -0
- package/dist/network/ManualQuery.d.ts +20 -0
- package/dist/network/ManualQuery.d.ts.map +1 -0
- package/dist/network/ManualQuery.js +77 -0
- package/dist/network/ManualQuery.js.map +1 -0
- package/dist/network/NetworkClient.d.ts +20 -0
- package/dist/network/NetworkClient.d.ts.map +1 -0
- package/dist/network/NetworkClient.js +85 -0
- package/dist/network/NetworkClient.js.map +1 -0
- package/dist/network/ReactQueryNetworkClient.d.ts +16 -0
- package/dist/network/ReactQueryNetworkClient.d.ts.map +1 -0
- package/dist/network/ReactQueryNetworkClient.js +125 -0
- package/dist/network/ReactQueryNetworkClient.js.map +1 -0
- package/dist/network/react-query-client.d.ts +3 -0
- package/dist/network/react-query-client.d.ts.map +1 -0
- package/dist/network/react-query-client.js +3 -0
- package/dist/network/react-query-client.js.map +1 -0
- package/dist/network/types.d.ts +55 -0
- package/dist/network/types.d.ts.map +1 -0
- package/dist/network/types.js +2 -0
- package/dist/network/types.js.map +1 -0
- package/package.json +56 -0
- package/readme.md +1 -0
- package/scripts/generate-apis.ts +134 -0
- package/src/api/account.ts +4793 -0
- package/src/api/ai.ts +3121 -0
- package/src/api/apiRuntime.ts +2029 -0
- package/src/api/cloudAccount.ts +1133 -0
- package/src/api/cloudServices.ts +2872 -0
- package/src/api/insights.ts +264 -0
- package/src/api/plugin.ts +685 -0
- package/src/api/serviceCatalog.ts +2908 -0
- package/src/api/workflows.ts +709 -0
- package/src/api/workspace.ts +5178 -0
- package/src/api/workspaceManager.ts +1516 -0
- package/src/api/workspaceSearchEngine.ts +153 -0
- package/src/apis.json +129 -0
- package/src/client/account.ts +52 -0
- package/src/error/CanceledError.ts +7 -0
- package/src/error/DefaultAPIError.ts +51 -0
- package/src/error/StackspotAPIError.ts +31 -0
- package/src/error/dictionary/account.ts +56 -0
- package/src/error/dictionary/action.ts +206 -0
- package/src/error/dictionary/base.ts +22 -0
- package/src/error/dictionary/cnt-fields.ts +14 -0
- package/src/error/dictionary/cnt.ts +80 -0
- package/src/error/dictionary/rte.ts +24 -0
- package/src/error/dictionary/rtm.ts +10 -0
- package/src/error/dictionary/workspace-fields.ts +10 -0
- package/src/error/dictionary/workspace.ts +100 -0
- package/src/index.ts +5 -0
- package/src/network/AutoMutation.ts +25 -0
- package/src/network/AutoOperation.ts +88 -0
- package/src/network/AutoQuery.ts +80 -0
- package/src/network/ManualMutation.ts +41 -0
- package/src/network/ManualOperation.ts +55 -0
- package/src/network/ManualQuery.ts +93 -0
- package/src/network/NetworkClient.ts +93 -0
- package/src/network/ReactQueryNetworkClient.ts +154 -0
- package/src/network/react-query-client.ts +3 -0
- package/src/network/types.ts +82 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import { DefinedUseQueryResult, UseQueryOptions, UseSuspenseQueryResult, useQuery, useSuspenseQuery } from '@tanstack/react-query'
|
|
4
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
|
+
import { AutoOperation } from './AutoOperation'
|
|
6
|
+
import { queryClient } from './react-query-client'
|
|
7
|
+
import { AutoQueryObjectParams, QueryObject } from './types'
|
|
8
|
+
|
|
9
|
+
export class AutoQuery<Variables, Result> extends AutoOperation<Variables> implements QueryObject<Variables, Result> {
|
|
10
|
+
/**
|
|
11
|
+
* Prevents the same request from being triggered more than once if subsequent calls are made before the first ends.
|
|
12
|
+
*/
|
|
13
|
+
private currentRequests = new Map<Variables | undefined, Promise<Result>>()
|
|
14
|
+
constructor(params: AutoQueryObjectParams<Variables, Result>) {
|
|
15
|
+
super(params)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private createQueryFn(variables: Variables | undefined) {
|
|
19
|
+
return async () => {
|
|
20
|
+
if (!this.currentRequests.has(variables)) this.currentRequests.set(variables, this.callFn(variables))
|
|
21
|
+
const result = await this.currentRequests.get(variables)
|
|
22
|
+
this.currentRequests.delete(variables)
|
|
23
|
+
return result!
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
query(...[variables]: Variables extends void ? [] : [variables: Variables]) {
|
|
28
|
+
return queryClient.fetchQuery({
|
|
29
|
+
queryKey: this.getKey(variables),
|
|
30
|
+
queryFn: this.createQueryFn(variables),
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#useQueryResult<T extends boolean>(
|
|
35
|
+
suspense: T,
|
|
36
|
+
...args: Variables extends void
|
|
37
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
38
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
39
|
+
) {
|
|
40
|
+
/* `this.fn` is a oazapfts function, i.e. it has arity 1 or 2. If it accepts variables, its arity is 2: the 1st parameter is the
|
|
41
|
+
variables and the 2nd is the RequestOpts. If it doesn't accept variables, its arity is one: it accepts only the RequestOpts.
|
|
42
|
+
We can use this information to determine what the type of `args` actually is at runtime. If variables are accepted, than the 1st
|
|
43
|
+
argument is the variables and the 2nd is the query options, otherwise, it has a single argument, which is the query options. */
|
|
44
|
+
const [variables, options] = this.fn.length > 1
|
|
45
|
+
? args as [Variables, Omit<UseQueryOptions, 'queryFn' | 'queryKey'> | undefined]
|
|
46
|
+
: [undefined, args[0] as Omit<UseQueryOptions, 'queryFn' | 'queryKey'> | undefined]
|
|
47
|
+
const use = suspense ? useSuspenseQuery : useQuery
|
|
48
|
+
return use({
|
|
49
|
+
...options,
|
|
50
|
+
queryKey: this.getKey(variables),
|
|
51
|
+
queryFn: this.createQueryFn(variables),
|
|
52
|
+
}, queryClient) as T extends true ? UseSuspenseQueryResult<Result, StackspotAPIError> : DefinedUseQueryResult<Result, StackspotAPIError>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
useQuery(
|
|
56
|
+
...args: Variables extends void
|
|
57
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
58
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
59
|
+
) {
|
|
60
|
+
const result = this.#useQueryResult(true, ...args)
|
|
61
|
+
return result.data
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
useStatefulQuery(
|
|
65
|
+
...args: Variables extends void
|
|
66
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
67
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
68
|
+
) {
|
|
69
|
+
const result = this.#useQueryResult(false, ...args)
|
|
70
|
+
return [result.data, result.isPending, result.error as StackspotAPIError | undefined, result] as const
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
invalidate(variables?: Partial<Variables>) {
|
|
74
|
+
return queryClient.invalidateQueries({ queryKey: this.getKey(variables) })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getKey(variables?: Partial<Variables>) {
|
|
78
|
+
return [this.apiName, this.fn.name, variables]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import { UseMutationOptions, useMutation } from '@tanstack/react-query'
|
|
4
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
|
+
import { ManualOperation } from './ManualOperation'
|
|
6
|
+
import { queryClient } from './react-query-client'
|
|
7
|
+
import { FullOperationConfig, MutationObject } from './types'
|
|
8
|
+
|
|
9
|
+
export class ManualMutation<
|
|
10
|
+
Variables extends Record<string, any> | void,
|
|
11
|
+
Result
|
|
12
|
+
> extends ManualOperation<Variables> implements MutationObject<Variables, Result> {
|
|
13
|
+
constructor(config: FullOperationConfig<Variables extends void ? [AbortSignal] : [AbortSignal, Variables], Result>) {
|
|
14
|
+
super(config)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
private async makeRequest(variables?: Record<string, any>) {
|
|
18
|
+
const abortController = new AbortController()
|
|
19
|
+
if (!this.abortMap.has(variables)) this.abortMap.set(variables, [])
|
|
20
|
+
this.abortMap.get(variables)?.push(abortController)
|
|
21
|
+
try {
|
|
22
|
+
return await this.config.request(
|
|
23
|
+
...[abortController.signal, variables] as Variables extends void ? [AbortSignal] : [AbortSignal, Variables],
|
|
24
|
+
)
|
|
25
|
+
} finally {
|
|
26
|
+
this.abortMap.delete(variables)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
mutate(...args: Variables extends void ? [] : [variables: Variables]) {
|
|
31
|
+
return this.makeRequest(...args)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
useMutation(options?: Omit<UseMutationOptions<Result, StackspotAPIError, Variables>, 'mutationFn'>) {
|
|
35
|
+
const result = useMutation<Result, StackspotAPIError, Variables>({
|
|
36
|
+
...options,
|
|
37
|
+
mutationFn: (...args: any) => this.makeRequest(...args),
|
|
38
|
+
}, queryClient)
|
|
39
|
+
return [result.mutateAsync as any, result.isPending, result.error as StackspotAPIError | undefined, result as any] as const
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import { UseQueryOptions, UseQueryResult, useQuery } from '@tanstack/react-query'
|
|
4
|
+
import { CanceledError } from '../error/CanceledError'
|
|
5
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
6
|
+
import { queryClient } from './react-query-client'
|
|
7
|
+
import { FullOperationConfig, OperationObject } from './types'
|
|
8
|
+
|
|
9
|
+
export abstract class ManualOperation<Variables extends Record<string, any> | void> implements OperationObject<Variables> {
|
|
10
|
+
protected config: FullOperationConfig<Variables extends void ? [AbortSignal] : [AbortSignal, Variables], any>
|
|
11
|
+
protected abortMap = new Map<Record<string, any> | undefined, AbortController[]>()
|
|
12
|
+
|
|
13
|
+
constructor(config: FullOperationConfig<Variables extends void ? [AbortSignal] : [AbortSignal, Variables], any>) {
|
|
14
|
+
this.config = config
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
isAllowed(...[variables]: Variables extends void ? [] : [variables?: Partial<Variables>]) {
|
|
18
|
+
return queryClient.fetchQuery({
|
|
19
|
+
queryKey: this.getPermissionKey(variables as Variables),
|
|
20
|
+
// @ts-ignore the following is correct. TS can't correctly infer the conditional type here =(
|
|
21
|
+
queryFn: () => this.config.permission(variables),
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
useAllowed(
|
|
26
|
+
...args: Variables extends void
|
|
27
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
28
|
+
: [variables?: Partial<Variables>, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
29
|
+
) {
|
|
30
|
+
const [variables, options] = args.length === 2 ? args : [undefined, args[0]]
|
|
31
|
+
const result = useQuery({
|
|
32
|
+
...options,
|
|
33
|
+
queryKey: this.getPermissionKey(variables as Variables),
|
|
34
|
+
// @ts-ignore the following is correct. TS can't correctly infer the conditional type here =(
|
|
35
|
+
queryFn: () => this.config.permission(variables),
|
|
36
|
+
}, queryClient) as UseQueryResult<boolean, StackspotAPIError>
|
|
37
|
+
return [result.data, result.isPending, result.error, result] as const
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getPermissionKey(variables?: Partial<Variables>) {
|
|
41
|
+
return [this.config.apiName, `${this.config.name}.permission`, variables]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
cancel(variables?: Partial<Variables> | undefined) {
|
|
45
|
+
if (variables && this.abortMap.has(variables)) {
|
|
46
|
+
this.abortMap.get(variables)?.forEach(c => c.abort(new CanceledError()))
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
if (!variables && this.abortMap.size) {
|
|
50
|
+
this.abortMap.forEach(controllers => controllers.forEach(c => c.abort(new CanceledError())))
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import { DefinedUseQueryResult, UseQueryOptions, UseSuspenseQueryResult, useQuery, useSuspenseQuery } from '@tanstack/react-query'
|
|
4
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
|
+
import { ManualOperation } from './ManualOperation'
|
|
6
|
+
import { queryClient } from './react-query-client'
|
|
7
|
+
import { FullOperationConfig, QueryObject } from './types'
|
|
8
|
+
|
|
9
|
+
export class ManualQuery<
|
|
10
|
+
Variables extends Record<string, any> | void,
|
|
11
|
+
Result
|
|
12
|
+
> extends ManualOperation<Variables> implements QueryObject<Variables, Result> {
|
|
13
|
+
/**
|
|
14
|
+
* Prevents the same request from being triggered more than once if subsequent calls are made before the first ends.
|
|
15
|
+
*/
|
|
16
|
+
private currentRequests = new Map<Record<string, any> | undefined, Promise<Result>>()
|
|
17
|
+
|
|
18
|
+
constructor(config: FullOperationConfig<Variables extends void ? [AbortSignal] : [AbortSignal, Variables], Result>) {
|
|
19
|
+
super(config)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private async makeRequest(variables: Record<string, any> | undefined) {
|
|
23
|
+
const abortController = new AbortController()
|
|
24
|
+
this.abortMap.set(variables, [abortController])
|
|
25
|
+
try {
|
|
26
|
+
return await this.config.request(
|
|
27
|
+
...[abortController.signal, variables] as Variables extends void ? [AbortSignal] : [AbortSignal, Variables],
|
|
28
|
+
)
|
|
29
|
+
} finally {
|
|
30
|
+
this.abortMap.delete(variables)
|
|
31
|
+
this.currentRequests.delete(variables)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private createQueryFn(variables: Record<string, any> | undefined) {
|
|
36
|
+
return () => {
|
|
37
|
+
if (!this.currentRequests.has(variables)) this.currentRequests.set(variables, this.makeRequest(variables))
|
|
38
|
+
return this.currentRequests.get(variables)!
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
query(...[variables]: Variables extends void ? [] : [variables: Variables]) {
|
|
43
|
+
return queryClient.fetchQuery({
|
|
44
|
+
queryKey: this.getKey(variables as Variables),
|
|
45
|
+
queryFn: this.createQueryFn(variables),
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#useQueryResult<T extends boolean>(
|
|
50
|
+
suspense: T,
|
|
51
|
+
...args: Variables extends void
|
|
52
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
53
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
54
|
+
) {
|
|
55
|
+
/* `this.config.request` is a function with arity 1 or 2. If it accepts variables, its arity is 2: the 1st parameter is the
|
|
56
|
+
AbortSignal and the 2nd is the variables. If it doesn't accept variables, its arity is one: it accepts only the AbortSignal.
|
|
57
|
+
We can use this information to determine what the type of `args` actually is at runtime. If variables are accepted, than the 1st
|
|
58
|
+
argument is the variables and the 2nd is the query options, otherwise, it has a single argument, which is the query options. */
|
|
59
|
+
const [variables, options] = this.config.request.length === 2 ? args : [undefined, args[0]]
|
|
60
|
+
const use = suspense ? useSuspenseQuery : useQuery
|
|
61
|
+
return use({
|
|
62
|
+
...options,
|
|
63
|
+
queryKey: this.getKey(variables as Variables),
|
|
64
|
+
queryFn: this.createQueryFn(variables),
|
|
65
|
+
}, queryClient) as T extends true ? UseSuspenseQueryResult<Result, StackspotAPIError> : DefinedUseQueryResult<Result, StackspotAPIError>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
useQuery(
|
|
69
|
+
...args: Variables extends void
|
|
70
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
71
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
72
|
+
) {
|
|
73
|
+
const result = this.#useQueryResult(false, ...args)
|
|
74
|
+
return result.data
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
useStatefulQuery(
|
|
78
|
+
...args: Variables extends void
|
|
79
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
80
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
81
|
+
) {
|
|
82
|
+
const result = this.#useQueryResult(false, ...args)
|
|
83
|
+
return [result.data, result.isPending, result.error as StackspotAPIError | undefined, result] as const
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
invalidate(variables?: Partial<Variables>) {
|
|
87
|
+
return queryClient.invalidateQueries({ queryKey: this.getKey(variables) })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getKey(variables?: Partial<Variables>) {
|
|
91
|
+
return [this.config.apiName, this.config.name, variables]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { mergeHeaders } from '@oazapfts/runtime/headers'
|
|
2
|
+
import { AuthenticationError } from '@stack-spot/auth'
|
|
3
|
+
import { requestPermission } from '@stack-spot/opa'
|
|
4
|
+
import { Env, HTTPMethod, RequestOptions, RequestWithBody, SessionManager } from './types'
|
|
5
|
+
|
|
6
|
+
export class NetworkClient {
|
|
7
|
+
private baseURL: Record<Env, string>
|
|
8
|
+
private static sessionManager?: SessionManager
|
|
9
|
+
private static env?: Env
|
|
10
|
+
|
|
11
|
+
constructor(baseURL: Record<Env, string>) {
|
|
12
|
+
this.baseURL = baseURL
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static setup(sessionManager: SessionManager, env: Env) {
|
|
16
|
+
NetworkClient.sessionManager = sessionManager
|
|
17
|
+
NetworkClient.env = env
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private uninitializedError() {
|
|
21
|
+
return new Error('Please, call "NetworkClient.setup(sessionManager, env)" before attempting to make a request.')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected resolveURL(path: string) {
|
|
25
|
+
if (!NetworkClient.env) throw this.uninitializedError()
|
|
26
|
+
// paths must not start with "/", otherwise, the base url will not be fully appended to it.
|
|
27
|
+
const fixedPath = path.replace(/^\//, '')
|
|
28
|
+
// the baseUrl must end with "/", otherwise, the last of its part will get replaced by the path, instead of concatenated with.
|
|
29
|
+
const fixedBaseUrl = this.baseURL[NetworkClient.env].replace(/([^/])$/, '$1/')
|
|
30
|
+
return new URL(fixedPath, fixedBaseUrl)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected requestPermission(method: HTTPMethod, path: string, body?: string | object): Promise<boolean> {
|
|
34
|
+
return requestPermission(method, this.resolveURL(path).toString(), body)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private getSessionManager() {
|
|
38
|
+
const sessionManager = NetworkClient.sessionManager
|
|
39
|
+
if (!sessionManager) throw this.uninitializedError()
|
|
40
|
+
return sessionManager
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private sendRequest(path: string, method: HTTPMethod, request?: RequestWithBody): Promise<Response> {
|
|
44
|
+
const sessionManager = this.getSessionManager()
|
|
45
|
+
const url = this.resolveURL(path)
|
|
46
|
+
try {
|
|
47
|
+
if (request?.params) {
|
|
48
|
+
Object.keys(request.params).forEach((key) => {
|
|
49
|
+
const value = request?.params?.[key]
|
|
50
|
+
if (value !== undefined) url.searchParams.set(key, value)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
const isFormData = request?.body instanceof FormData
|
|
54
|
+
const isJsonContent = (typeof request?.body === 'object') && !isFormData
|
|
55
|
+
const body = isJsonContent ? JSON.stringify(request.body) : request?.body as string | FormData
|
|
56
|
+
const defaultHeaders: Record<string, string> = isJsonContent ? { 'Content-Type': 'application/json' } : {}
|
|
57
|
+
const headers = mergeHeaders(defaultHeaders, request?.headers)
|
|
58
|
+
return (
|
|
59
|
+
sessionManager.hasSession()
|
|
60
|
+
? sessionManager.getSession().fetch(url, { method: method.toUpperCase(), headers, body })
|
|
61
|
+
: fetch(url, { method: method.toUpperCase(), headers, body })
|
|
62
|
+
)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error instanceof AuthenticationError) sessionManager.endSession()
|
|
65
|
+
throw error
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected get<T>(path: string, request?: RequestOptions): Promise<T> {
|
|
70
|
+
return this.sendRequest(path, 'get', request) as Promise<T>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected post<T>(path: string, request?: RequestWithBody): Promise<T> {
|
|
74
|
+
return this.sendRequest(path, 'post', request) as Promise<T>
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected put<T>(path: string, request?: RequestWithBody): Promise<T> {
|
|
78
|
+
return this.sendRequest(path, 'put', request) as Promise<T>
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected patch<T>(path: string, request?: RequestWithBody): Promise<T> {
|
|
82
|
+
return this.sendRequest(path, 'patch', request) as Promise<T>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
protected delete<T>(path: string, request?: RequestWithBody): Promise<T> {
|
|
86
|
+
return this.sendRequest(path, 'delete', request) as Promise<T>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected isFreemium() {
|
|
90
|
+
const sessionManager = this.getSessionManager()
|
|
91
|
+
return sessionManager.hasSession() && !!sessionManager.getSession().getTokenData().freemium_status
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import { Defaults, HttpError, RequestOpts } from '@oazapfts/runtime'
|
|
4
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
|
+
import { AutoMutation } from './AutoMutation'
|
|
6
|
+
import { AutoQuery } from './AutoQuery'
|
|
7
|
+
import { ManualMutation } from './ManualMutation'
|
|
8
|
+
import { ManualQuery } from './ManualQuery'
|
|
9
|
+
import { NetworkClient } from './NetworkClient'
|
|
10
|
+
import { Env, HTTPMethod, MutationObject, OperationConfig, OperationObject, QueryObject } from './types'
|
|
11
|
+
|
|
12
|
+
const DUMMY_ULID = '01HSTZ5471CBG4AW9BFJ0VTVG9'
|
|
13
|
+
const PERMISSION_ERROR = 'permission-error'
|
|
14
|
+
|
|
15
|
+
export abstract class ReactQueryNetworkClient extends NetworkClient {
|
|
16
|
+
constructor(baseURL: Record<Env, string>, defaults: Defaults<any>) {
|
|
17
|
+
super(baseURL)
|
|
18
|
+
defaults.baseUrl = ''
|
|
19
|
+
// eslint-disable-next-line arrow-body-style
|
|
20
|
+
defaults.fetch = (...args) => {
|
|
21
|
+
return this.#onFetch(...args)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#getMappedHeaders(headers: HeadersInit | undefined) {
|
|
26
|
+
if (!headers) return undefined
|
|
27
|
+
if (headers instanceof Headers) {
|
|
28
|
+
const headersMap: Record<string, string> = {}
|
|
29
|
+
headers.forEach((value, key) => headersMap[key] = value)
|
|
30
|
+
return headersMap
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(headers)) {
|
|
33
|
+
const headersMap: Record<string, string> = {}
|
|
34
|
+
headers.forEach(([key, value]) => headersMap[key] = value)
|
|
35
|
+
return headersMap
|
|
36
|
+
}
|
|
37
|
+
return headers
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#parseErrorData(error: HttpError) {
|
|
41
|
+
if (typeof error.data === 'string') {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(error.data)
|
|
44
|
+
} catch { /* empty */ }
|
|
45
|
+
}
|
|
46
|
+
return error.data
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#transformError(error: any): StackspotAPIError {
|
|
50
|
+
if (!(error instanceof HttpError)) throw new StackspotAPIError({ status: 0, message: error?.message || `${error}`, stack: error.stack })
|
|
51
|
+
const data = this.#parseErrorData(error)
|
|
52
|
+
if (data.type === PERMISSION_ERROR) {
|
|
53
|
+
// eslint-disable-next-line no-console
|
|
54
|
+
console.error('Error while validating permissions:', data.message)
|
|
55
|
+
return new StackspotAPIError({
|
|
56
|
+
status: 500,
|
|
57
|
+
stack: error.stack,
|
|
58
|
+
message: language => language === 'pt'
|
|
59
|
+
? 'Ocorreu um erro ao validar as permissões.'
|
|
60
|
+
: 'There was an error while validating permissions.',
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
return this.buildStackSpotError(error)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#withErrorTreatment = <Args extends any[], Result>(block: (...args: Args) => Promise<Result>): ((...args: Args) => Promise<Result>) => (
|
|
67
|
+
async (...args) => {
|
|
68
|
+
try {
|
|
69
|
+
return await block(...args)
|
|
70
|
+
} catch (error: any) {
|
|
71
|
+
throw this.#transformError(error)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
#onFetch(input: string | URL | Request, init?: RequestInit | undefined): Promise<Response> {
|
|
77
|
+
const [path, method, body, headers] = input instanceof Request
|
|
78
|
+
? [input.url, input.method as HTTPMethod, input.body, input.headers]
|
|
79
|
+
: [`${input}`, (init?.method?.toLowerCase() || 'get') as HTTPMethod, init?.body, init?.headers]
|
|
80
|
+
return this[method](path, { body: body as any, headers: this.#getMappedHeaders(headers) })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected abstract buildStackSpotError(error: HttpError): StackspotAPIError
|
|
84
|
+
|
|
85
|
+
async #onFetchPermission(input: string | URL | Request, init?: RequestInit | undefined): Promise<Response> {
|
|
86
|
+
const [path, method, body] = input instanceof Request
|
|
87
|
+
? [input.url, input.method as HTTPMethod, input.body]
|
|
88
|
+
: [`${input}`, (init?.method?.toLowerCase() || 'get') as HTTPMethod, init?.body]
|
|
89
|
+
try {
|
|
90
|
+
/* We allow the dev not to pass any variable when validating a permission. For this reason, oazapfts might generate a path with
|
|
91
|
+
"undefined". If this happens, we must replace the undefined text with a dummy ULID. */
|
|
92
|
+
const fixedPath = path.replace(/\/undefined(\/|$)/g, `/${DUMMY_ULID}$1`)
|
|
93
|
+
const isAllowed = await this.requestPermission(method, fixedPath, body as any)
|
|
94
|
+
return new Response(`${isAllowed}`, { headers: { 'Content-Type': 'application/json' } })
|
|
95
|
+
} catch (error: any) {
|
|
96
|
+
return new Response(JSON.stringify({ type: PERMISSION_ERROR, message: error.message || `${error}` }), { status: 500 })
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
protected query<Args extends [opts?: RequestOpts] | [variables: Record<string, any>, opts?: RequestOpts], Result>(
|
|
101
|
+
fn: (...args: Args) => Promise<Result>
|
|
102
|
+
): Args extends [variables: infer Variables, opts?: RequestOpts] ? QueryObject<Variables, Result> : QueryObject<void, Result>
|
|
103
|
+
protected query<Args extends [AbortSignal, Record<string, any>] | [AbortSignal], Result>(
|
|
104
|
+
config: OperationConfig<Args, Result>,
|
|
105
|
+
): QueryObject<Args extends [AbortSignal] ? void : Args[1], Result>
|
|
106
|
+
protected query<Args extends [AbortSignal, Record<string, any>] | [AbortSignal], Result>(
|
|
107
|
+
config: Omit<OperationConfig<Args, Result>, 'permission'>,
|
|
108
|
+
): Omit<QueryObject<Args extends [AbortSignal] ? void : Args[1], Result>, 'isAllowed' | 'useAllowed' | 'getPermissionKey'>
|
|
109
|
+
protected query(fnOrConfig: any): QueryObject<any, any> {
|
|
110
|
+
return typeof fnOrConfig === 'function'
|
|
111
|
+
? new AutoQuery(
|
|
112
|
+
{
|
|
113
|
+
apiName: this.constructor.name,
|
|
114
|
+
fn: fnOrConfig,
|
|
115
|
+
onFetchPermission: (...args) => this.#onFetchPermission(...args),
|
|
116
|
+
transformError: error => this.#transformError(error),
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
: new ManualQuery({
|
|
120
|
+
...fnOrConfig,
|
|
121
|
+
apiName: this.constructor.name,
|
|
122
|
+
request: this.#withErrorTreatment(fnOrConfig.request),
|
|
123
|
+
permission: fnOrConfig.permission ? this.#withErrorTreatment(fnOrConfig.permission) : undefined,
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
protected mutation<Args extends [opts?: RequestOpts] | [variables: Record<string, any>, opts?: RequestOpts], Result>(
|
|
128
|
+
fn: (...args: Args) => Promise<Result>,
|
|
129
|
+
): Args extends [variables: infer Variables, opts?: RequestOpts] ? MutationObject<Variables, Result> : MutationObject<void, Result>
|
|
130
|
+
protected mutation<Args extends [AbortSignal, Record<string, any>] | [AbortSignal], Result>(
|
|
131
|
+
config: OperationConfig<Args, Result>,
|
|
132
|
+
): MutationObject<Args extends [AbortSignal] ? void : Args[1], Result>
|
|
133
|
+
protected mutation<Args extends [AbortSignal, Record<string, any>] | [AbortSignal], Result>(
|
|
134
|
+
config: Omit<OperationConfig<Args, Result>, 'permission'>,
|
|
135
|
+
): Omit<MutationObject<Args extends [AbortSignal] ? void : Args[1], Result>, keyof OperationObject<any>>
|
|
136
|
+
protected mutation(fnOrConfig: any): MutationObject<any, any> {
|
|
137
|
+
if (typeof fnOrConfig === 'function') {
|
|
138
|
+
return new AutoMutation(
|
|
139
|
+
{
|
|
140
|
+
apiName: this.constructor.name,
|
|
141
|
+
fn: fnOrConfig,
|
|
142
|
+
onFetchPermission: (...args) => this.#onFetchPermission(...args),
|
|
143
|
+
transformError: error => this.#transformError(error),
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
return new ManualMutation({
|
|
148
|
+
...fnOrConfig,
|
|
149
|
+
apiName: this.constructor.name,
|
|
150
|
+
request: this.#withErrorTreatment(fnOrConfig.request),
|
|
151
|
+
permission: fnOrConfig.permission ? this.#withErrorTreatment(fnOrConfig.permission) : undefined,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { RequestOpts } from '@oazapfts/runtime'
|
|
2
|
+
import { Session } from '@stack-spot/auth'
|
|
3
|
+
import { MutateOptions, UseMutationOptions, UseMutationResult, UseQueryOptions, UseQueryResult } from '@tanstack/react-query'
|
|
4
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
|
+
|
|
6
|
+
export interface SessionManager {
|
|
7
|
+
hasSession(): boolean,
|
|
8
|
+
endSession(): void,
|
|
9
|
+
getSession(): Session,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface RequestOptions {
|
|
13
|
+
params?: Record<string, string | undefined>,
|
|
14
|
+
headers?: Record<string, string>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RequestWithBody extends RequestOptions {
|
|
18
|
+
body?: string | object | FormData,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Env = 'dev' | 'stg' | 'prd'
|
|
22
|
+
|
|
23
|
+
export type HTTPMethod = 'post' | 'patch' | 'delete' | 'put' | 'get'
|
|
24
|
+
|
|
25
|
+
export interface OperationConfig<Args extends [AbortSignal, Record<string, any>] | [AbortSignal], Result> {
|
|
26
|
+
name: string,
|
|
27
|
+
request: (...args: Args) => Promise<Result>,
|
|
28
|
+
permission: (...args: Args extends [AbortSignal] ? [] : [Args[1]]) => Promise<boolean>,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface FullOperationConfig<
|
|
32
|
+
Args extends [AbortSignal, Record<string, any>] | [AbortSignal], Result
|
|
33
|
+
> extends OperationConfig<Args, Result> {
|
|
34
|
+
apiName: string,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface OperationObject<Variables> {
|
|
38
|
+
isAllowed: (...args: Variables extends void ? [] : [variables?: Partial<Variables>]) => Promise<boolean>,
|
|
39
|
+
useAllowed: (
|
|
40
|
+
...args: Variables extends void
|
|
41
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
42
|
+
: [variables?: Partial<Variables>, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
43
|
+
) => Readonly<[boolean | undefined, boolean, StackspotAPIError | null | undefined, UseQueryResult<boolean, StackspotAPIError>]>,
|
|
44
|
+
getPermissionKey: (...args: Variables extends void ? [] : [variables?: Partial<Variables>]) => any[],
|
|
45
|
+
cancel: (variables?: Partial<Variables>) => boolean,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface QueryObject<Variables, Result> extends OperationObject<Variables> {
|
|
49
|
+
query: (...args: Variables extends void ? [] : [variables: Variables]) => Promise<Result>,
|
|
50
|
+
useQuery: (
|
|
51
|
+
...args: Variables extends void
|
|
52
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
53
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
54
|
+
) => Result | undefined,
|
|
55
|
+
useStatefulQuery: (
|
|
56
|
+
...args: Variables extends void
|
|
57
|
+
? [options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
58
|
+
: [variables: Variables, options?: Omit<UseQueryOptions, 'queryFn' | 'queryKey'>]
|
|
59
|
+
) => Readonly<[Result | undefined, boolean, StackspotAPIError | null | undefined, UseQueryResult<Result, StackspotAPIError>]>,
|
|
60
|
+
invalidate: (...args: Variables extends void ? [] : [variables?: Partial<Variables>]) => Promise<void>,
|
|
61
|
+
getKey: (...args: Variables extends void ? [] : [variables?: Partial<Variables>]) => any[],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface AutoQueryObjectParams<Variables, Result> {
|
|
65
|
+
apiName: string,
|
|
66
|
+
fn: ((variables: Variables, opts?: RequestOpts) => Promise<Result>) | ((opts?: RequestOpts) => Promise<Result>),
|
|
67
|
+
onFetchPermission: typeof fetch,
|
|
68
|
+
transformError?: (error: any) => StackspotAPIError,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface MutationObject<Variables, Result> extends OperationObject<Variables> {
|
|
72
|
+
mutate: (...args: Variables extends void ? [] : [variables: Variables]) => Promise<Result>,
|
|
73
|
+
useMutation: (options?: Omit<UseMutationOptions<Result, StackspotAPIError, Variables>, 'mutationFn'>) =>
|
|
74
|
+
Readonly<[
|
|
75
|
+
(...args: Variables extends void
|
|
76
|
+
? [options?: MutateOptions<Result, StackspotAPIError>]
|
|
77
|
+
: [variables: Variables, options?: MutateOptions<Result, StackspotAPIError, Variables>]
|
|
78
|
+
) => Promise<Result>,
|
|
79
|
+
boolean, StackspotAPIError | undefined,
|
|
80
|
+
UseMutationResult<Result, StackspotAPIError, Variables>,
|
|
81
|
+
]>,
|
|
82
|
+
}
|