@stack-spot/portal-network 0.1.0 → 0.2.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/CHANGELOG.md +9 -0
- package/package.json +7 -6
- package/src/apis.json +2 -10
- package/src/client/account.ts +44 -2
- package/src/error/CanceledError.ts +3 -0
- package/src/error/DefaultAPIError.ts +27 -1
- package/src/error/StackspotAPIError.ts +33 -1
- package/src/network/AutoInfiniteQuery.ts +97 -0
- package/src/network/AutoQuery.ts +2 -1
- package/src/network/ManualInfiniteQuery.ts +88 -0
- package/src/network/ManualQuery.ts +2 -2
- package/src/network/NetworkClient.ts +79 -13
- package/src/network/ReactQueryNetworkClient.ts +130 -12
- package/src/network/react-query-client.ts +3 -0
- package/src/network/types.ts +208 -2
- package/.generate-api.failed.json +0 -1
- package/dist/api/account.d.ts +0 -2368
- package/dist/api/account.d.ts.map +0 -1
- package/dist/api/account.js +0 -1521
- package/dist/api/account.js.map +0 -1
- package/dist/api/ai.d.ts +0 -1432
- package/dist/api/ai.d.ts.map +0 -1
- package/dist/api/ai.js +0 -1342
- package/dist/api/ai.js.map +0 -1
- package/dist/api/apiRuntime.d.ts +0 -922
- package/dist/api/apiRuntime.d.ts.map +0 -1
- package/dist/api/apiRuntime.js +0 -599
- package/dist/api/apiRuntime.js.map +0 -1
- package/dist/api/cloudAccount.d.ts +0 -473
- package/dist/api/cloudAccount.d.ts.map +0 -1
- package/dist/api/cloudAccount.js +0 -300
- package/dist/api/cloudAccount.js.map +0 -1
- package/dist/api/cloudServices.d.ts +0 -1233
- package/dist/api/cloudServices.d.ts.map +0 -1
- package/dist/api/cloudServices.js +0 -715
- package/dist/api/cloudServices.js.map +0 -1
- package/dist/api/insights.d.ts +0 -123
- package/dist/api/insights.d.ts.map +0 -1
- package/dist/api/insights.js +0 -112
- package/dist/api/insights.js.map +0 -1
- package/dist/api/plugin.d.ts +0 -368
- package/dist/api/plugin.d.ts.map +0 -1
- package/dist/api/plugin.js +0 -218
- package/dist/api/plugin.js.map +0 -1
- package/dist/api/serviceCatalog.d.ts +0 -737
- package/dist/api/serviceCatalog.d.ts.map +0 -1
- package/dist/api/serviceCatalog.js +0 -611
- package/dist/api/serviceCatalog.js.map +0 -1
- package/dist/api/workflows.d.ts +0 -366
- package/dist/api/workflows.d.ts.map +0 -1
- package/dist/api/workflows.js +0 -175
- package/dist/api/workflows.js.map +0 -1
- package/dist/api/workspace.js +0 -1476
- package/dist/api/workspace.js.map +0 -1
- package/dist/api/workspaceManager.d.ts +0 -1121
- package/dist/api/workspaceManager.d.ts.map +0 -1
- package/dist/api/workspaceManager.js +0 -357
- package/dist/api/workspaceManager.js.map +0 -1
- package/dist/api/workspaceSearchEngine.d.ts +0 -93
- package/dist/api/workspaceSearchEngine.d.ts.map +0 -1
- package/dist/api/workspaceSearchEngine.js +0 -55
- package/dist/api/workspaceSearchEngine.js.map +0 -1
- package/dist/apis.json +0 -129
- package/dist/client/account.d.ts +0 -46
- package/dist/client/account.d.ts.map +0 -1
- package/dist/client/account.js +0 -104
- package/dist/client/account.js.map +0 -1
- package/dist/error/CanceledError.d.ts +0 -5
- package/dist/error/CanceledError.d.ts.map +0 -1
- package/dist/error/CanceledError.js +0 -7
- package/dist/error/CanceledError.js.map +0 -1
- package/dist/error/DefaultAPIError.d.ts +0 -9
- package/dist/error/DefaultAPIError.d.ts.map +0 -1
- package/dist/error/DefaultAPIError.js +0 -58
- package/dist/error/DefaultAPIError.js.map +0 -1
- package/dist/error/StackspotAPIError.d.ts +0 -19
- package/dist/error/StackspotAPIError.d.ts.map +0 -1
- package/dist/error/StackspotAPIError.js +0 -39
- package/dist/error/StackspotAPIError.js.map +0 -1
- package/dist/error/dictionary/account.d.ts +0 -55
- package/dist/error/dictionary/account.d.ts.map +0 -1
- package/dist/error/dictionary/account.js +0 -55
- package/dist/error/dictionary/account.js.map +0 -1
- package/dist/error/dictionary/action.d.ts +0 -163
- package/dist/error/dictionary/action.d.ts.map +0 -1
- package/dist/error/dictionary/action.js +0 -163
- package/dist/error/dictionary/action.js.map +0 -1
- package/dist/error/dictionary/base.d.ts +0 -21
- package/dist/error/dictionary/base.d.ts.map +0 -1
- package/dist/error/dictionary/base.js +0 -21
- package/dist/error/dictionary/base.js.map +0 -1
- package/dist/error/dictionary/cnt-fields.d.ts +0 -13
- package/dist/error/dictionary/cnt-fields.d.ts.map +0 -1
- package/dist/error/dictionary/cnt-fields.js +0 -13
- package/dist/error/dictionary/cnt-fields.js.map +0 -1
- package/dist/error/dictionary/cnt.d.ts +0 -79
- package/dist/error/dictionary/cnt.d.ts.map +0 -1
- package/dist/error/dictionary/cnt.js +0 -79
- package/dist/error/dictionary/cnt.js.map +0 -1
- package/dist/error/dictionary/rte.d.ts +0 -23
- package/dist/error/dictionary/rte.d.ts.map +0 -1
- package/dist/error/dictionary/rte.js +0 -23
- package/dist/error/dictionary/rte.js.map +0 -1
- package/dist/error/dictionary/rtm.d.ts +0 -9
- package/dist/error/dictionary/rtm.d.ts.map +0 -1
- package/dist/error/dictionary/rtm.js +0 -9
- package/dist/error/dictionary/rtm.js.map +0 -1
- package/dist/error/dictionary/workspace-fields.d.ts +0 -9
- package/dist/error/dictionary/workspace-fields.d.ts.map +0 -1
- package/dist/error/dictionary/workspace-fields.js +0 -9
- package/dist/error/dictionary/workspace-fields.js.map +0 -1
- package/dist/error/dictionary/workspace.d.ts +0 -99
- package/dist/error/dictionary/workspace.d.ts.map +0 -1
- package/dist/error/dictionary/workspace.js +0 -99
- package/dist/error/dictionary/workspace.js.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +0 -1
- package/dist/network/AutoMutation.d.ts +0 -10
- package/dist/network/AutoMutation.d.ts.map +0 -1
- package/dist/network/AutoMutation.js +0 -20
- package/dist/network/AutoMutation.js.map +0 -1
- package/dist/network/AutoOperation.d.ts +0 -19
- package/dist/network/AutoOperation.d.ts.map +0 -1
- package/dist/network/AutoOperation.js +0 -99
- package/dist/network/AutoOperation.js.map +0 -1
- package/dist/network/AutoQuery.d.ts +0 -19
- package/dist/network/AutoQuery.d.ts.map +0 -1
- package/dist/network/AutoQuery.js +0 -70
- package/dist/network/AutoQuery.js.map +0 -1
- package/dist/network/ManualMutation.d.ts +0 -11
- package/dist/network/ManualMutation.d.ts.map +0 -1
- package/dist/network/ManualMutation.js +0 -32
- package/dist/network/ManualMutation.js.map +0 -1
- package/dist/network/ManualOperation.d.ts +0 -13
- package/dist/network/ManualOperation.d.ts.map +0 -1
- package/dist/network/ManualOperation.js +0 -53
- package/dist/network/ManualOperation.js.map +0 -1
- package/dist/network/ManualQuery.d.ts +0 -20
- package/dist/network/ManualQuery.d.ts.map +0 -1
- package/dist/network/ManualQuery.js +0 -77
- package/dist/network/ManualQuery.js.map +0 -1
- package/dist/network/NetworkClient.d.ts +0 -20
- package/dist/network/NetworkClient.d.ts.map +0 -1
- package/dist/network/NetworkClient.js +0 -85
- package/dist/network/NetworkClient.js.map +0 -1
- package/dist/network/ReactQueryNetworkClient.d.ts +0 -16
- package/dist/network/ReactQueryNetworkClient.d.ts.map +0 -1
- package/dist/network/ReactQueryNetworkClient.js +0 -125
- package/dist/network/ReactQueryNetworkClient.js.map +0 -1
- package/dist/network/react-query-client.d.ts +0 -3
- package/dist/network/react-query-client.d.ts.map +0 -1
- package/dist/network/react-query-client.js +0 -3
- package/dist/network/react-query-client.js.map +0 -1
- package/dist/network/types.d.ts +0 -55
- package/dist/network/types.d.ts.map +0 -1
- package/dist/network/types.js +0 -2
- package/dist/network/types.js.map +0 -1
- package/src/api/plugin.ts +0 -685
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.0](https://github.com/stack-spot/portal-commons/compare/portal-network-v0.1.0...portal-network@v0.2.0) (2024-07-31)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* network library ([eec8b1b](https://github.com/stack-spot/portal-commons/commit/eec8b1b24c77d055884b8ed8c2ccac452c5b16e2))
|
|
9
|
+
* network: infinite query ([#214](https://github.com/stack-spot/portal-commons/issues/214)) ([aded3f7](https://github.com/stack-spot/portal-commons/commit/aded3f78665a829fd31d78766be7d5942aaa62e3))
|
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stack-spot/portal-network",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./dist/index.js",
|
|
9
|
+
"./package.json": "./package.json",
|
|
9
10
|
"./api/*": "./dist/api/*.js"
|
|
10
11
|
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "rimraf dist && tsc-silent -p tsconfig.build.json --suppress @/src/api/ && tsc-esm-fix --target='dist'",
|
|
14
|
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
15
|
+
"generate": "tsx ./scripts/generate-apis.ts"
|
|
16
|
+
},
|
|
11
17
|
"peerDependencies": {
|
|
12
18
|
"@stack-spot/auth": "^5.2.0",
|
|
13
19
|
"@stack-spot/opa": "^2.2.0",
|
|
@@ -47,10 +53,5 @@
|
|
|
47
53
|
"dependencies": {
|
|
48
54
|
"@oazapfts/runtime": "^1.0.3",
|
|
49
55
|
"lodash": "^4.17.21"
|
|
50
|
-
},
|
|
51
|
-
"scripts": {
|
|
52
|
-
"build": "rimraf dist && tsc-silent -p tsconfig.build.json --suppress @/src/api/ && tsc-esm-fix --target='dist'",
|
|
53
|
-
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
54
|
-
"generate": "tsx ./scripts/generate-apis.ts"
|
|
55
56
|
}
|
|
56
57
|
}
|
package/src/apis.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"ai": {
|
|
3
3
|
"url": {
|
|
4
|
-
"dev": "https://code-buddy-api.dev.stackspot.com",
|
|
5
|
-
"stg": "https://code-buddy-api.stg.stackspot.com",
|
|
4
|
+
"dev": "https://genai-code-buddy-api.dev.stackspot.com",
|
|
5
|
+
"stg": "https://genai-code-buddy-api.stg.stackspot.com",
|
|
6
6
|
"prd": "https://genai-code-buddy-api.stackspot.com"
|
|
7
7
|
},
|
|
8
8
|
"docs": "/openapi.json"
|
|
@@ -79,14 +79,6 @@
|
|
|
79
79
|
},
|
|
80
80
|
"docs": "/v3/api-docs"
|
|
81
81
|
},
|
|
82
|
-
"plugin": {
|
|
83
|
-
"url": {
|
|
84
|
-
"dev": "https://content-plugin-api.dev.stackspot.com",
|
|
85
|
-
"stg": "https://content-plugin-api.stg.stackspot.com",
|
|
86
|
-
"prd": "https://plugin-api.v1.stackspot.com"
|
|
87
|
-
},
|
|
88
|
-
"docs": "/openapi.json"
|
|
89
|
-
},
|
|
90
82
|
"workflows": {
|
|
91
83
|
"url": {
|
|
92
84
|
"dev": "https://workflow-workflow-api.dev.stackspot.com",
|
package/src/client/account.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { HttpError } from '@oazapfts/runtime'
|
|
|
2
2
|
import {
|
|
3
3
|
accountDataIsAvailable, create, createPartner,
|
|
4
4
|
defaults,
|
|
5
|
-
deletePartner,
|
|
5
|
+
deletePartner, getAccountMembers1, getFeatures,
|
|
6
|
+
getPartnerAccount, getPartnersSharingAllowed,
|
|
6
7
|
getPersonalClientCredentials, updatePartnerAccountAdminData, updatePartnerAccountData, validateNewPartnerData,
|
|
7
8
|
validatePartnerAssociationLimit,
|
|
8
9
|
} from '../api/account'
|
|
@@ -21,17 +22,54 @@ class AccountClient extends ReactQueryNetworkClient {
|
|
|
21
22
|
return new DefaultAPIError(error.data, error.status, accountDictionary, error.headers)
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Get credentials from personal service client (create if not exists one to the logged user).
|
|
27
|
+
*/
|
|
24
28
|
generatePersonalClientCredentials = this.mutation(getPersonalClientCredentials)
|
|
25
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Create a Feature Flag
|
|
31
|
+
*/
|
|
32
|
+
createFeatureFlag = this.mutation(create)
|
|
33
|
+
/**
|
|
34
|
+
* Returns information if account data (name and slug) are available to be registered
|
|
35
|
+
*/
|
|
26
36
|
isAvailable = this.query(accountDataIsAvailable)
|
|
37
|
+
/**
|
|
38
|
+
* List Feature Flags in an Account
|
|
39
|
+
*/
|
|
27
40
|
featureFlags = this.query(getFeatures)
|
|
41
|
+
/**
|
|
42
|
+
* Get Partners with whom it is allowed to share content.
|
|
43
|
+
*/
|
|
28
44
|
partners = this.query(getPartnersSharingAllowed)
|
|
45
|
+
/**
|
|
46
|
+
* Get Partner by account Id
|
|
47
|
+
*/
|
|
29
48
|
partner = this.query(getPartnerAccount)
|
|
49
|
+
/**
|
|
50
|
+
* Create an Account Order for Partner
|
|
51
|
+
*/
|
|
30
52
|
createPartner = this.mutation(createPartner)
|
|
53
|
+
/**
|
|
54
|
+
* Update Partner Account data.
|
|
55
|
+
*/
|
|
31
56
|
updatePartner = this.mutation(updatePartnerAccountData)
|
|
57
|
+
/**
|
|
58
|
+
* Update Partner Account Admin data.
|
|
59
|
+
*/
|
|
32
60
|
updatePartnerAdmin = this.mutation(updatePartnerAccountAdminData)
|
|
61
|
+
/**
|
|
62
|
+
* Delete Partner
|
|
63
|
+
*/
|
|
33
64
|
deactivatePartner = this.mutation(deletePartner)
|
|
65
|
+
/**
|
|
66
|
+
* Validate new Partner account data
|
|
67
|
+
*/
|
|
34
68
|
validateNewPartnerData = this.mutation(validateNewPartnerData)
|
|
69
|
+
/**
|
|
70
|
+
* Validates the association limit for the partner. Yields `{ isValid: true }` if valid or `{ isValid: false, message: string }`
|
|
71
|
+
* otherwise.
|
|
72
|
+
*/
|
|
35
73
|
validatePartnerAssociationLimit = this.query({
|
|
36
74
|
name: 'validatePartnerAssociationLimit',
|
|
37
75
|
request: async (signal) => {
|
|
@@ -47,6 +85,10 @@ class AccountClient extends ReactQueryNetworkClient {
|
|
|
47
85
|
}
|
|
48
86
|
},
|
|
49
87
|
})
|
|
88
|
+
/**
|
|
89
|
+
* Get all members (paginated).
|
|
90
|
+
*/
|
|
91
|
+
allMembers = this.infiniteQuery(getAccountMembers1)
|
|
50
92
|
}
|
|
51
93
|
|
|
52
94
|
export const accountClient = new AccountClient()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { StackspotAPIError } from './StackspotAPIError'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* This error is thrown when a request is canceled by the user before it completes.
|
|
5
|
+
*/
|
|
3
6
|
export class CanceledError extends StackspotAPIError {
|
|
4
7
|
constructor() {
|
|
5
8
|
super({ status: 0, message: lang => lang === 'en' ? 'Canceled by the user' : 'Cancelado pelo usuário' })
|
|
@@ -34,11 +34,37 @@ function createMessage(raw: ErrorResponse, dictionary: Dictionary, language: Lan
|
|
|
34
34
|
return `${title}\n${details?.join('\n')}`
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* This represents the Error thrown by most Stackspot APIs.
|
|
39
|
+
*/
|
|
37
40
|
export class DefaultAPIError extends StackspotAPIError {
|
|
41
|
+
/**
|
|
42
|
+
* The error object as defined by the Stackspot API. If the response doesn't match the expected format this will be undefined.
|
|
43
|
+
*/
|
|
38
44
|
data?: ErrorResponse
|
|
45
|
+
/**
|
|
46
|
+
* The response data, if this matches the expected format expected by a Stackspot Error, it will be the same as `data`.
|
|
47
|
+
*/
|
|
39
48
|
raw?: any
|
|
40
49
|
|
|
41
|
-
constructor(
|
|
50
|
+
constructor(
|
|
51
|
+
/**
|
|
52
|
+
* The response's data
|
|
53
|
+
*/
|
|
54
|
+
rawResponse: any,
|
|
55
|
+
/**
|
|
56
|
+
* The response's status
|
|
57
|
+
*/
|
|
58
|
+
status: number,
|
|
59
|
+
/**
|
|
60
|
+
* A dictionary for translating error codes and descriptions.
|
|
61
|
+
*/
|
|
62
|
+
dictionary: Dictionary,
|
|
63
|
+
/**
|
|
64
|
+
* The response's headers.
|
|
65
|
+
*/
|
|
66
|
+
headers?: Headers,
|
|
67
|
+
) {
|
|
42
68
|
super({
|
|
43
69
|
status: status,
|
|
44
70
|
code: rawResponse.code,
|
|
@@ -3,16 +3,42 @@ import { Language, getLanguage } from '@stack-spot/portal-translate'
|
|
|
3
3
|
type InternationalizedMessage = (language: Language) => string
|
|
4
4
|
|
|
5
5
|
interface ErrorProperties {
|
|
6
|
+
/**
|
|
7
|
+
* The response's status.
|
|
8
|
+
*/
|
|
6
9
|
status: number,
|
|
10
|
+
/**
|
|
11
|
+
* The response's headers.
|
|
12
|
+
*/
|
|
7
13
|
headers?: Headers,
|
|
14
|
+
/**
|
|
15
|
+
* The error code defined by the API, if any.
|
|
16
|
+
*/
|
|
8
17
|
code?: string,
|
|
18
|
+
/**
|
|
19
|
+
* The error message: may be a simple string or a function that returns a string depending on the language passed as parameter.
|
|
20
|
+
*
|
|
21
|
+
* If not provided, will attempt to use the error code or 'unknown'.
|
|
22
|
+
*/
|
|
9
23
|
message?: InternationalizedMessage | string,
|
|
24
|
+
/**
|
|
25
|
+
* The error's stack trace, if any.
|
|
26
|
+
*/
|
|
10
27
|
stack?: string,
|
|
11
28
|
}
|
|
12
29
|
|
|
13
30
|
export class StackspotAPIError extends Error {
|
|
31
|
+
/**
|
|
32
|
+
* The response's status.
|
|
33
|
+
*/
|
|
14
34
|
status: number
|
|
35
|
+
/**
|
|
36
|
+
* The response's headers.
|
|
37
|
+
*/
|
|
15
38
|
headers: Headers | undefined
|
|
39
|
+
/**
|
|
40
|
+
* The error code defined by the API, if any.
|
|
41
|
+
*/
|
|
16
42
|
code: string | undefined
|
|
17
43
|
private intl?: InternationalizedMessage
|
|
18
44
|
|
|
@@ -25,7 +51,13 @@ export class StackspotAPIError extends Error {
|
|
|
25
51
|
this.intl = typeof message === 'string' ? () => message : message
|
|
26
52
|
}
|
|
27
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Translates the error to the given language.
|
|
56
|
+
* @param language
|
|
57
|
+
* @returns an error message
|
|
58
|
+
*/
|
|
28
59
|
translate(language: Language = getLanguage()) {
|
|
29
|
-
|
|
60
|
+
const unknown = language === 'en' ? 'unknown error' : 'erro desconhecido'
|
|
61
|
+
return this.intl?.(language) ?? this.message ?? this.code ?? this.status === 0 ? unknown : `${this.status}`
|
|
30
62
|
}
|
|
31
63
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
InfiniteData, QueryFunctionContext, QueryKey,
|
|
5
|
+
UseInfiniteQueryResult,
|
|
6
|
+
useInfiniteQuery,
|
|
7
|
+
useSuspenseInfiniteQuery,
|
|
8
|
+
} from '@tanstack/react-query'
|
|
9
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
10
|
+
import { AutoQuery } from './AutoQuery'
|
|
11
|
+
import { queryClient } from './react-query-client'
|
|
12
|
+
import { AutoQueryObjectParams, InfiniteQueryObject, InfiniteQueryOptions, UseInfiniteQueryObjectOptions } from './types'
|
|
13
|
+
|
|
14
|
+
export class AutoInfiniteQuery<Variables, Result, PageParamName extends keyof Variables, Accumulator extends keyof Result | ''>
|
|
15
|
+
extends AutoQuery<Variables, Result>
|
|
16
|
+
implements InfiniteQueryObject<Variables, Result, Accumulator>
|
|
17
|
+
{
|
|
18
|
+
private options: Required<InfiniteQueryOptions<Variables, Result, PageParamName, Accumulator | ''>>
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
params: AutoQueryObjectParams<Variables, Result>,
|
|
22
|
+
options: InfiniteQueryOptions<Variables, Result, PageParamName, Accumulator>,
|
|
23
|
+
) {
|
|
24
|
+
super(params)
|
|
25
|
+
this.options = {
|
|
26
|
+
...options,
|
|
27
|
+
accumulator: options.accumulator ?? '',
|
|
28
|
+
defaultVariables: options.defaultVariables ?? {},
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private createInfiniteQueryFn(
|
|
33
|
+
variables: Variables | undefined,
|
|
34
|
+
): (context: QueryFunctionContext<QueryKey, Variables[PageParamName]>) => Promise<Result> {
|
|
35
|
+
return async ({ pageParam }) => {
|
|
36
|
+
const paginatedVariables = {
|
|
37
|
+
...variables,
|
|
38
|
+
[this.options.pageParamName]: pageParam ?? variables?.[this.options.pageParamName],
|
|
39
|
+
} as Variables
|
|
40
|
+
if (!this.currentRequests.has(paginatedVariables)) this.currentRequests.set(paginatedVariables, this.callFn(paginatedVariables))
|
|
41
|
+
const result = await this.currentRequests.get(paginatedVariables)
|
|
42
|
+
this.currentRequests.delete(paginatedVariables)
|
|
43
|
+
return result!
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private getListFromData(data: InfiniteData<Result> | undefined) {
|
|
48
|
+
return data?.pages.map(
|
|
49
|
+
page => this.options.accumulator ? page[this.options.accumulator as keyof Result] : page,
|
|
50
|
+
).flat() as Accumulator extends keyof Result ? Result[Accumulator] : Result
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private useInfiniteQueryResult(suspense: boolean, variables = {} as Variables, options?: UseInfiniteQueryObjectOptions) {
|
|
54
|
+
variables = { ...variables, ...this.options.defaultVariables }
|
|
55
|
+
const use = suspense ? useSuspenseInfiniteQuery : useInfiniteQuery
|
|
56
|
+
return use<any, any, any, any, any>({
|
|
57
|
+
...options,
|
|
58
|
+
queryKey: ['infinite', ...this.getKey(variables)],
|
|
59
|
+
queryFn: this.createInfiniteQueryFn(variables),
|
|
60
|
+
initialPageParam: this.options.initialPageParam,
|
|
61
|
+
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
|
|
62
|
+
this.options.getNextPageParam({ variables, lastPage, allPages, lastPageParam, allPageParams }),
|
|
63
|
+
}, queryClient) as UseInfiniteQueryResult<InfiniteData<Result>, StackspotAPIError>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
useInfiniteQuery(
|
|
67
|
+
variables?: Variables,
|
|
68
|
+
options?: UseInfiniteQueryObjectOptions,
|
|
69
|
+
): [
|
|
70
|
+
Accumulator extends keyof Result ? Result[Accumulator] : Result,
|
|
71
|
+
UseInfiniteQueryResult<InfiniteData<Result>, StackspotAPIError>,
|
|
72
|
+
] {
|
|
73
|
+
const result = this.useInfiniteQueryResult(true, variables, options)
|
|
74
|
+
return [
|
|
75
|
+
this.getListFromData(result.data),
|
|
76
|
+
result,
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
useStatefulInfiniteQuery(
|
|
81
|
+
variables?: Variables,
|
|
82
|
+
options?: UseInfiniteQueryObjectOptions,
|
|
83
|
+
): [
|
|
84
|
+
Accumulator extends keyof Result ? Result[Accumulator] : Result,
|
|
85
|
+
boolean,
|
|
86
|
+
StackspotAPIError | undefined | null,
|
|
87
|
+
UseInfiniteQueryResult<InfiniteData<Result>, StackspotAPIError>,
|
|
88
|
+
] {
|
|
89
|
+
const result = this.useInfiniteQueryResult(false, variables, options)
|
|
90
|
+
return [
|
|
91
|
+
this.getListFromData(result.data),
|
|
92
|
+
result.isPending,
|
|
93
|
+
result.error,
|
|
94
|
+
result,
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/network/AutoQuery.ts
CHANGED
|
@@ -10,7 +10,8 @@ export class AutoQuery<Variables, Result> extends AutoOperation<Variables> imple
|
|
|
10
10
|
/**
|
|
11
11
|
* Prevents the same request from being triggered more than once if subsequent calls are made before the first ends.
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
protected currentRequests = new Map<Variables | undefined, Promise<Result>>()
|
|
14
|
+
|
|
14
15
|
constructor(params: AutoQueryObjectParams<Variables, Result>) {
|
|
15
16
|
super(params)
|
|
16
17
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import { InfiniteData, QueryFunctionContext, QueryKey, UseInfiniteQueryResult, useInfiniteQuery, useSuspenseInfiniteQuery } from '@tanstack/react-query'
|
|
4
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
|
+
import { ManualQuery } from './ManualQuery'
|
|
6
|
+
import { queryClient } from './react-query-client'
|
|
7
|
+
import { FullOperationConfig, InfiniteQueryConfig, InfiniteQueryObject, UseInfiniteQueryObjectOptions } from './types'
|
|
8
|
+
|
|
9
|
+
export class ManualInfiniteQuery<
|
|
10
|
+
Variables extends Record<string, any>,
|
|
11
|
+
Result,
|
|
12
|
+
PageParamName extends keyof Variables,
|
|
13
|
+
Accumulator extends keyof Result | ''
|
|
14
|
+
> extends ManualQuery<Variables, Result> implements InfiniteQueryObject<Variables, Result, Accumulator> {
|
|
15
|
+
constructor(config: InfiniteQueryConfig<Variables, Result, PageParamName, Accumulator> & { apiName: string }) {
|
|
16
|
+
super(config as FullOperationConfig<any, any>)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private getConfig() {
|
|
20
|
+
return this.config as unknown as InfiniteQueryConfig<Variables, Result, PageParamName, Accumulator>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private createInfiniteQueryFn(
|
|
24
|
+
variables: Variables,
|
|
25
|
+
): (context: QueryFunctionContext<QueryKey, Variables[PageParamName]>) => Promise<Result> {
|
|
26
|
+
return async ({ pageParam }) => {
|
|
27
|
+
const paginatedVariables = {
|
|
28
|
+
...variables,
|
|
29
|
+
[this.getConfig().pageParamName]: pageParam ?? variables?.[this.getConfig().pageParamName],
|
|
30
|
+
} as Variables
|
|
31
|
+
if (!this.currentRequests.has(paginatedVariables)) this.currentRequests.set(paginatedVariables, this.makeRequest(paginatedVariables))
|
|
32
|
+
const result = await this.currentRequests.get(paginatedVariables)
|
|
33
|
+
this.currentRequests.delete(paginatedVariables)
|
|
34
|
+
return result!
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private getListFromData(data: InfiniteData<Result> | undefined) {
|
|
39
|
+
return data?.pages.map(
|
|
40
|
+
page => this.getConfig().accumulator ? page[this.getConfig().accumulator as keyof Result] : page,
|
|
41
|
+
).flat() as Accumulator extends keyof Result ? Result[Accumulator] : Result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private useInfiniteQueryResult(suspense: boolean, variables = {} as Variables, options?: UseInfiniteQueryObjectOptions) {
|
|
45
|
+
variables = { ...variables, ...this.getConfig().defaultVariables }
|
|
46
|
+
const use = suspense ? useSuspenseInfiniteQuery : useInfiniteQuery
|
|
47
|
+
return use<any, any, any, any, any>({
|
|
48
|
+
...options,
|
|
49
|
+
queryKey: ['infinite', ...this.getKey(variables)],
|
|
50
|
+
queryFn: this.createInfiniteQueryFn(variables),
|
|
51
|
+
initialPageParam: this.getConfig().initialPageParam,
|
|
52
|
+
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
|
|
53
|
+
this.getConfig().getNextPageParam({ variables, lastPage, allPages, lastPageParam, allPageParams }),
|
|
54
|
+
}, queryClient) as UseInfiniteQueryResult<InfiniteData<Result>, StackspotAPIError>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
useInfiniteQuery(
|
|
58
|
+
variables?: Variables,
|
|
59
|
+
options?: UseInfiniteQueryObjectOptions,
|
|
60
|
+
): [
|
|
61
|
+
Accumulator extends keyof Result ? Result[Accumulator] : Result,
|
|
62
|
+
UseInfiniteQueryResult<InfiniteData<Result>, StackspotAPIError>,
|
|
63
|
+
] {
|
|
64
|
+
const result = this.useInfiniteQueryResult(true, variables, options)
|
|
65
|
+
return [
|
|
66
|
+
this.getListFromData(result.data),
|
|
67
|
+
result,
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
useStatefulInfiniteQuery(
|
|
72
|
+
variables?: Variables,
|
|
73
|
+
options?: UseInfiniteQueryObjectOptions,
|
|
74
|
+
): [
|
|
75
|
+
Accumulator extends keyof Result ? Result[Accumulator] : Result,
|
|
76
|
+
boolean,
|
|
77
|
+
StackspotAPIError | undefined | null,
|
|
78
|
+
UseInfiniteQueryResult<InfiniteData<Result>, StackspotAPIError>,
|
|
79
|
+
] {
|
|
80
|
+
const result = this.useInfiniteQueryResult(false, variables, options)
|
|
81
|
+
return [
|
|
82
|
+
this.getListFromData(result.data),
|
|
83
|
+
result.isPending,
|
|
84
|
+
result.error,
|
|
85
|
+
result,
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -13,13 +13,13 @@ export class ManualQuery<
|
|
|
13
13
|
/**
|
|
14
14
|
* Prevents the same request from being triggered more than once if subsequent calls are made before the first ends.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
protected currentRequests = new Map<Record<string, any> | undefined, Promise<Result>>()
|
|
17
17
|
|
|
18
18
|
constructor(config: FullOperationConfig<Variables extends void ? [AbortSignal] : [AbortSignal, Variables], Result>) {
|
|
19
19
|
super(config)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
protected async makeRequest(variables: Record<string, any> | undefined) {
|
|
23
23
|
const abortController = new AbortController()
|
|
24
24
|
this.abortMap.set(variables, [abortController])
|
|
25
25
|
try {
|
|
@@ -3,15 +3,35 @@ import { AuthenticationError } from '@stack-spot/auth'
|
|
|
3
3
|
import { requestPermission } from '@stack-spot/opa'
|
|
4
4
|
import { Env, HTTPMethod, RequestOptions, RequestWithBody, SessionManager } from './types'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* A set of methods for performing network requests to an API.
|
|
8
|
+
*
|
|
9
|
+
* The requests are authenticated unless there's no session available.
|
|
10
|
+
*
|
|
11
|
+
* In order for a network client to work properly, the general setup for the class `NetworkClient` must be made before any request is
|
|
12
|
+
* attempted:
|
|
13
|
+
*
|
|
14
|
+
* ```
|
|
15
|
+
* NetworkClient.setup(mySessionManager, currentEnv)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export abstract class NetworkClient {
|
|
7
19
|
private baseURL: Record<Env, string>
|
|
8
20
|
private static sessionManager?: SessionManager
|
|
9
21
|
private static env?: Env
|
|
10
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @param baseURL An object with the keys "dev", "stg" and "prd". The values must be the url for each of these environments.
|
|
25
|
+
*/
|
|
11
26
|
constructor(baseURL: Record<Env, string>) {
|
|
12
27
|
this.baseURL = baseURL
|
|
13
28
|
}
|
|
14
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Sets up all network clients. Must be called before attempting to make any request.
|
|
32
|
+
* @param sessionManager An object with functions capable of checking, retrieving and ending the current session.
|
|
33
|
+
* @param env The environment to send the requests to.
|
|
34
|
+
*/
|
|
15
35
|
static setup(sessionManager: SessionManager, env: Env) {
|
|
16
36
|
NetworkClient.sessionManager = sessionManager
|
|
17
37
|
NetworkClient.env = env
|
|
@@ -21,6 +41,11 @@ export class NetworkClient {
|
|
|
21
41
|
return new Error('Please, call "NetworkClient.setup(sessionManager, env)" before attempting to make a request.')
|
|
22
42
|
}
|
|
23
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Builds a URL with the `baseUrl` of this network client and the `path` passed as parameter.
|
|
46
|
+
* @param path the path to the resource.
|
|
47
|
+
* @returns a full URL.
|
|
48
|
+
*/
|
|
24
49
|
protected resolveURL(path: string) {
|
|
25
50
|
if (!NetworkClient.env) throw this.uninitializedError()
|
|
26
51
|
// paths must not start with "/", otherwise, the base url will not be fully appended to it.
|
|
@@ -30,6 +55,13 @@ export class NetworkClient {
|
|
|
30
55
|
return new URL(fixedPath, fixedBaseUrl)
|
|
31
56
|
}
|
|
32
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Verifies if the current user is allowed to send the given request.
|
|
60
|
+
* @param method the request's method.
|
|
61
|
+
* @param path the path to the resource.
|
|
62
|
+
* @param body the request's body.
|
|
63
|
+
* @returns a promise that resolves to true if it's allowed or false otherwise.
|
|
64
|
+
*/
|
|
33
65
|
protected requestPermission(method: HTTPMethod, path: string, body?: string | object): Promise<boolean> {
|
|
34
66
|
return requestPermission(method, this.resolveURL(path).toString(), body)
|
|
35
67
|
}
|
|
@@ -57,8 +89,8 @@ export class NetworkClient {
|
|
|
57
89
|
const headers = mergeHeaders(defaultHeaders, request?.headers)
|
|
58
90
|
return (
|
|
59
91
|
sessionManager.hasSession()
|
|
60
|
-
? sessionManager.getSession().fetch(url, { method: method.toUpperCase(), headers, body })
|
|
61
|
-
: fetch(url, { method: method.toUpperCase(), headers, body })
|
|
92
|
+
? sessionManager.getSession().fetch(url, { method: method.toUpperCase(), headers, body, signal: request?.signal })
|
|
93
|
+
: fetch(url, { method: method.toUpperCase(), headers, body, signal: request?.signal })
|
|
62
94
|
)
|
|
63
95
|
} catch (error) {
|
|
64
96
|
if (error instanceof AuthenticationError) sessionManager.endSession()
|
|
@@ -66,26 +98,60 @@ export class NetworkClient {
|
|
|
66
98
|
}
|
|
67
99
|
}
|
|
68
100
|
|
|
69
|
-
|
|
70
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Performs a GET request with the parameters provided.
|
|
103
|
+
* @param path the path to the resource.
|
|
104
|
+
* @param request the request options.
|
|
105
|
+
* @returns a promise that resolves to response's data.
|
|
106
|
+
*/
|
|
107
|
+
protected get(path: string, request?: RequestOptions) {
|
|
108
|
+
return this.sendRequest(path, 'get', request)
|
|
71
109
|
}
|
|
72
110
|
|
|
73
|
-
|
|
74
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Performs a POST request with the parameters provided.
|
|
113
|
+
* @param path the path to the resource.
|
|
114
|
+
* @param request the request options.
|
|
115
|
+
* @returns a promise that resolves to response's data.
|
|
116
|
+
*/
|
|
117
|
+
protected post(path: string, request?: RequestWithBody) {
|
|
118
|
+
return this.sendRequest(path, 'post', request)
|
|
75
119
|
}
|
|
76
120
|
|
|
77
|
-
|
|
78
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Performs a PUT request with the parameters provided.
|
|
123
|
+
* @param path the path to the resource.
|
|
124
|
+
* @param request the request options.
|
|
125
|
+
* @returns a promise that resolves to response's data.
|
|
126
|
+
*/
|
|
127
|
+
protected put(path: string, request?: RequestWithBody) {
|
|
128
|
+
return this.sendRequest(path, 'put', request)
|
|
79
129
|
}
|
|
80
130
|
|
|
81
|
-
|
|
82
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Performs a PATCH request with the parameters provided.
|
|
133
|
+
* @param path the path to the resource.
|
|
134
|
+
* @param request the request options.
|
|
135
|
+
* @returns a promise that resolves to response's data.
|
|
136
|
+
*/
|
|
137
|
+
protected patch(path: string, request?: RequestWithBody) {
|
|
138
|
+
return this.sendRequest(path, 'patch', request)
|
|
83
139
|
}
|
|
84
140
|
|
|
85
|
-
|
|
86
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Performs a DELETE request with the parameters provided.
|
|
143
|
+
* @param path the path to the resource.
|
|
144
|
+
* @param request the request options.
|
|
145
|
+
* @returns a promise that resolves to response's data.
|
|
146
|
+
*/
|
|
147
|
+
protected delete(path: string, request?: RequestWithBody) {
|
|
148
|
+
return this.sendRequest(path, 'delete', request)
|
|
87
149
|
}
|
|
88
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Checks whether or not the current account is freemium.
|
|
153
|
+
* @returns true if it's a freemium account, false otherwise.
|
|
154
|
+
*/
|
|
89
155
|
protected isFreemium() {
|
|
90
156
|
const sessionManager = this.getSessionManager()
|
|
91
157
|
return sessionManager.hasSession() && !!sessionManager.getSession().getTokenData().freemium_status
|