@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.
Files changed (160) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/package.json +7 -6
  3. package/src/apis.json +2 -10
  4. package/src/client/account.ts +44 -2
  5. package/src/error/CanceledError.ts +3 -0
  6. package/src/error/DefaultAPIError.ts +27 -1
  7. package/src/error/StackspotAPIError.ts +33 -1
  8. package/src/network/AutoInfiniteQuery.ts +97 -0
  9. package/src/network/AutoQuery.ts +2 -1
  10. package/src/network/ManualInfiniteQuery.ts +88 -0
  11. package/src/network/ManualQuery.ts +2 -2
  12. package/src/network/NetworkClient.ts +79 -13
  13. package/src/network/ReactQueryNetworkClient.ts +130 -12
  14. package/src/network/react-query-client.ts +3 -0
  15. package/src/network/types.ts +208 -2
  16. package/.generate-api.failed.json +0 -1
  17. package/dist/api/account.d.ts +0 -2368
  18. package/dist/api/account.d.ts.map +0 -1
  19. package/dist/api/account.js +0 -1521
  20. package/dist/api/account.js.map +0 -1
  21. package/dist/api/ai.d.ts +0 -1432
  22. package/dist/api/ai.d.ts.map +0 -1
  23. package/dist/api/ai.js +0 -1342
  24. package/dist/api/ai.js.map +0 -1
  25. package/dist/api/apiRuntime.d.ts +0 -922
  26. package/dist/api/apiRuntime.d.ts.map +0 -1
  27. package/dist/api/apiRuntime.js +0 -599
  28. package/dist/api/apiRuntime.js.map +0 -1
  29. package/dist/api/cloudAccount.d.ts +0 -473
  30. package/dist/api/cloudAccount.d.ts.map +0 -1
  31. package/dist/api/cloudAccount.js +0 -300
  32. package/dist/api/cloudAccount.js.map +0 -1
  33. package/dist/api/cloudServices.d.ts +0 -1233
  34. package/dist/api/cloudServices.d.ts.map +0 -1
  35. package/dist/api/cloudServices.js +0 -715
  36. package/dist/api/cloudServices.js.map +0 -1
  37. package/dist/api/insights.d.ts +0 -123
  38. package/dist/api/insights.d.ts.map +0 -1
  39. package/dist/api/insights.js +0 -112
  40. package/dist/api/insights.js.map +0 -1
  41. package/dist/api/plugin.d.ts +0 -368
  42. package/dist/api/plugin.d.ts.map +0 -1
  43. package/dist/api/plugin.js +0 -218
  44. package/dist/api/plugin.js.map +0 -1
  45. package/dist/api/serviceCatalog.d.ts +0 -737
  46. package/dist/api/serviceCatalog.d.ts.map +0 -1
  47. package/dist/api/serviceCatalog.js +0 -611
  48. package/dist/api/serviceCatalog.js.map +0 -1
  49. package/dist/api/workflows.d.ts +0 -366
  50. package/dist/api/workflows.d.ts.map +0 -1
  51. package/dist/api/workflows.js +0 -175
  52. package/dist/api/workflows.js.map +0 -1
  53. package/dist/api/workspace.js +0 -1476
  54. package/dist/api/workspace.js.map +0 -1
  55. package/dist/api/workspaceManager.d.ts +0 -1121
  56. package/dist/api/workspaceManager.d.ts.map +0 -1
  57. package/dist/api/workspaceManager.js +0 -357
  58. package/dist/api/workspaceManager.js.map +0 -1
  59. package/dist/api/workspaceSearchEngine.d.ts +0 -93
  60. package/dist/api/workspaceSearchEngine.d.ts.map +0 -1
  61. package/dist/api/workspaceSearchEngine.js +0 -55
  62. package/dist/api/workspaceSearchEngine.js.map +0 -1
  63. package/dist/apis.json +0 -129
  64. package/dist/client/account.d.ts +0 -46
  65. package/dist/client/account.d.ts.map +0 -1
  66. package/dist/client/account.js +0 -104
  67. package/dist/client/account.js.map +0 -1
  68. package/dist/error/CanceledError.d.ts +0 -5
  69. package/dist/error/CanceledError.d.ts.map +0 -1
  70. package/dist/error/CanceledError.js +0 -7
  71. package/dist/error/CanceledError.js.map +0 -1
  72. package/dist/error/DefaultAPIError.d.ts +0 -9
  73. package/dist/error/DefaultAPIError.d.ts.map +0 -1
  74. package/dist/error/DefaultAPIError.js +0 -58
  75. package/dist/error/DefaultAPIError.js.map +0 -1
  76. package/dist/error/StackspotAPIError.d.ts +0 -19
  77. package/dist/error/StackspotAPIError.d.ts.map +0 -1
  78. package/dist/error/StackspotAPIError.js +0 -39
  79. package/dist/error/StackspotAPIError.js.map +0 -1
  80. package/dist/error/dictionary/account.d.ts +0 -55
  81. package/dist/error/dictionary/account.d.ts.map +0 -1
  82. package/dist/error/dictionary/account.js +0 -55
  83. package/dist/error/dictionary/account.js.map +0 -1
  84. package/dist/error/dictionary/action.d.ts +0 -163
  85. package/dist/error/dictionary/action.d.ts.map +0 -1
  86. package/dist/error/dictionary/action.js +0 -163
  87. package/dist/error/dictionary/action.js.map +0 -1
  88. package/dist/error/dictionary/base.d.ts +0 -21
  89. package/dist/error/dictionary/base.d.ts.map +0 -1
  90. package/dist/error/dictionary/base.js +0 -21
  91. package/dist/error/dictionary/base.js.map +0 -1
  92. package/dist/error/dictionary/cnt-fields.d.ts +0 -13
  93. package/dist/error/dictionary/cnt-fields.d.ts.map +0 -1
  94. package/dist/error/dictionary/cnt-fields.js +0 -13
  95. package/dist/error/dictionary/cnt-fields.js.map +0 -1
  96. package/dist/error/dictionary/cnt.d.ts +0 -79
  97. package/dist/error/dictionary/cnt.d.ts.map +0 -1
  98. package/dist/error/dictionary/cnt.js +0 -79
  99. package/dist/error/dictionary/cnt.js.map +0 -1
  100. package/dist/error/dictionary/rte.d.ts +0 -23
  101. package/dist/error/dictionary/rte.d.ts.map +0 -1
  102. package/dist/error/dictionary/rte.js +0 -23
  103. package/dist/error/dictionary/rte.js.map +0 -1
  104. package/dist/error/dictionary/rtm.d.ts +0 -9
  105. package/dist/error/dictionary/rtm.d.ts.map +0 -1
  106. package/dist/error/dictionary/rtm.js +0 -9
  107. package/dist/error/dictionary/rtm.js.map +0 -1
  108. package/dist/error/dictionary/workspace-fields.d.ts +0 -9
  109. package/dist/error/dictionary/workspace-fields.d.ts.map +0 -1
  110. package/dist/error/dictionary/workspace-fields.js +0 -9
  111. package/dist/error/dictionary/workspace-fields.js.map +0 -1
  112. package/dist/error/dictionary/workspace.d.ts +0 -99
  113. package/dist/error/dictionary/workspace.d.ts.map +0 -1
  114. package/dist/error/dictionary/workspace.js +0 -99
  115. package/dist/error/dictionary/workspace.js.map +0 -1
  116. package/dist/index.d.ts +0 -6
  117. package/dist/index.d.ts.map +0 -1
  118. package/dist/index.js +0 -6
  119. package/dist/index.js.map +0 -1
  120. package/dist/network/AutoMutation.d.ts +0 -10
  121. package/dist/network/AutoMutation.d.ts.map +0 -1
  122. package/dist/network/AutoMutation.js +0 -20
  123. package/dist/network/AutoMutation.js.map +0 -1
  124. package/dist/network/AutoOperation.d.ts +0 -19
  125. package/dist/network/AutoOperation.d.ts.map +0 -1
  126. package/dist/network/AutoOperation.js +0 -99
  127. package/dist/network/AutoOperation.js.map +0 -1
  128. package/dist/network/AutoQuery.d.ts +0 -19
  129. package/dist/network/AutoQuery.d.ts.map +0 -1
  130. package/dist/network/AutoQuery.js +0 -70
  131. package/dist/network/AutoQuery.js.map +0 -1
  132. package/dist/network/ManualMutation.d.ts +0 -11
  133. package/dist/network/ManualMutation.d.ts.map +0 -1
  134. package/dist/network/ManualMutation.js +0 -32
  135. package/dist/network/ManualMutation.js.map +0 -1
  136. package/dist/network/ManualOperation.d.ts +0 -13
  137. package/dist/network/ManualOperation.d.ts.map +0 -1
  138. package/dist/network/ManualOperation.js +0 -53
  139. package/dist/network/ManualOperation.js.map +0 -1
  140. package/dist/network/ManualQuery.d.ts +0 -20
  141. package/dist/network/ManualQuery.d.ts.map +0 -1
  142. package/dist/network/ManualQuery.js +0 -77
  143. package/dist/network/ManualQuery.js.map +0 -1
  144. package/dist/network/NetworkClient.d.ts +0 -20
  145. package/dist/network/NetworkClient.d.ts.map +0 -1
  146. package/dist/network/NetworkClient.js +0 -85
  147. package/dist/network/NetworkClient.js.map +0 -1
  148. package/dist/network/ReactQueryNetworkClient.d.ts +0 -16
  149. package/dist/network/ReactQueryNetworkClient.d.ts.map +0 -1
  150. package/dist/network/ReactQueryNetworkClient.js +0 -125
  151. package/dist/network/ReactQueryNetworkClient.js.map +0 -1
  152. package/dist/network/react-query-client.d.ts +0 -3
  153. package/dist/network/react-query-client.d.ts.map +0 -1
  154. package/dist/network/react-query-client.js +0 -3
  155. package/dist/network/react-query-client.js.map +0 -1
  156. package/dist/network/types.d.ts +0 -55
  157. package/dist/network/types.d.ts.map +0 -1
  158. package/dist/network/types.js +0 -2
  159. package/dist/network/types.js.map +0 -1
  160. 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.1.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",
@@ -2,7 +2,8 @@ import { HttpError } from '@oazapfts/runtime'
2
2
  import {
3
3
  accountDataIsAvailable, create, createPartner,
4
4
  defaults,
5
- deletePartner, getFeatures, getPartnerAccount, getPartnersSharingAllowed,
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
- create = this.mutation(create)
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(rawResponse: any, status: number, dictionary: Dictionary, headers?: Headers) {
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
- return this.intl?.(language) ?? this.message
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
+ }
@@ -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
- private currentRequests = new Map<Variables | undefined, Promise<Result>>()
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
- private currentRequests = new Map<Record<string, any> | undefined, Promise<Result>>()
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
- private async makeRequest(variables: Record<string, any> | undefined) {
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
- export class NetworkClient {
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
- protected get<T>(path: string, request?: RequestOptions): Promise<T> {
70
- return this.sendRequest(path, 'get', request) as Promise<T>
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
- protected post<T>(path: string, request?: RequestWithBody): Promise<T> {
74
- return this.sendRequest(path, 'post', request) as Promise<T>
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
- protected put<T>(path: string, request?: RequestWithBody): Promise<T> {
78
- return this.sendRequest(path, 'put', request) as Promise<T>
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
- protected patch<T>(path: string, request?: RequestWithBody): Promise<T> {
82
- return this.sendRequest(path, 'patch', request) as Promise<T>
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
- protected delete<T>(path: string, request?: RequestWithBody): Promise<T> {
86
- return this.sendRequest(path, 'delete', request) as Promise<T>
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