@ethlete/query 5.15.0 → 5.16.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 (184) hide show
  1. package/.eslintrc.json +40 -0
  2. package/CHANGELOG.md +14 -0
  3. package/jest.config.ts +23 -0
  4. package/ng-package.json +8 -0
  5. package/package.json +6 -23
  6. package/project.json +37 -0
  7. package/{index.d.ts → src/index.ts} +2 -0
  8. package/src/lib/auth/auth-provider.types.ts +160 -0
  9. package/src/lib/auth/auth-provider.utils.ts +39 -0
  10. package/src/lib/auth/basic-auth-provider.ts +17 -0
  11. package/src/lib/auth/bearer-auth-provider.ts +243 -0
  12. package/src/lib/auth/custom-header-auth-provider.ts +13 -0
  13. package/{lib/class-validator/class-validator.types.d.ts → src/lib/class-validator/class-validator.types.ts} +4 -3
  14. package/src/lib/class-validator/class-validator.utils.ts +5 -0
  15. package/src/lib/devtools/components/query-devtools/query-devtools.component.html +402 -0
  16. package/src/lib/devtools/components/query-devtools/query-devtools.component.scss +360 -0
  17. package/src/lib/devtools/components/query-devtools/query-devtools.component.ts +427 -0
  18. package/src/lib/devtools/pipes/query-short-name.pipe.ts +24 -0
  19. package/src/lib/devtools/utils/query-client.utils.ts +25 -0
  20. package/src/lib/directives/infinity-query-trigger.directive.ts +58 -0
  21. package/src/lib/directives/infinity-query.directive.ts +208 -0
  22. package/src/lib/directives/query.directive.ts +201 -0
  23. package/src/lib/entity/entity-store.ts +180 -0
  24. package/src/lib/entity/entity.types.ts +34 -0
  25. package/src/lib/entity/entity.utils.ts +51 -0
  26. package/src/lib/gql/gql.ts +22 -0
  27. package/src/lib/infinite-query/infinity-query.ts +245 -0
  28. package/src/lib/infinite-query/infinity-query.types.ts +149 -0
  29. package/src/lib/infinite-query/infinity-query.utils.ts +14 -0
  30. package/src/lib/logger/logger.ts +30 -0
  31. package/src/lib/query/query.ts +631 -0
  32. package/src/lib/query/query.types.ts +532 -0
  33. package/src/lib/query/query.utils.spec.ts +92 -0
  34. package/src/lib/query/query.utils.ts +450 -0
  35. package/src/lib/query-client/query-client.spec.ts +77 -0
  36. package/src/lib/query-client/query-client.ts +195 -0
  37. package/src/lib/query-client/query-client.types.ts +101 -0
  38. package/src/lib/query-client/query-client.utils.spec.ts +61 -0
  39. package/src/lib/query-client/query-client.utils.ts +28 -0
  40. package/src/lib/query-creator/query-creator.ts +103 -0
  41. package/src/lib/query-creator/query-creator.types.ts +74 -0
  42. package/src/lib/query-form/query-form.ts +526 -0
  43. package/src/lib/query-form/query-form.types.ts +169 -0
  44. package/src/lib/query-form/query-form.utils.ts +116 -0
  45. package/src/lib/query-store/query-store.ts +192 -0
  46. package/src/lib/request/request.ts +253 -0
  47. package/src/lib/request/request.types.ts +164 -0
  48. package/src/lib/request/request.util.spec.ts +177 -0
  49. package/src/lib/request/request.util.ts +399 -0
  50. package/src/lib/stories/angular-example-page.mdx +85 -0
  51. package/src/lib/stories/authentication-page.mdx +35 -0
  52. package/src/lib/stories/changelog-page.mdx +9 -0
  53. package/src/lib/stories/client-page.mdx +100 -0
  54. package/src/lib/stories/queries-page.mdx +117 -0
  55. package/src/lib/symfony/symfony.types.ts +21 -0
  56. package/src/lib/symfony/symfony.utils.ts +43 -0
  57. package/src/lib/utils/data.utils.ts +280 -0
  58. package/src/lib/utils/deep-freeze.util.ts +21 -0
  59. package/{lib/utils/def.util.d.ts → src/lib/utils/def.util.ts} +1 -1
  60. package/src/test-setup.ts +1 -0
  61. package/tsconfig.json +29 -0
  62. package/tsconfig.lib.json +12 -0
  63. package/tsconfig.lib.prod.json +10 -0
  64. package/tsconfig.spec.json +11 -0
  65. package/esm2022/ethlete-query.mjs +0 -5
  66. package/esm2022/index.mjs +0 -20
  67. package/esm2022/lib/auth/auth-provider.types.mjs +0 -2
  68. package/esm2022/lib/auth/auth-provider.utils.mjs +0 -25
  69. package/esm2022/lib/auth/basic-auth-provider.mjs +0 -15
  70. package/esm2022/lib/auth/bearer-auth-provider.mjs +0 -168
  71. package/esm2022/lib/auth/custom-header-auth-provider.mjs +0 -12
  72. package/esm2022/lib/auth/index.mjs +0 -6
  73. package/esm2022/lib/class-validator/class-validator.types.mjs +0 -2
  74. package/esm2022/lib/class-validator/class-validator.utils.mjs +0 -4
  75. package/esm2022/lib/class-validator/index.mjs +0 -3
  76. package/esm2022/lib/devtools/components/query-devtools/index.mjs +0 -2
  77. package/esm2022/lib/devtools/components/query-devtools/query-devtools.component.mjs +0 -297
  78. package/esm2022/lib/devtools/pipes/index.mjs +0 -2
  79. package/esm2022/lib/devtools/pipes/query-short-name.pipe.mjs +0 -24
  80. package/esm2022/lib/devtools/utils/index.mjs +0 -2
  81. package/esm2022/lib/devtools/utils/query-client.utils.mjs +0 -13
  82. package/esm2022/lib/directives/index.mjs +0 -4
  83. package/esm2022/lib/directives/infinity-query-trigger.directive.mjs +0 -56
  84. package/esm2022/lib/directives/infinity-query.directive.mjs +0 -147
  85. package/esm2022/lib/directives/query.directive.mjs +0 -123
  86. package/esm2022/lib/entity/entity-store.mjs +0 -127
  87. package/esm2022/lib/entity/entity.types.mjs +0 -2
  88. package/esm2022/lib/entity/entity.utils.mjs +0 -29
  89. package/esm2022/lib/entity/index.mjs +0 -4
  90. package/esm2022/lib/gql/gql.mjs +0 -23
  91. package/esm2022/lib/gql/index.mjs +0 -2
  92. package/esm2022/lib/infinite-query/index.mjs +0 -4
  93. package/esm2022/lib/infinite-query/infinity-query.mjs +0 -171
  94. package/esm2022/lib/infinite-query/infinity-query.types.mjs +0 -2
  95. package/esm2022/lib/infinite-query/infinity-query.utils.mjs +0 -3
  96. package/esm2022/lib/logger/index.mjs +0 -2
  97. package/esm2022/lib/logger/logger.mjs +0 -16
  98. package/esm2022/lib/query/index.mjs +0 -4
  99. package/esm2022/lib/query/query.mjs +0 -428
  100. package/esm2022/lib/query/query.types.mjs +0 -2
  101. package/esm2022/lib/query/query.utils.mjs +0 -253
  102. package/esm2022/lib/query-client/index.mjs +0 -4
  103. package/esm2022/lib/query-client/query-client.mjs +0 -80
  104. package/esm2022/lib/query-client/query-client.types.mjs +0 -2
  105. package/esm2022/lib/query-client/query-client.utils.mjs +0 -20
  106. package/esm2022/lib/query-creator/index.mjs +0 -3
  107. package/esm2022/lib/query-creator/query-creator.mjs +0 -55
  108. package/esm2022/lib/query-creator/query-creator.types.mjs +0 -2
  109. package/esm2022/lib/query-form/index.mjs +0 -4
  110. package/esm2022/lib/query-form/query-form.mjs +0 -368
  111. package/esm2022/lib/query-form/query-form.types.mjs +0 -2
  112. package/esm2022/lib/query-form/query-form.utils.mjs +0 -97
  113. package/esm2022/lib/query-store/index.mjs +0 -2
  114. package/esm2022/lib/query-store/query-store.mjs +0 -144
  115. package/esm2022/lib/request/index.mjs +0 -4
  116. package/esm2022/lib/request/request.mjs +0 -196
  117. package/esm2022/lib/request/request.types.mjs +0 -2
  118. package/esm2022/lib/request/request.util.mjs +0 -252
  119. package/esm2022/lib/symfony/index.mjs +0 -3
  120. package/esm2022/lib/symfony/symfony.types.mjs +0 -2
  121. package/esm2022/lib/symfony/symfony.utils.mjs +0 -31
  122. package/esm2022/lib/utils/data.utils.mjs +0 -138
  123. package/esm2022/lib/utils/deep-freeze.util.mjs +0 -17
  124. package/esm2022/lib/utils/def.util.mjs +0 -8
  125. package/esm2022/lib/utils/index.mjs +0 -4
  126. package/fesm2022/ethlete-query.mjs +0 -3276
  127. package/fesm2022/ethlete-query.mjs.map +0 -1
  128. package/lib/auth/auth-provider.types.d.ts +0 -133
  129. package/lib/auth/auth-provider.utils.d.ts +0 -9
  130. package/lib/auth/basic-auth-provider.d.ts +0 -10
  131. package/lib/auth/bearer-auth-provider.d.ts +0 -23
  132. package/lib/auth/custom-header-auth-provider.d.ts +0 -9
  133. package/lib/class-validator/class-validator.utils.d.ts +0 -2
  134. package/lib/devtools/components/query-devtools/query-devtools.component.d.ts +0 -151
  135. package/lib/devtools/pipes/query-short-name.pipe.d.ts +0 -8
  136. package/lib/devtools/utils/query-client.utils.d.ts +0 -12
  137. package/lib/directives/infinity-query-trigger.directive.d.ts +0 -15
  138. package/lib/directives/infinity-query.directive.d.ts +0 -49
  139. package/lib/directives/query.directive.d.ts +0 -63
  140. package/lib/entity/entity-store.d.ts +0 -25
  141. package/lib/entity/entity.types.d.ts +0 -25
  142. package/lib/entity/entity.utils.d.ts +0 -12
  143. package/lib/gql/gql.d.ts +0 -6
  144. package/lib/infinite-query/infinity-query.d.ts +0 -29
  145. package/lib/infinite-query/infinity-query.types.d.ts +0 -104
  146. package/lib/infinite-query/infinity-query.utils.d.ts +0 -4
  147. package/lib/logger/logger.d.ts +0 -12
  148. package/lib/query/query.d.ts +0 -60
  149. package/lib/query/query.types.d.ts +0 -388
  150. package/lib/query/query.utils.d.ts +0 -91
  151. package/lib/query-client/query-client.d.ts +0 -29
  152. package/lib/query-client/query-client.types.d.ts +0 -82
  153. package/lib/query-client/query-client.utils.d.ts +0 -4
  154. package/lib/query-creator/query-creator.d.ts +0 -20
  155. package/lib/query-creator/query-creator.types.d.ts +0 -13
  156. package/lib/query-form/query-form.d.ts +0 -89
  157. package/lib/query-form/query-form.types.d.ts +0 -138
  158. package/lib/query-form/query-form.utils.d.ts +0 -13
  159. package/lib/query-store/query-store.d.ts +0 -31
  160. package/lib/request/request.d.ts +0 -3
  161. package/lib/request/request.types.d.ts +0 -126
  162. package/lib/request/request.util.d.ts +0 -92
  163. package/lib/symfony/symfony.types.d.ts +0 -19
  164. package/lib/symfony/symfony.utils.d.ts +0 -6
  165. package/lib/utils/data.utils.d.ts +0 -59
  166. package/lib/utils/deep-freeze.util.d.ts +0 -1
  167. /package/{lib/auth/index.d.ts → src/lib/auth/index.ts} +0 -0
  168. /package/{lib/class-validator/index.d.ts → src/lib/class-validator/index.ts} +0 -0
  169. /package/{lib/devtools/components/query-devtools/index.d.ts → src/lib/devtools/components/query-devtools/index.ts} +0 -0
  170. /package/{lib/devtools/pipes/index.d.ts → src/lib/devtools/pipes/index.ts} +0 -0
  171. /package/{lib/devtools/utils/index.d.ts → src/lib/devtools/utils/index.ts} +0 -0
  172. /package/{lib/directives/index.d.ts → src/lib/directives/index.ts} +0 -0
  173. /package/{lib/entity/index.d.ts → src/lib/entity/index.ts} +0 -0
  174. /package/{lib/gql/index.d.ts → src/lib/gql/index.ts} +0 -0
  175. /package/{lib/infinite-query/index.d.ts → src/lib/infinite-query/index.ts} +0 -0
  176. /package/{lib/logger/index.d.ts → src/lib/logger/index.ts} +0 -0
  177. /package/{lib/query/index.d.ts → src/lib/query/index.ts} +0 -0
  178. /package/{lib/query-client/index.d.ts → src/lib/query-client/index.ts} +0 -0
  179. /package/{lib/query-creator/index.d.ts → src/lib/query-creator/index.ts} +0 -0
  180. /package/{lib/query-form/index.d.ts → src/lib/query-form/index.ts} +0 -0
  181. /package/{lib/query-store/index.d.ts → src/lib/query-store/index.ts} +0 -0
  182. /package/{lib/request/index.d.ts → src/lib/request/index.ts} +0 -0
  183. /package/{lib/symfony/index.d.ts → src/lib/symfony/index.ts} +0 -0
  184. /package/{lib/utils/index.d.ts → src/lib/utils/index.ts} +0 -0
@@ -0,0 +1,450 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { assertInInjectionContext, isDevMode, signal } from '@angular/core';
3
+ import { toObservable } from '@angular/core/rxjs-interop';
4
+ import { Paginated } from '@ethlete/types';
5
+ import { BehaviorSubject, Observable, filter, map, of, switchMap, takeWhile, tap } from 'rxjs';
6
+ import { EntityStore } from '../entity';
7
+ import { transformGql } from '../gql';
8
+ import { QueryClient } from '../query-client';
9
+ import {
10
+ AnyQueryCreator,
11
+ QueryArgsOf,
12
+ QueryCreator,
13
+ QueryDataOf,
14
+ QueryResponseOf,
15
+ QueryStoreIdOf,
16
+ QueryStoreOf,
17
+ } from '../query-creator';
18
+ import { QueryForm } from '../query-form';
19
+ import { HttpStatusCode, Method, RequestHeaders, RequestHeadersMethodMap, transformMethod } from '../request';
20
+ import { isSymfonyPagerfantaOutOfRangeError } from '../symfony';
21
+ import { QueryContainerConfig, addQueryContainerHandling } from '../utils';
22
+ import {
23
+ AnyGqlQueryConfig,
24
+ AnyQuery,
25
+ AnyQueryCollection,
26
+ AnyQueryCollectionData,
27
+ AnyQueryCreatorCollection,
28
+ AnyRestQueryConfig,
29
+ BaseArguments,
30
+ Cancelled,
31
+ Failure,
32
+ GqlQueryConfig,
33
+ Loading,
34
+ Prepared,
35
+ QueryCollectionOf,
36
+ QueryState,
37
+ QueryStateResponseOf,
38
+ QueryStateType,
39
+ ResetPageOnErrorOperatorConfig,
40
+ RouteType,
41
+ Success,
42
+ } from './query.types';
43
+
44
+ type OmitNull<T> = T extends null ? never : T;
45
+
46
+ export function filterSuccess() {
47
+ return function <T extends QueryState | null, Response extends QueryStateResponseOf<OmitNull<T>>>(
48
+ source: Observable<T>,
49
+ ) {
50
+ return source.pipe(filter((value) => isQueryStateSuccess(value))) as Observable<Success<Response>>;
51
+ };
52
+ }
53
+
54
+ export function ignoreAutoRefresh() {
55
+ return function <T extends QueryState | null>(source: Observable<T>) {
56
+ return source.pipe(filter((value) => !isAutoRefresh(value))) as Observable<T>;
57
+ };
58
+ }
59
+
60
+ export function filterFailure() {
61
+ return function <T extends QueryState | null>(source: Observable<T>) {
62
+ return source.pipe(filter((value) => isQueryStateFailure(value))) as Observable<Failure>;
63
+ };
64
+ }
65
+
66
+ export function filterQueryStates(allowedStates: QueryStateType[]) {
67
+ return function <T extends QueryState | null>(source: Observable<T>) {
68
+ return source.pipe(filter((value) => value && allowedStates.includes(value.type))) as Observable<T>;
69
+ };
70
+ }
71
+
72
+ export function takeUntilResponse(config?: { excludeNull?: boolean }) {
73
+ return function <T extends QueryState | null>(source: Observable<T>) {
74
+ return source.pipe(
75
+ takeWhile((value) => {
76
+ if (config?.excludeNull && value === null) {
77
+ return false;
78
+ }
79
+
80
+ return isQueryStateLoading(value) || isQueryStatePrepared(value) || value === null;
81
+ }, true),
82
+ );
83
+ };
84
+ }
85
+
86
+ export function filterNull() {
87
+ return function <T>(source: Observable<T>) {
88
+ return source.pipe(filter((value): value is OmitNull<T> => value !== null));
89
+ };
90
+ }
91
+
92
+ export function switchQueryState() {
93
+ return function <T extends AnyQuery | null, Data extends QueryDataOf<T>>(source: Observable<T>) {
94
+ return source.pipe(switchMap((value) => value?.state$ ?? of(null))) as Observable<QueryState<Data> | null>;
95
+ };
96
+ }
97
+
98
+ export function switchQueryCollectionState() {
99
+ return function <T extends AnyQueryCollection | null, Data extends AnyQueryCollectionData<OmitNull<T>>>(
100
+ source: Observable<T>,
101
+ ) {
102
+ return source.pipe(switchMap((value) => value?.query.state$ ?? of(null))) as Observable<QueryState<Data> | null>;
103
+ };
104
+ }
105
+
106
+ export const resetPageOnError =
107
+ <T extends AnyQuery | null | undefined, J extends QueryForm<any>>(config: ResetPageOnErrorOperatorConfig<J>) =>
108
+ (source: Observable<T>) => {
109
+ const { queryForm, pageControlKey = 'page' } = config;
110
+
111
+ if (isDevMode() && !queryForm.form.controls[pageControlKey]) {
112
+ console.warn(`resetPageOnError: queryForm.form.controls["${pageControlKey}"] is undefined`);
113
+ }
114
+
115
+ return source.pipe(
116
+ switchMap((q) => {
117
+ if (!q) {
118
+ return of(q);
119
+ }
120
+
121
+ return q.state$.pipe(
122
+ tap((state) => {
123
+ if (!isQueryStateFailure(state)) {
124
+ return;
125
+ }
126
+
127
+ switch (state.error.status) {
128
+ // The page is invalid (in prod mode, code is 416)
129
+ case HttpStatusCode.RangeNotSatisfiable:
130
+ queryForm.form.controls[pageControlKey]?.patchValue(1);
131
+ break;
132
+
133
+ // The page might be invalid (in dev mode, code is 500)
134
+ case HttpStatusCode.InternalServerError:
135
+ if (isSymfonyPagerfantaOutOfRangeError(state.error.detail)) {
136
+ queryForm.form.controls[pageControlKey].patchValue(1);
137
+ }
138
+ break;
139
+ }
140
+ }),
141
+ takeUntilResponse(),
142
+ );
143
+ }),
144
+ );
145
+ };
146
+
147
+ export const isQueryStateLoading = (state: QueryState | null | undefined): state is Loading =>
148
+ state?.type === QueryStateType.Loading;
149
+
150
+ export const isQueryStateSuccess = (state: QueryState | null | undefined): state is Success =>
151
+ state?.type === QueryStateType.Success;
152
+
153
+ export const isQueryStateFailure = (state: QueryState | null | undefined): state is Failure =>
154
+ state?.type === QueryStateType.Failure;
155
+
156
+ export const isQueryStateCancelled = (state: QueryState | null | undefined): state is Cancelled =>
157
+ state?.type === QueryStateType.Cancelled;
158
+
159
+ export const isQueryStatePrepared = (state: QueryState | null | undefined): state is Prepared =>
160
+ state?.type === QueryStateType.Prepared;
161
+
162
+ export const isAutoRefresh = (state: QueryState | null | undefined): boolean => state?.meta.triggeredVia === 'auto';
163
+
164
+ export const mergeHeaders = (...headers: Array<RequestHeaders | null | undefined>) => {
165
+ return headers.reduce((acc, headers) => {
166
+ if (!headers) {
167
+ return acc;
168
+ }
169
+
170
+ return {
171
+ ...acc,
172
+ ...headers,
173
+ };
174
+ }, {});
175
+ };
176
+
177
+ export const isGqlQueryConfig = <
178
+ Response,
179
+ Arguments extends BaseArguments | undefined,
180
+ Route extends RouteType<Arguments> | undefined,
181
+ Store extends EntityStore<unknown>,
182
+ Data,
183
+ Id,
184
+ >(
185
+ config: unknown,
186
+ ): config is GqlQueryConfig<Route, Response, Arguments, Store, Data, Id> => {
187
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
188
+ return false;
189
+ }
190
+
191
+ if (!('query' in config)) {
192
+ return false;
193
+ }
194
+
195
+ return true;
196
+ };
197
+
198
+ export const isQuery = <T extends AnyQuery>(query: unknown): query is T => {
199
+ if (!query || typeof query !== 'object' || Array.isArray(query)) {
200
+ return false;
201
+ }
202
+
203
+ if (!('state$' in query)) {
204
+ return false;
205
+ }
206
+
207
+ return true;
208
+ };
209
+
210
+ export const isQueryCollection = <T extends AnyQueryCollection>(query: unknown): query is T => {
211
+ if (!query || typeof query !== 'object' || Array.isArray(query)) {
212
+ return false;
213
+ }
214
+
215
+ if (!('query' in query)) {
216
+ return false;
217
+ }
218
+
219
+ return true;
220
+ };
221
+
222
+ /**
223
+ * @deprecated Use `createQueryCollectionSubject` instead. Will be removed in v6.
224
+ */
225
+ export const createQueryCollection = <T extends AnyQueryCreatorCollection, R extends QueryCollectionOf<T>>(
226
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
227
+ queryMap: T,
228
+ ) => new BehaviorSubject<R | null>(null);
229
+
230
+ export const createQueryCollectionSubject = <T extends AnyQueryCreatorCollection, R extends QueryCollectionOf<T>>(
231
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
232
+ queryMap: T,
233
+ config?: QueryContainerConfig,
234
+ ) => {
235
+ assertInInjectionContext(createQueryCollectionSubject);
236
+
237
+ const subject = new BehaviorSubject<R | null>(null);
238
+
239
+ addQueryContainerHandling(
240
+ subject.pipe(map((s) => s?.query ?? null)),
241
+ () => subject.getValue()?.query ?? null,
242
+ config,
243
+ );
244
+
245
+ return subject;
246
+ };
247
+
248
+ export const createQueryCollectionSignal = <T extends AnyQueryCreatorCollection, R extends QueryCollectionOf<T>>(
249
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
250
+ queryMap: T,
251
+ config?: QueryContainerConfig,
252
+ ) => {
253
+ assertInInjectionContext(createQueryCollectionSignal);
254
+
255
+ const _signal = signal<R | null>(null);
256
+
257
+ addQueryContainerHandling(
258
+ toObservable(_signal).pipe(map((s) => s?.query ?? null)),
259
+ () => _signal()?.query ?? null,
260
+ config,
261
+ );
262
+
263
+ return _signal;
264
+ };
265
+
266
+ export const extractQuery = <T extends AnyQuery | AnyQueryCollection | null>(v: T) =>
267
+ (isQuery(v) ? v : v?.query) ?? null;
268
+
269
+ export const getDefaultHeaders = (
270
+ headers: RequestHeaders | RequestHeadersMethodMap | null | undefined,
271
+ method: Method,
272
+ ) => {
273
+ if (!headers) {
274
+ return {};
275
+ }
276
+
277
+ if (method in headers) {
278
+ return (headers[method] ?? {}) as RequestHeaders;
279
+ }
280
+
281
+ return headers as RequestHeaders;
282
+ };
283
+
284
+ export const castQueryCreatorTypes = <
285
+ QC extends AnyQueryCreator,
286
+ Arguments extends QueryArgsOf<QC>,
287
+ Response extends QueryResponseOf<QC>,
288
+ Store extends QueryStoreOf<QC>,
289
+ Data extends QueryDataOf<QC>,
290
+ Route extends RouteType<Arguments>,
291
+ Id extends QueryStoreIdOf<QC>,
292
+ OverrideArguments extends BaseArguments | undefined,
293
+ OverrideResponse extends Response | undefined,
294
+ >(config: {
295
+ creator: QC;
296
+ args?: OverrideArguments;
297
+ response?: OverrideResponse;
298
+ }) => {
299
+ if (config.args?.pathParams) {
300
+ console.error(config);
301
+
302
+ throw new Error('Path params cannot be overridden in castQueryCreatorTypes. Create a new query creator instead.');
303
+ }
304
+
305
+ return config.creator as unknown as QueryCreator<OverrideArguments, OverrideResponse, Route, Store, Data, Id>;
306
+ };
307
+
308
+ export const computeQueryMethod = (config: { config: AnyRestQueryConfig | AnyGqlQueryConfig; client: QueryClient }) => {
309
+ let method: Method;
310
+
311
+ if (isGqlQueryConfig(config.config)) {
312
+ const transferVia = config.config.transferVia ?? config.client.config.request?.gql?.transferVia;
313
+ method = transformMethod({ method: config.config.method, transferVia });
314
+ } else {
315
+ method = transformMethod({ method: config.config.method });
316
+ }
317
+
318
+ return method;
319
+ };
320
+
321
+ export const computeQueryBody = (config: {
322
+ config: AnyRestQueryConfig | AnyGqlQueryConfig;
323
+ client: QueryClient;
324
+ method: Method;
325
+ args?: BaseArguments;
326
+ }) => {
327
+ let body: unknown;
328
+
329
+ if (
330
+ config.method === 'GET' ||
331
+ config.method === 'HEAD' ||
332
+ config.method === 'OPTIONS' ||
333
+ (isGqlQueryConfig(config.config) &&
334
+ (config.config.transferVia === 'GET' ||
335
+ (config.client.config.request?.gql?.transferVia === 'GET' && !config.config.transferVia)))
336
+ ) {
337
+ return undefined;
338
+ }
339
+
340
+ if (isGqlQueryConfig(config.config)) {
341
+ const queryTemplate = config.config.query;
342
+ const query = transformGql(queryTemplate);
343
+
344
+ body = query(config.args?.variables);
345
+ } else {
346
+ body = config.args?.body;
347
+ }
348
+
349
+ return body;
350
+ };
351
+
352
+ export const computeQueryAuthHeader = (config: {
353
+ config: AnyRestQueryConfig | AnyGqlQueryConfig;
354
+ client: QueryClient;
355
+ }) => {
356
+ let authHeader: Record<string, string> | null = null;
357
+
358
+ if (config.config.secure) {
359
+ const header = config.client.authProvider?.header;
360
+
361
+ if (header) {
362
+ authHeader = header;
363
+ }
364
+ } else if (config.client.authProvider?.header) {
365
+ if (config.config.secure === undefined || config.config.secure) {
366
+ authHeader = config.client.authProvider.header;
367
+ }
368
+ }
369
+
370
+ return authHeader;
371
+ };
372
+
373
+ export const computeQueryHeaders = (config: {
374
+ config: AnyRestQueryConfig | AnyGqlQueryConfig;
375
+ client: QueryClient;
376
+ args?: BaseArguments;
377
+ }) => {
378
+ const authHeader = computeQueryAuthHeader(config);
379
+
380
+ const mergedHeaders =
381
+ mergeHeaders(
382
+ getDefaultHeaders(config.client.config.request?.headers, config.config.method),
383
+ authHeader,
384
+ config.args?.headers,
385
+ ) || undefined;
386
+
387
+ return mergedHeaders;
388
+ };
389
+
390
+ export const computeQueryQueryParams = (config: {
391
+ config: AnyRestQueryConfig | AnyGqlQueryConfig;
392
+ client: QueryClient;
393
+ args?: BaseArguments;
394
+ }) => {
395
+ if (
396
+ isGqlQueryConfig(config.config) &&
397
+ (config.config.transferVia === 'GET' ||
398
+ (config.client.config.request?.gql?.transferVia === 'GET' && !config.config.transferVia))
399
+ ) {
400
+ const queryTemplate = config.config.query;
401
+ const query = transformGql(queryTemplate);
402
+
403
+ let params = query(config.args?.variables);
404
+
405
+ if (config.args?.queryParams) {
406
+ params = { ...params, ...config.args?.queryParams };
407
+ }
408
+
409
+ return params;
410
+ }
411
+
412
+ return config.args?.queryParams;
413
+ };
414
+
415
+ export const paginatedEntityValueUpdater =
416
+ <
417
+ T extends Paginated<unknown>,
418
+ Args extends BaseArguments,
419
+ Entity,
420
+ J extends T extends Paginated<infer X> ? X : never,
421
+ >(
422
+ findFn: (val: J, entity: Entity) => boolean,
423
+ ) =>
424
+ ({ response, rawResponse, entity }: { rawResponse: T; response: T; args: Args; entity: Entity }) => {
425
+ const indexOfEntityRaw = rawResponse.items.findIndex((item) => findFn(item as J, entity));
426
+ const indexOfEntity = response.items.findIndex((item) => findFn(item as J, entity));
427
+
428
+ if (indexOfEntity === -1 || indexOfEntityRaw === -1) {
429
+ return null;
430
+ }
431
+
432
+ const newRawResponse = {
433
+ ...rawResponse,
434
+ items: [
435
+ ...rawResponse.items.slice(0, indexOfEntityRaw),
436
+ entity,
437
+ ...rawResponse.items.slice(indexOfEntityRaw + 1),
438
+ ],
439
+ };
440
+
441
+ const newResponse = {
442
+ ...response,
443
+ items: [...response.items.slice(0, indexOfEntity), entity, ...response.items.slice(indexOfEntity + 1)],
444
+ };
445
+
446
+ return {
447
+ response: newResponse,
448
+ rawResponse: newRawResponse,
449
+ };
450
+ };
@@ -0,0 +1,77 @@
1
+ import { CustomHeaderAuthProvider } from '../auth';
2
+ import { QueryClient } from './query-client';
3
+
4
+ describe('QueryClient', () => {
5
+ let queryClient: QueryClient;
6
+ let mockAuthProvider: CustomHeaderAuthProvider;
7
+ let mockAuthProvider2: CustomHeaderAuthProvider;
8
+
9
+ beforeEach(() => {
10
+ queryClient = new QueryClient({
11
+ baseRoute: 'http://localhost:3333',
12
+ });
13
+ mockAuthProvider = new CustomHeaderAuthProvider({ name: 'x-auth-token', value: '1234' });
14
+ mockAuthProvider2 = new CustomHeaderAuthProvider({ name: 'x-auth-token-2', value: '12345' });
15
+ });
16
+
17
+ describe('setAuthProvider', () => {
18
+ it('should set the auth provider', () => {
19
+ queryClient.setAuthProvider(mockAuthProvider);
20
+ expect(queryClient.authProvider).toBe(mockAuthProvider);
21
+ });
22
+
23
+ it('should cleanup the current auth provider if a new one gets supplied', () => {
24
+ queryClient.setAuthProvider(mockAuthProvider);
25
+
26
+ const mock = jest.spyOn(mockAuthProvider, 'cleanUp');
27
+
28
+ queryClient.setAuthProvider(mockAuthProvider2);
29
+
30
+ expect(mock).toHaveBeenCalled();
31
+ expect(queryClient.authProvider).toBe(mockAuthProvider2);
32
+ });
33
+ });
34
+
35
+ describe('_updateBaseRoute', () => {
36
+ it('should update the base route', () => {
37
+ expect(queryClient.config.baseRoute).toBe('http://localhost:3333');
38
+
39
+ queryClient._updateBaseRoute('http://localhost:3334');
40
+
41
+ expect(queryClient.config.baseRoute).toBe('http://localhost:3334');
42
+ });
43
+ });
44
+
45
+ describe('clearAuthProvider', () => {
46
+ it('should clear the auth provider', () => {
47
+ // Set an auth provider first
48
+ queryClient.setAuthProvider(mockAuthProvider);
49
+ expect(queryClient.authProvider).toBe(mockAuthProvider);
50
+
51
+ // Clear the auth provider
52
+ queryClient.clearAuthProvider();
53
+ expect(queryClient.authProvider).toBeNull();
54
+ });
55
+
56
+ it('should emit null to the authProvider$ observable', () => {
57
+ // Set an auth provider first
58
+ queryClient.setAuthProvider(mockAuthProvider);
59
+
60
+ // Subscribe to the authProvider$ observable
61
+ const mockSubscriber = jest.fn();
62
+ queryClient.authProvider$.subscribe(mockSubscriber);
63
+
64
+ // Clear the auth provider
65
+ queryClient.clearAuthProvider();
66
+
67
+ // Expect the mock subscriber to be called with null
68
+ expect(mockSubscriber).toHaveBeenCalledWith(null);
69
+ });
70
+
71
+ it('should do nothing if there is no auth provider', () => {
72
+ expect(queryClient.authProvider).toBeNull();
73
+ queryClient.clearAuthProvider();
74
+ expect(queryClient.authProvider).toBeNull();
75
+ });
76
+ });
77
+ });