@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.
- package/.eslintrc.json +40 -0
- package/CHANGELOG.md +14 -0
- package/jest.config.ts +23 -0
- package/ng-package.json +8 -0
- package/package.json +6 -23
- package/project.json +37 -0
- package/{index.d.ts → src/index.ts} +2 -0
- package/src/lib/auth/auth-provider.types.ts +160 -0
- package/src/lib/auth/auth-provider.utils.ts +39 -0
- package/src/lib/auth/basic-auth-provider.ts +17 -0
- package/src/lib/auth/bearer-auth-provider.ts +243 -0
- package/src/lib/auth/custom-header-auth-provider.ts +13 -0
- package/{lib/class-validator/class-validator.types.d.ts → src/lib/class-validator/class-validator.types.ts} +4 -3
- package/src/lib/class-validator/class-validator.utils.ts +5 -0
- package/src/lib/devtools/components/query-devtools/query-devtools.component.html +402 -0
- package/src/lib/devtools/components/query-devtools/query-devtools.component.scss +360 -0
- package/src/lib/devtools/components/query-devtools/query-devtools.component.ts +427 -0
- package/src/lib/devtools/pipes/query-short-name.pipe.ts +24 -0
- package/src/lib/devtools/utils/query-client.utils.ts +25 -0
- package/src/lib/directives/infinity-query-trigger.directive.ts +58 -0
- package/src/lib/directives/infinity-query.directive.ts +208 -0
- package/src/lib/directives/query.directive.ts +201 -0
- package/src/lib/entity/entity-store.ts +180 -0
- package/src/lib/entity/entity.types.ts +34 -0
- package/src/lib/entity/entity.utils.ts +51 -0
- package/src/lib/gql/gql.ts +22 -0
- package/src/lib/infinite-query/infinity-query.ts +245 -0
- package/src/lib/infinite-query/infinity-query.types.ts +149 -0
- package/src/lib/infinite-query/infinity-query.utils.ts +14 -0
- package/src/lib/logger/logger.ts +30 -0
- package/src/lib/query/query.ts +631 -0
- package/src/lib/query/query.types.ts +532 -0
- package/src/lib/query/query.utils.spec.ts +92 -0
- package/src/lib/query/query.utils.ts +450 -0
- package/src/lib/query-client/query-client.spec.ts +77 -0
- package/src/lib/query-client/query-client.ts +195 -0
- package/src/lib/query-client/query-client.types.ts +101 -0
- package/src/lib/query-client/query-client.utils.spec.ts +61 -0
- package/src/lib/query-client/query-client.utils.ts +28 -0
- package/src/lib/query-creator/query-creator.ts +103 -0
- package/src/lib/query-creator/query-creator.types.ts +74 -0
- package/src/lib/query-form/query-form.ts +526 -0
- package/src/lib/query-form/query-form.types.ts +169 -0
- package/src/lib/query-form/query-form.utils.ts +116 -0
- package/src/lib/query-store/query-store.ts +192 -0
- package/src/lib/request/request.ts +253 -0
- package/src/lib/request/request.types.ts +164 -0
- package/src/lib/request/request.util.spec.ts +177 -0
- package/src/lib/request/request.util.ts +399 -0
- package/src/lib/stories/angular-example-page.mdx +85 -0
- package/src/lib/stories/authentication-page.mdx +35 -0
- package/src/lib/stories/changelog-page.mdx +9 -0
- package/src/lib/stories/client-page.mdx +100 -0
- package/src/lib/stories/queries-page.mdx +117 -0
- package/src/lib/symfony/symfony.types.ts +21 -0
- package/src/lib/symfony/symfony.utils.ts +43 -0
- package/src/lib/utils/data.utils.ts +280 -0
- package/src/lib/utils/deep-freeze.util.ts +21 -0
- package/{lib/utils/def.util.d.ts → src/lib/utils/def.util.ts} +1 -1
- package/src/test-setup.ts +1 -0
- package/tsconfig.json +29 -0
- package/tsconfig.lib.json +12 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +11 -0
- package/esm2022/ethlete-query.mjs +0 -5
- package/esm2022/index.mjs +0 -20
- package/esm2022/lib/auth/auth-provider.types.mjs +0 -2
- package/esm2022/lib/auth/auth-provider.utils.mjs +0 -25
- package/esm2022/lib/auth/basic-auth-provider.mjs +0 -15
- package/esm2022/lib/auth/bearer-auth-provider.mjs +0 -168
- package/esm2022/lib/auth/custom-header-auth-provider.mjs +0 -12
- package/esm2022/lib/auth/index.mjs +0 -6
- package/esm2022/lib/class-validator/class-validator.types.mjs +0 -2
- package/esm2022/lib/class-validator/class-validator.utils.mjs +0 -4
- package/esm2022/lib/class-validator/index.mjs +0 -3
- package/esm2022/lib/devtools/components/query-devtools/index.mjs +0 -2
- package/esm2022/lib/devtools/components/query-devtools/query-devtools.component.mjs +0 -297
- package/esm2022/lib/devtools/pipes/index.mjs +0 -2
- package/esm2022/lib/devtools/pipes/query-short-name.pipe.mjs +0 -24
- package/esm2022/lib/devtools/utils/index.mjs +0 -2
- package/esm2022/lib/devtools/utils/query-client.utils.mjs +0 -13
- package/esm2022/lib/directives/index.mjs +0 -4
- package/esm2022/lib/directives/infinity-query-trigger.directive.mjs +0 -56
- package/esm2022/lib/directives/infinity-query.directive.mjs +0 -147
- package/esm2022/lib/directives/query.directive.mjs +0 -123
- package/esm2022/lib/entity/entity-store.mjs +0 -127
- package/esm2022/lib/entity/entity.types.mjs +0 -2
- package/esm2022/lib/entity/entity.utils.mjs +0 -29
- package/esm2022/lib/entity/index.mjs +0 -4
- package/esm2022/lib/gql/gql.mjs +0 -23
- package/esm2022/lib/gql/index.mjs +0 -2
- package/esm2022/lib/infinite-query/index.mjs +0 -4
- package/esm2022/lib/infinite-query/infinity-query.mjs +0 -171
- package/esm2022/lib/infinite-query/infinity-query.types.mjs +0 -2
- package/esm2022/lib/infinite-query/infinity-query.utils.mjs +0 -3
- package/esm2022/lib/logger/index.mjs +0 -2
- package/esm2022/lib/logger/logger.mjs +0 -16
- package/esm2022/lib/query/index.mjs +0 -4
- package/esm2022/lib/query/query.mjs +0 -428
- package/esm2022/lib/query/query.types.mjs +0 -2
- package/esm2022/lib/query/query.utils.mjs +0 -253
- package/esm2022/lib/query-client/index.mjs +0 -4
- package/esm2022/lib/query-client/query-client.mjs +0 -80
- package/esm2022/lib/query-client/query-client.types.mjs +0 -2
- package/esm2022/lib/query-client/query-client.utils.mjs +0 -20
- package/esm2022/lib/query-creator/index.mjs +0 -3
- package/esm2022/lib/query-creator/query-creator.mjs +0 -55
- package/esm2022/lib/query-creator/query-creator.types.mjs +0 -2
- package/esm2022/lib/query-form/index.mjs +0 -4
- package/esm2022/lib/query-form/query-form.mjs +0 -368
- package/esm2022/lib/query-form/query-form.types.mjs +0 -2
- package/esm2022/lib/query-form/query-form.utils.mjs +0 -97
- package/esm2022/lib/query-store/index.mjs +0 -2
- package/esm2022/lib/query-store/query-store.mjs +0 -144
- package/esm2022/lib/request/index.mjs +0 -4
- package/esm2022/lib/request/request.mjs +0 -196
- package/esm2022/lib/request/request.types.mjs +0 -2
- package/esm2022/lib/request/request.util.mjs +0 -252
- package/esm2022/lib/symfony/index.mjs +0 -3
- package/esm2022/lib/symfony/symfony.types.mjs +0 -2
- package/esm2022/lib/symfony/symfony.utils.mjs +0 -31
- package/esm2022/lib/utils/data.utils.mjs +0 -138
- package/esm2022/lib/utils/deep-freeze.util.mjs +0 -17
- package/esm2022/lib/utils/def.util.mjs +0 -8
- package/esm2022/lib/utils/index.mjs +0 -4
- package/fesm2022/ethlete-query.mjs +0 -3276
- package/fesm2022/ethlete-query.mjs.map +0 -1
- package/lib/auth/auth-provider.types.d.ts +0 -133
- package/lib/auth/auth-provider.utils.d.ts +0 -9
- package/lib/auth/basic-auth-provider.d.ts +0 -10
- package/lib/auth/bearer-auth-provider.d.ts +0 -23
- package/lib/auth/custom-header-auth-provider.d.ts +0 -9
- package/lib/class-validator/class-validator.utils.d.ts +0 -2
- package/lib/devtools/components/query-devtools/query-devtools.component.d.ts +0 -151
- package/lib/devtools/pipes/query-short-name.pipe.d.ts +0 -8
- package/lib/devtools/utils/query-client.utils.d.ts +0 -12
- package/lib/directives/infinity-query-trigger.directive.d.ts +0 -15
- package/lib/directives/infinity-query.directive.d.ts +0 -49
- package/lib/directives/query.directive.d.ts +0 -63
- package/lib/entity/entity-store.d.ts +0 -25
- package/lib/entity/entity.types.d.ts +0 -25
- package/lib/entity/entity.utils.d.ts +0 -12
- package/lib/gql/gql.d.ts +0 -6
- package/lib/infinite-query/infinity-query.d.ts +0 -29
- package/lib/infinite-query/infinity-query.types.d.ts +0 -104
- package/lib/infinite-query/infinity-query.utils.d.ts +0 -4
- package/lib/logger/logger.d.ts +0 -12
- package/lib/query/query.d.ts +0 -60
- package/lib/query/query.types.d.ts +0 -388
- package/lib/query/query.utils.d.ts +0 -91
- package/lib/query-client/query-client.d.ts +0 -29
- package/lib/query-client/query-client.types.d.ts +0 -82
- package/lib/query-client/query-client.utils.d.ts +0 -4
- package/lib/query-creator/query-creator.d.ts +0 -20
- package/lib/query-creator/query-creator.types.d.ts +0 -13
- package/lib/query-form/query-form.d.ts +0 -89
- package/lib/query-form/query-form.types.d.ts +0 -138
- package/lib/query-form/query-form.utils.d.ts +0 -13
- package/lib/query-store/query-store.d.ts +0 -31
- package/lib/request/request.d.ts +0 -3
- package/lib/request/request.types.d.ts +0 -126
- package/lib/request/request.util.d.ts +0 -92
- package/lib/symfony/symfony.types.d.ts +0 -19
- package/lib/symfony/symfony.utils.d.ts +0 -6
- package/lib/utils/data.utils.d.ts +0 -59
- package/lib/utils/deep-freeze.util.d.ts +0 -1
- /package/{lib/auth/index.d.ts → src/lib/auth/index.ts} +0 -0
- /package/{lib/class-validator/index.d.ts → src/lib/class-validator/index.ts} +0 -0
- /package/{lib/devtools/components/query-devtools/index.d.ts → src/lib/devtools/components/query-devtools/index.ts} +0 -0
- /package/{lib/devtools/pipes/index.d.ts → src/lib/devtools/pipes/index.ts} +0 -0
- /package/{lib/devtools/utils/index.d.ts → src/lib/devtools/utils/index.ts} +0 -0
- /package/{lib/directives/index.d.ts → src/lib/directives/index.ts} +0 -0
- /package/{lib/entity/index.d.ts → src/lib/entity/index.ts} +0 -0
- /package/{lib/gql/index.d.ts → src/lib/gql/index.ts} +0 -0
- /package/{lib/infinite-query/index.d.ts → src/lib/infinite-query/index.ts} +0 -0
- /package/{lib/logger/index.d.ts → src/lib/logger/index.ts} +0 -0
- /package/{lib/query/index.d.ts → src/lib/query/index.ts} +0 -0
- /package/{lib/query-client/index.d.ts → src/lib/query-client/index.ts} +0 -0
- /package/{lib/query-creator/index.d.ts → src/lib/query-creator/index.ts} +0 -0
- /package/{lib/query-form/index.d.ts → src/lib/query-form/index.ts} +0 -0
- /package/{lib/query-store/index.d.ts → src/lib/query-store/index.ts} +0 -0
- /package/{lib/request/index.d.ts → src/lib/request/index.ts} +0 -0
- /package/{lib/symfony/index.d.ts → src/lib/symfony/index.ts} +0 -0
- /package/{lib/utils/index.d.ts → src/lib/utils/index.ts} +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { AuthProvider } from '../auth';
|
|
3
|
+
import { EntityStore } from '../entity';
|
|
4
|
+
import {
|
|
5
|
+
BaseArguments,
|
|
6
|
+
GqlQueryConfig,
|
|
7
|
+
GqlQueryConfigWithoutMethod,
|
|
8
|
+
QueryConfigWithoutMethod,
|
|
9
|
+
RestQueryConfig,
|
|
10
|
+
RouteType,
|
|
11
|
+
} from '../query';
|
|
12
|
+
import { QueryCreator } from '../query-creator';
|
|
13
|
+
import { QueryStore } from '../query-store';
|
|
14
|
+
import { RequestHeaders, RequestHeadersMethodMap } from '../request';
|
|
15
|
+
import { QueryClientConfig } from './query-client.types';
|
|
16
|
+
|
|
17
|
+
export class QueryClient {
|
|
18
|
+
/**
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
readonly _store: QueryStore;
|
|
22
|
+
|
|
23
|
+
get config() {
|
|
24
|
+
return this._clientConfig;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get authProvider() {
|
|
28
|
+
return this._authProvider$.getValue() ?? this._clientConfig.parent?._authProvider$.getValue() ?? null;
|
|
29
|
+
}
|
|
30
|
+
get authProvider$() {
|
|
31
|
+
return this._authProvider$.asObservable() ?? this._clientConfig.parent?._authProvider$.asObservable() ?? null;
|
|
32
|
+
}
|
|
33
|
+
private readonly _authProvider$ = new BehaviorSubject<AuthProvider | null>(null);
|
|
34
|
+
|
|
35
|
+
constructor(private _clientConfig: QueryClientConfig) {
|
|
36
|
+
this._store = new QueryStore({
|
|
37
|
+
enableChangeLogging: _clientConfig.logging?.queryStateChanges,
|
|
38
|
+
enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
|
|
39
|
+
autoRefreshQueriesOnWindowFocus: _clientConfig.request?.autoRefreshQueriesOnWindowFocus ?? true,
|
|
40
|
+
enableSmartPolling: _clientConfig.request?.enableSmartPolling ?? true,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get = <
|
|
45
|
+
Route extends RouteType<Arguments>,
|
|
46
|
+
Response,
|
|
47
|
+
Id,
|
|
48
|
+
Data = Response,
|
|
49
|
+
Arguments extends BaseArguments | undefined = undefined,
|
|
50
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
51
|
+
>(
|
|
52
|
+
queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
|
|
53
|
+
) =>
|
|
54
|
+
this.fetch<Route, Response, Arguments, Id, Store, Data>({
|
|
55
|
+
...queryConfig,
|
|
56
|
+
method: 'GET',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
post = <
|
|
60
|
+
Route extends RouteType<Arguments>,
|
|
61
|
+
Response,
|
|
62
|
+
Id,
|
|
63
|
+
Data = Response,
|
|
64
|
+
Arguments extends BaseArguments | undefined = undefined,
|
|
65
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
66
|
+
>(
|
|
67
|
+
queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
|
|
68
|
+
) =>
|
|
69
|
+
this.fetch<Route, Response, Arguments, Id, Store, Data>({
|
|
70
|
+
...queryConfig,
|
|
71
|
+
method: 'POST',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
put = <
|
|
75
|
+
Route extends RouteType<Arguments>,
|
|
76
|
+
Response,
|
|
77
|
+
Id,
|
|
78
|
+
Data = Response,
|
|
79
|
+
Arguments extends BaseArguments | undefined = undefined,
|
|
80
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
81
|
+
>(
|
|
82
|
+
queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
|
|
83
|
+
) =>
|
|
84
|
+
this.fetch<Route, Response, Arguments, Id, Store, Data>({
|
|
85
|
+
...queryConfig,
|
|
86
|
+
method: 'PUT',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
patch = <
|
|
90
|
+
Route extends RouteType<Arguments>,
|
|
91
|
+
Response,
|
|
92
|
+
Id,
|
|
93
|
+
Data = Response,
|
|
94
|
+
Arguments extends BaseArguments | undefined = undefined,
|
|
95
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
96
|
+
>(
|
|
97
|
+
queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
|
|
98
|
+
) =>
|
|
99
|
+
this.fetch<Route, Response, Arguments, Id, Store, Data>({
|
|
100
|
+
...queryConfig,
|
|
101
|
+
method: 'PATCH',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
delete = <
|
|
105
|
+
Route extends RouteType<Arguments>,
|
|
106
|
+
Response,
|
|
107
|
+
Id,
|
|
108
|
+
Data = Response,
|
|
109
|
+
Arguments extends BaseArguments | undefined = undefined,
|
|
110
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
111
|
+
>(
|
|
112
|
+
queryConfig: QueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
|
|
113
|
+
) =>
|
|
114
|
+
this.fetch<Route, Response, Arguments, Id, Store, Data>({
|
|
115
|
+
...queryConfig,
|
|
116
|
+
method: 'DELETE',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
gqlQuery = <
|
|
120
|
+
Route extends RouteType<Arguments>,
|
|
121
|
+
Response,
|
|
122
|
+
Id,
|
|
123
|
+
Data = Response,
|
|
124
|
+
Arguments extends BaseArguments | undefined = undefined,
|
|
125
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
126
|
+
>(
|
|
127
|
+
queryConfig: GqlQueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
|
|
128
|
+
) =>
|
|
129
|
+
this.fetch<Route, Response, Arguments, Id, Store, Data>({
|
|
130
|
+
...queryConfig,
|
|
131
|
+
method: 'GQL_QUERY',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
gqlMutate = <
|
|
135
|
+
Route extends RouteType<Arguments>,
|
|
136
|
+
Response,
|
|
137
|
+
Id,
|
|
138
|
+
Data = Response,
|
|
139
|
+
Arguments extends BaseArguments | undefined = undefined,
|
|
140
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
141
|
+
>(
|
|
142
|
+
queryConfig: GqlQueryConfigWithoutMethod<Route, Response, Arguments, Store, Data, Id>,
|
|
143
|
+
) =>
|
|
144
|
+
this.fetch<Route, Response, Arguments, Id, Store, Data>({
|
|
145
|
+
...queryConfig,
|
|
146
|
+
method: 'GQL_MUTATE',
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
fetch = <
|
|
150
|
+
Route extends RouteType<Arguments>,
|
|
151
|
+
Response,
|
|
152
|
+
Arguments extends BaseArguments | undefined,
|
|
153
|
+
Id,
|
|
154
|
+
Store extends EntityStore<unknown> = EntityStore<unknown>,
|
|
155
|
+
Data = Response,
|
|
156
|
+
>(
|
|
157
|
+
queryConfig:
|
|
158
|
+
| RestQueryConfig<Route, Response, Arguments, Store, Data, Id>
|
|
159
|
+
| GqlQueryConfig<Route, Response, Arguments, Store, Data, Id>,
|
|
160
|
+
) => new QueryCreator<Arguments, Response, Route, Store, Data, Id>(queryConfig, this, this._store);
|
|
161
|
+
|
|
162
|
+
setAuthProvider = (authProvider: AuthProvider) => {
|
|
163
|
+
if (this.authProvider) {
|
|
164
|
+
this.authProvider?.cleanUp();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this._authProvider$.next(authProvider);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
setDefaultHeaders = (config: {
|
|
171
|
+
headers: RequestHeaders | RequestHeadersMethodMap | null;
|
|
172
|
+
refreshQueriesInUse?: boolean;
|
|
173
|
+
}) => {
|
|
174
|
+
if (!this._clientConfig.request) {
|
|
175
|
+
this._clientConfig.request = {
|
|
176
|
+
headers: config.headers ?? undefined,
|
|
177
|
+
};
|
|
178
|
+
} else {
|
|
179
|
+
this._clientConfig.request.headers = config.headers ?? undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (config.refreshQueriesInUse) {
|
|
183
|
+
this._store.refreshQueriesInUse({ purgeUnused: true, ignoreCacheValidity: true });
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
clearAuthProvider = () => {
|
|
188
|
+
this.authProvider?.cleanUp();
|
|
189
|
+
this._authProvider$.next(null);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
_updateBaseRoute = (route: string) => {
|
|
193
|
+
this._clientConfig.baseRoute = route;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { GqlTransferOption } from '../query/query.types';
|
|
3
|
+
import {
|
|
4
|
+
BuildQueryStringConfig,
|
|
5
|
+
CacheAdapterFn,
|
|
6
|
+
RequestHeaders,
|
|
7
|
+
RequestHeadersMethodMap,
|
|
8
|
+
RequestRetryFn,
|
|
9
|
+
} from '../request';
|
|
10
|
+
import { QueryClient } from './query-client';
|
|
11
|
+
|
|
12
|
+
export interface QueryClientConfig {
|
|
13
|
+
/**
|
|
14
|
+
* The api base route. Should **not** end with a trailing slash.
|
|
15
|
+
* @example 'https://api.example.com'
|
|
16
|
+
*/
|
|
17
|
+
baseRoute: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Logging configuration for debugging.
|
|
21
|
+
*/
|
|
22
|
+
logging?: {
|
|
23
|
+
/**
|
|
24
|
+
* Log all query state changes.
|
|
25
|
+
*/
|
|
26
|
+
queryStateChanges?: boolean;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Log query state garbage collector runs.
|
|
30
|
+
*/
|
|
31
|
+
queryStateGarbageCollector?: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Log if subscriptions are made to queries that are in the `prepared` state.
|
|
35
|
+
* This usually indicates that the `.execute()` method was forgotten.
|
|
36
|
+
*/
|
|
37
|
+
preparedQuerySubscriptions?: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Request options.
|
|
42
|
+
*/
|
|
43
|
+
request?: {
|
|
44
|
+
/**
|
|
45
|
+
* Adapter function used for extracting the time until the response is invalid.
|
|
46
|
+
* The default uses the `cache-control` (`max-age` & `s-maxage`), `age` and `expires` headers.
|
|
47
|
+
* Should return a number in seconds.
|
|
48
|
+
*/
|
|
49
|
+
cacheAdapter?: CacheAdapterFn;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Default headers to be sent with every request.
|
|
53
|
+
* Will be overridden by headers passed to the query.
|
|
54
|
+
* Do not include the `Authorization` header here. Use an `AuthProvider` instead.
|
|
55
|
+
*/
|
|
56
|
+
headers?: RequestHeaders | RequestHeadersMethodMap;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Whether to automatically refresh expired queries when the window regains focus.
|
|
60
|
+
* @default true
|
|
61
|
+
*/
|
|
62
|
+
autoRefreshQueriesOnWindowFocus?: boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether to automatically stop polling queries when the window loses focus.
|
|
66
|
+
* Polling will resume when the window regains focus.
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
enableSmartPolling?: boolean;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A retry function to be used for all queries.
|
|
73
|
+
* @default shouldRetryRequest()
|
|
74
|
+
*/
|
|
75
|
+
retryFn?: RequestRetryFn;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Configuration options for all graphql queries.
|
|
79
|
+
*/
|
|
80
|
+
gql?: {
|
|
81
|
+
/**
|
|
82
|
+
* Determines if the query should be sent via GET or POST.
|
|
83
|
+
* - `GET`: The query will be sent via query parameters.
|
|
84
|
+
* - `POST`: The query will be sent via the body.
|
|
85
|
+
* @default 'POST'
|
|
86
|
+
*/
|
|
87
|
+
transferVia?: GqlTransferOption;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Configuration options for all query strings.
|
|
92
|
+
*/
|
|
93
|
+
queryParams?: BuildQueryStringConfig;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Parent query client.
|
|
98
|
+
* Used for sharing the same auth provider.
|
|
99
|
+
*/
|
|
100
|
+
parent?: QueryClient;
|
|
101
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BaseArguments } from '../query/query.types';
|
|
2
|
+
import { buildQueryCacheKey, shouldCacheQuery } from './query-client.utils';
|
|
3
|
+
|
|
4
|
+
describe('buildQueryCacheKey', () => {
|
|
5
|
+
it('should return a string with a shortened query and variables', () => {
|
|
6
|
+
const route = '/posts';
|
|
7
|
+
const args: BaseArguments = {
|
|
8
|
+
variables: {
|
|
9
|
+
id: 123,
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const expectedCacheKey = '1769813287';
|
|
14
|
+
|
|
15
|
+
const cacheKey = buildQueryCacheKey(route, args);
|
|
16
|
+
|
|
17
|
+
expect(cacheKey).toBe(expectedCacheKey);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('shouldCacheQuery', () => {
|
|
22
|
+
it('should return true for GET requests', () => {
|
|
23
|
+
const method = 'GET';
|
|
24
|
+
|
|
25
|
+
const shouldCache = shouldCacheQuery(method);
|
|
26
|
+
|
|
27
|
+
expect(shouldCache).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return true for OPTIONS requests', () => {
|
|
31
|
+
const method = 'OPTIONS';
|
|
32
|
+
|
|
33
|
+
const shouldCache = shouldCacheQuery(method);
|
|
34
|
+
|
|
35
|
+
expect(shouldCache).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return true for HEAD requests', () => {
|
|
39
|
+
const method = 'HEAD';
|
|
40
|
+
|
|
41
|
+
const shouldCache = shouldCacheQuery(method);
|
|
42
|
+
|
|
43
|
+
expect(shouldCache).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return true for GQL_QUERY requests', () => {
|
|
47
|
+
const method = 'GQL_QUERY';
|
|
48
|
+
|
|
49
|
+
const shouldCache = shouldCacheQuery(method);
|
|
50
|
+
|
|
51
|
+
expect(shouldCache).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return false for other HTTP methods', () => {
|
|
55
|
+
const method = 'POST';
|
|
56
|
+
|
|
57
|
+
const shouldCache = shouldCacheQuery(method);
|
|
58
|
+
|
|
59
|
+
expect(shouldCache).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BaseArguments } from '../query/query.types';
|
|
2
|
+
import { Method } from '../request';
|
|
3
|
+
|
|
4
|
+
export const shouldCacheQuery = (method: Method) => {
|
|
5
|
+
return method === 'GET' || method === 'OPTIONS' || method === 'HEAD' || method === 'GQL_QUERY';
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const buildQueryCacheKey = (route: string, args: BaseArguments | undefined) => {
|
|
9
|
+
const variables = JSON.stringify(args?.variables || {})
|
|
10
|
+
// replace all curly braces with empty string
|
|
11
|
+
.replace(/{|}/g, '')
|
|
12
|
+
// replace new lines and whitespaces with empty string
|
|
13
|
+
.replace(/\s/g, '');
|
|
14
|
+
|
|
15
|
+
const seed = `${route}...${variables}`;
|
|
16
|
+
|
|
17
|
+
let hash = 0;
|
|
18
|
+
|
|
19
|
+
for (const char of seed) {
|
|
20
|
+
hash = (Math.imul(31, hash) + char.charCodeAt(0)) << 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Force positive number hash.
|
|
24
|
+
// 2147483647 = equivalent of Integer.MAX_VALUE.
|
|
25
|
+
hash += 2147483647 + 1;
|
|
26
|
+
|
|
27
|
+
return hash.toString();
|
|
28
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { assertInInjectionContext, signal } from '@angular/core';
|
|
2
|
+
import { toObservable } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { BehaviorSubject } from 'rxjs';
|
|
4
|
+
import { EntityStore } from '../entity';
|
|
5
|
+
import { Query, computeQueryQueryParams } from '../query';
|
|
6
|
+
import { QueryClient, buildQueryCacheKey, shouldCacheQuery } from '../query-client';
|
|
7
|
+
import { QueryStore } from '../query-store';
|
|
8
|
+
import {
|
|
9
|
+
AnyRoute,
|
|
10
|
+
BaseArguments,
|
|
11
|
+
GqlQueryConfig,
|
|
12
|
+
RestQueryConfig,
|
|
13
|
+
RouteType,
|
|
14
|
+
WithConfig,
|
|
15
|
+
WithHeaders,
|
|
16
|
+
WithMock,
|
|
17
|
+
} from '../query/query.types';
|
|
18
|
+
import { buildRoute } from '../request';
|
|
19
|
+
import { QueryContainerConfig, addQueryContainerHandling } from '../utils';
|
|
20
|
+
import { QueryPrepareFn } from './query-creator.types';
|
|
21
|
+
|
|
22
|
+
export class QueryCreator<
|
|
23
|
+
Arguments extends BaseArguments | undefined,
|
|
24
|
+
Response,
|
|
25
|
+
Route extends RouteType<Arguments>,
|
|
26
|
+
Store extends EntityStore<unknown>,
|
|
27
|
+
Data,
|
|
28
|
+
Id,
|
|
29
|
+
> {
|
|
30
|
+
constructor(
|
|
31
|
+
private _queryConfig:
|
|
32
|
+
| RestQueryConfig<Route, Response, Arguments, Store, Data, Id>
|
|
33
|
+
| GqlQueryConfig<Route, Response, Arguments, Store, Data, Id>,
|
|
34
|
+
private _client: QueryClient,
|
|
35
|
+
private _store: QueryStore,
|
|
36
|
+
) {}
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
prepare: QueryPrepareFn<Arguments, Response, Route, Store, Data, Id> = (
|
|
41
|
+
args?: Arguments & WithHeaders & WithConfig & WithMock<Response>,
|
|
42
|
+
) => {
|
|
43
|
+
const route = buildRoute({
|
|
44
|
+
base: this._client.config.baseRoute,
|
|
45
|
+
route: this._queryConfig.route as AnyRoute,
|
|
46
|
+
pathParams: args?.pathParams,
|
|
47
|
+
queryParams: computeQueryQueryParams({ config: this._queryConfig, client: this._client, args }),
|
|
48
|
+
queryParamConfig: this._client.config.request?.queryParams,
|
|
49
|
+
}) as Route;
|
|
50
|
+
|
|
51
|
+
const cacheKey = (args?.config?.queryStoreCacheKey ?? '') + buildQueryCacheKey(route as string, args);
|
|
52
|
+
|
|
53
|
+
if (shouldCacheQuery(this._queryConfig.method) && !args?.config?.skipQueryStore) {
|
|
54
|
+
const existingQuery = this._store.get<Query<Response, Arguments, Route, Store, Data, Id>>(cacheKey);
|
|
55
|
+
|
|
56
|
+
if (existingQuery) {
|
|
57
|
+
return existingQuery;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const query = new Query<Response, Arguments, Route, Store, Data, Id>(
|
|
62
|
+
this._client,
|
|
63
|
+
this._queryConfig,
|
|
64
|
+
route,
|
|
65
|
+
args ?? ({} as Arguments),
|
|
66
|
+
cacheKey,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (shouldCacheQuery(this._queryConfig.method) && !args?.config?.skipQueryStore) {
|
|
70
|
+
this._store.add(cacheKey, query);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this._store._dispatchQueryCreated(query);
|
|
74
|
+
|
|
75
|
+
return query;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
createSubject = (initialValue?: ReturnType<typeof this.prepare> | null, config?: QueryContainerConfig) => {
|
|
79
|
+
assertInInjectionContext(this.createSubject);
|
|
80
|
+
|
|
81
|
+
const subject = new BehaviorSubject<ReturnType<typeof this.prepare> | null>(initialValue ?? null);
|
|
82
|
+
|
|
83
|
+
addQueryContainerHandling(subject, () => subject.getValue(), config);
|
|
84
|
+
|
|
85
|
+
return subject;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
createSignal = (initialValue?: ReturnType<typeof this.prepare> | null, config?: QueryContainerConfig) => {
|
|
89
|
+
assertInInjectionContext(this.createSignal);
|
|
90
|
+
|
|
91
|
+
const _signal = signal<ReturnType<typeof this.prepare> | null>(initialValue ?? null);
|
|
92
|
+
|
|
93
|
+
addQueryContainerHandling(toObservable(_signal), () => _signal(), config);
|
|
94
|
+
|
|
95
|
+
return _signal;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @deprecated Use `myQuery.createSubject()` or `myQuery.createSignal()` instead. Will be removed in v6.
|
|
100
|
+
*/
|
|
101
|
+
behaviorSubject = (initialValue?: ReturnType<typeof this.prepare> | null) =>
|
|
102
|
+
new BehaviorSubject<ReturnType<typeof this.prepare> | null>(initialValue ?? null);
|
|
103
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { EntityStore } from '../entity';
|
|
3
|
+
import { Query } from '../query/query';
|
|
4
|
+
import {
|
|
5
|
+
AnyQuery,
|
|
6
|
+
BaseArguments,
|
|
7
|
+
EmptyObject,
|
|
8
|
+
RouteType,
|
|
9
|
+
WithConfig,
|
|
10
|
+
WithHeaders,
|
|
11
|
+
WithMock,
|
|
12
|
+
} from '../query/query.types';
|
|
13
|
+
import { QueryCreator } from './query-creator';
|
|
14
|
+
|
|
15
|
+
export type QueryPrepareFn<
|
|
16
|
+
Arguments extends BaseArguments | undefined,
|
|
17
|
+
Response,
|
|
18
|
+
Route extends RouteType<Arguments>,
|
|
19
|
+
Store extends EntityStore<unknown>,
|
|
20
|
+
Data,
|
|
21
|
+
Id,
|
|
22
|
+
> = Arguments extends BaseArguments
|
|
23
|
+
? (
|
|
24
|
+
args: Arguments & WithHeaders & WithConfig & WithMock<Response>,
|
|
25
|
+
) => Query<Response, Arguments, Route, Store, Data, Id>
|
|
26
|
+
: (
|
|
27
|
+
args?: (Arguments extends EmptyObject ? Arguments : EmptyObject) & WithHeaders & WithConfig & WithMock<Response>,
|
|
28
|
+
) => Query<Response, Arguments, Route, Store, Data, Id>;
|
|
29
|
+
|
|
30
|
+
export type AnyQueryCreator = QueryCreator<any, any, any, any, any, any>;
|
|
31
|
+
|
|
32
|
+
export type QueryArgsOf<T extends AnyQueryCreator | AnyQuery | null> =
|
|
33
|
+
T extends QueryCreator<infer Args, any, any, any, any, any>
|
|
34
|
+
? Args
|
|
35
|
+
: T extends Query<any, infer Args, any, any, any, any>
|
|
36
|
+
? Args
|
|
37
|
+
: never;
|
|
38
|
+
|
|
39
|
+
export type QueryResponseOf<T extends AnyQueryCreator | AnyQuery | null> =
|
|
40
|
+
T extends QueryCreator<any, infer Response, any, any, any, any>
|
|
41
|
+
? Response
|
|
42
|
+
: T extends Query<infer Response, any, any, any, any, any>
|
|
43
|
+
? Response
|
|
44
|
+
: never;
|
|
45
|
+
|
|
46
|
+
export type QueryRouteOf<T extends AnyQueryCreator | AnyQuery | null> =
|
|
47
|
+
T extends QueryCreator<any, any, infer Route, any, any, any>
|
|
48
|
+
? Route
|
|
49
|
+
: T extends Query<any, any, infer Route, any, any, any>
|
|
50
|
+
? Route
|
|
51
|
+
: never;
|
|
52
|
+
|
|
53
|
+
export type QueryStoreOf<T extends AnyQueryCreator | AnyQuery | null> =
|
|
54
|
+
T extends QueryCreator<any, any, any, infer Store, any, any>
|
|
55
|
+
? Store
|
|
56
|
+
: T extends Query<any, any, any, infer Store, any, any>
|
|
57
|
+
? Store
|
|
58
|
+
: never;
|
|
59
|
+
|
|
60
|
+
export type QueryDataOf<T extends AnyQueryCreator | AnyQuery | null> =
|
|
61
|
+
T extends QueryCreator<any, any, any, any, infer Data, any>
|
|
62
|
+
? Data
|
|
63
|
+
: T extends Query<any, any, any, any, infer Data, any>
|
|
64
|
+
? Data
|
|
65
|
+
: never;
|
|
66
|
+
|
|
67
|
+
export type QueryStoreIdOf<T extends AnyQueryCreator | AnyQuery | null> =
|
|
68
|
+
T extends QueryCreator<any, any, any, any, any, infer StoreId>
|
|
69
|
+
? StoreId
|
|
70
|
+
: T extends Query<any, any, any, any, any, infer StoreId>
|
|
71
|
+
? StoreId
|
|
72
|
+
: never;
|
|
73
|
+
|
|
74
|
+
export type ConstructQuery<T extends AnyQueryCreator> = ReturnType<T['prepare']>;
|