@cratis/arc 18.4.2 → 18.5.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/commands/for_Command/given/a_command.ts +4 -5
- package/commands/for_Command/when_executing/with_custom_http_headers.ts +4 -1
- package/commands/for_Command/when_executing/with_origin_and_api_base_path.ts +5 -2
- package/commands/for_Command/when_executing/with_route_parameters.ts +4 -2
- package/dist/cjs/helpers/fetchHelper.d.ts +7 -0
- package/dist/cjs/helpers/fetchHelper.d.ts.map +1 -0
- package/dist/cjs/identity/IIdentityProvider.d.ts +2 -1
- package/dist/cjs/identity/IIdentityProvider.d.ts.map +1 -1
- package/dist/cjs/identity/IIdentityProvider.js.map +1 -1
- package/dist/cjs/identity/IdentityProvider.d.ts +4 -3
- package/dist/cjs/identity/IdentityProvider.d.ts.map +1 -1
- package/dist/cjs/identity/IdentityProvider.js +12 -9
- package/dist/cjs/identity/IdentityProvider.js.map +1 -1
- package/dist/cjs/queries/IObservableQueryConnection.d.ts +2 -0
- package/dist/cjs/queries/IObservableQueryConnection.d.ts.map +1 -1
- package/dist/cjs/queries/NullObservableQueryConnection.d.ts +2 -0
- package/dist/cjs/queries/NullObservableQueryConnection.d.ts.map +1 -1
- package/dist/cjs/queries/NullObservableQueryConnection.js +6 -0
- package/dist/cjs/queries/NullObservableQueryConnection.js.map +1 -1
- package/dist/cjs/queries/ObservableQueryConnection.d.ts +14 -1
- package/dist/cjs/queries/ObservableQueryConnection.d.ts.map +1 -1
- package/dist/cjs/queries/ObservableQueryConnection.js +73 -2
- package/dist/cjs/queries/ObservableQueryConnection.js.map +1 -1
- package/dist/cjs/queries/WebSocketMessage.d.ts +11 -0
- package/dist/cjs/queries/WebSocketMessage.d.ts.map +1 -0
- package/dist/cjs/queries/WebSocketMessage.js +9 -0
- package/dist/cjs/queries/WebSocketMessage.js.map +1 -0
- package/dist/cjs/queries/index.d.ts +1 -0
- package/dist/cjs/queries/index.d.ts.map +1 -1
- package/dist/cjs/queries/index.js +5 -0
- package/dist/cjs/queries/index.js.map +1 -1
- package/dist/esm/helpers/fetchHelper.d.ts +7 -0
- package/dist/esm/helpers/fetchHelper.d.ts.map +1 -0
- package/dist/esm/helpers/fetchHelper.js +22 -0
- package/dist/esm/helpers/fetchHelper.js.map +1 -0
- package/dist/esm/identity/IIdentityProvider.d.ts +2 -1
- package/dist/esm/identity/IIdentityProvider.d.ts.map +1 -1
- package/dist/esm/identity/IIdentityProvider.js.map +1 -1
- package/dist/esm/identity/IdentityProvider.d.ts +4 -3
- package/dist/esm/identity/IdentityProvider.d.ts.map +1 -1
- package/dist/esm/identity/IdentityProvider.js +12 -9
- package/dist/esm/identity/IdentityProvider.js.map +1 -1
- package/dist/esm/queries/IObservableQueryConnection.d.ts +2 -0
- package/dist/esm/queries/IObservableQueryConnection.d.ts.map +1 -1
- package/dist/esm/queries/NullObservableQueryConnection.d.ts +2 -0
- package/dist/esm/queries/NullObservableQueryConnection.d.ts.map +1 -1
- package/dist/esm/queries/NullObservableQueryConnection.js +6 -0
- package/dist/esm/queries/NullObservableQueryConnection.js.map +1 -1
- package/dist/esm/queries/ObservableQueryConnection.d.ts +14 -1
- package/dist/esm/queries/ObservableQueryConnection.d.ts.map +1 -1
- package/dist/esm/queries/ObservableQueryConnection.js +73 -2
- package/dist/esm/queries/ObservableQueryConnection.js.map +1 -1
- package/dist/esm/queries/WebSocketMessage.d.ts +11 -0
- package/dist/esm/queries/WebSocketMessage.d.ts.map +1 -0
- package/dist/esm/queries/WebSocketMessage.js +9 -0
- package/dist/esm/queries/WebSocketMessage.js.map +1 -0
- package/dist/esm/queries/index.d.ts +1 -0
- package/dist/esm/queries/index.d.ts.map +1 -1
- package/dist/esm/queries/index.js +1 -0
- package/dist/esm/queries/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/helpers/fetchHelper.ts +30 -0
- package/identity/IIdentityProvider.ts +4 -1
- package/identity/IdentityProvider.ts +14 -9
- package/identity/for_IdentityProvider/given/an_identity_provider.ts +4 -5
- package/identity/for_IdentityProvider/when_getting_current/with_type_safe_details.ts +43 -0
- package/identity/for_IdentityProvider/when_refreshing/with_type_safe_details.ts +42 -0
- package/package.json +1 -1
- package/queries/IObservableQueryConnection.ts +10 -0
- package/queries/NullObservableQueryConnection.ts +10 -0
- package/queries/ObservableQueryConnection.ts +93 -2
- package/queries/WebSocketMessage.ts +44 -0
- package/queries/for_ObservableQueryConnection/given/an_observable_query_connection.ts +27 -0
- package/queries/for_ObservableQueryConnection/when_constructing.ts +15 -0
- package/queries/for_ObservableQueryFor/when_performing/with_enumerable_query.ts +5 -2
- package/queries/for_ObservableQueryFor/when_performing/with_paging.ts +5 -2
- package/queries/for_ObservableQueryFor/when_performing/with_parameter_descriptor_values.ts +5 -2
- package/queries/for_ObservableQueryFor/when_performing/with_partial_parameter_descriptor_values.ts +5 -2
- package/queries/for_ObservableQueryFor/when_performing/with_route_parameters_and_unused_parameters.ts +5 -2
- package/queries/for_ObservableQueryFor/when_performing/with_sorting.ts +5 -2
- package/queries/for_ObservableQueryFor/when_performing/with_valid_arguments.ts +7 -2
- package/queries/for_QueryFor/when_performing/with_abort_controller.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_enumerable_query.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_fetch_error.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_json_parse_error.ts +6 -3
- package/queries/for_QueryFor/when_performing/with_paging.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_parameter_descriptor_values.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_partial_parameter_descriptor_values.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_query_without_required_parameters.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_route_parameters_and_unused_parameters.ts +5 -2
- package/queries/for_QueryFor/when_performing/with_sorting.ts +7 -3
- package/queries/for_QueryFor/when_performing/with_valid_arguments.ts +6 -3
- package/queries/for_WebSocketMessage/when_creating_messages.ts +67 -0
- package/queries/index.ts +2 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { Guid } from '@cratis/fundamentals';
|
|
5
|
+
import { IdentityProvider } from '../../IdentityProvider';
|
|
6
|
+
import { an_identity_provider } from '../given/an_identity_provider';
|
|
7
|
+
import { given } from '../../../given';
|
|
8
|
+
|
|
9
|
+
class TestDetails {
|
|
10
|
+
userId: Guid = Guid.empty;
|
|
11
|
+
role: string = '';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('when refreshing with type safe details', given(an_identity_provider, context => {
|
|
15
|
+
let result: { id: string; name: string; details: TestDetails };
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
const testGuid = Guid.create();
|
|
19
|
+
context.fetchStub.resolves({
|
|
20
|
+
ok: true,
|
|
21
|
+
json: async () => ({
|
|
22
|
+
id: 'test-user-id',
|
|
23
|
+
name: 'Test User',
|
|
24
|
+
details: {
|
|
25
|
+
userId: testGuid.toString(),
|
|
26
|
+
role: 'admin'
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
} as Response);
|
|
30
|
+
|
|
31
|
+
const identity = await IdentityProvider.refresh(TestDetails);
|
|
32
|
+
result = {
|
|
33
|
+
id: identity.id,
|
|
34
|
+
name: identity.name,
|
|
35
|
+
details: identity.details
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should deserialize details with proper types', () => {
|
|
40
|
+
result.details.userId.should.be.instanceOf(Guid);
|
|
41
|
+
});
|
|
42
|
+
}));
|
package/package.json
CHANGED
|
@@ -7,6 +7,16 @@ import { DataReceived } from './ObservableQueryConnection';
|
|
|
7
7
|
* Defines a connection for observable queries.
|
|
8
8
|
*/
|
|
9
9
|
export interface IObservableQueryConnection<TDataType> {
|
|
10
|
+
/**
|
|
11
|
+
* Gets the latency of the last ping/pong sequence in milliseconds.
|
|
12
|
+
*/
|
|
13
|
+
readonly lastPingLatency: number;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Gets the average latency since the connection started in milliseconds.
|
|
17
|
+
*/
|
|
18
|
+
readonly averageLatency: number;
|
|
19
|
+
|
|
10
20
|
/**
|
|
11
21
|
* Connect to a specific route.
|
|
12
22
|
* @param {DataReceived<TDataType> dataReceived Callback that will receive the data.
|
|
@@ -17,6 +17,16 @@ export class NullObservableQueryConnection<TDataType> implements IObservableQuer
|
|
|
17
17
|
constructor(readonly defaultValue: TDataType) {
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/** @inheritdoc */
|
|
21
|
+
get lastPingLatency(): number {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** @inheritdoc */
|
|
26
|
+
get averageLatency(): number {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
/** @inheritdoc */
|
|
21
31
|
connect(dataReceived: DataReceived<TDataType>) {
|
|
22
32
|
dataReceived(QueryResult.empty(this.defaultValue));
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { Globals } from '../Globals';
|
|
5
5
|
import { IObservableQueryConnection } from './IObservableQueryConnection';
|
|
6
6
|
import { QueryResult } from './QueryResult';
|
|
7
|
+
import { WebSocketMessage, WebSocketMessageType } from './WebSocketMessage';
|
|
7
8
|
|
|
8
9
|
export type DataReceived<TDataType> = (data: QueryResult<TDataType>) => void;
|
|
9
10
|
|
|
@@ -15,12 +16,21 @@ export class ObservableQueryConnection<TDataType> implements IObservableQueryCon
|
|
|
15
16
|
private _socket!: WebSocket;
|
|
16
17
|
private _disconnected = false;
|
|
17
18
|
private _url: string;
|
|
19
|
+
private _pingInterval?: ReturnType<typeof setInterval>;
|
|
20
|
+
private _pingIntervalMs: number = 10000;
|
|
21
|
+
private _lastPingSentTime?: number;
|
|
22
|
+
private _lastPongLatency: number = 0;
|
|
23
|
+
private _latencySamples: number[] = [];
|
|
24
|
+
private _connectionStartTime?: number;
|
|
18
25
|
|
|
19
26
|
/**
|
|
20
27
|
* Initializes a new instance of the {@link ObservableQueryConnection<TDataType>} class.
|
|
21
28
|
* @param {Url} url The fully qualified Url.
|
|
29
|
+
* @param {string} _microservice The microservice name.
|
|
30
|
+
* @param {number} pingIntervalMs The ping interval in milliseconds (default: 10000).
|
|
22
31
|
*/
|
|
23
|
-
constructor(url: URL, private readonly _microservice: string) {
|
|
32
|
+
constructor(url: URL, private readonly _microservice: string, pingIntervalMs: number = 10000) {
|
|
33
|
+
this._pingIntervalMs = pingIntervalMs;
|
|
24
34
|
const secure = url.protocol?.indexOf('https') === 0 || false;
|
|
25
35
|
|
|
26
36
|
this._url = `${secure ? 'wss' : 'ws'}://${url.host}${url.pathname}${url.search}`;
|
|
@@ -41,6 +51,24 @@ export class ObservableQueryConnection<TDataType> implements IObservableQueryCon
|
|
|
41
51
|
this.disconnect();
|
|
42
52
|
}
|
|
43
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Gets the latency of the last ping/pong sequence in milliseconds.
|
|
56
|
+
*/
|
|
57
|
+
get lastPingLatency(): number {
|
|
58
|
+
return this._lastPongLatency;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Gets the average latency since the connection started in milliseconds.
|
|
63
|
+
*/
|
|
64
|
+
get averageLatency(): number {
|
|
65
|
+
if (this._latencySamples.length === 0) {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
const sum = this._latencySamples.reduce((acc, val) => acc + val, 0);
|
|
69
|
+
return sum / this._latencySamples.length;
|
|
70
|
+
}
|
|
71
|
+
|
|
44
72
|
/** @inheritdoc */
|
|
45
73
|
connect(dataReceived: DataReceived<TDataType>, queryArguments?: object) {
|
|
46
74
|
let url = this._url;
|
|
@@ -80,15 +108,19 @@ export class ObservableQueryConnection<TDataType> implements IObservableQueryCon
|
|
|
80
108
|
console.log(`Connection for '${url}' established`);
|
|
81
109
|
timeToWait = 500;
|
|
82
110
|
currentAttempt = 0;
|
|
111
|
+
this._connectionStartTime = Date.now();
|
|
112
|
+
this.startPinging();
|
|
83
113
|
};
|
|
84
114
|
this._socket.onclose = () => {
|
|
85
115
|
if (this._disconnected) return;
|
|
86
116
|
console.log(`Unexpected connection closed for route '${url}'`);
|
|
117
|
+
this.stopPinging();
|
|
87
118
|
retry();
|
|
88
119
|
};
|
|
89
120
|
this._socket.onerror = (error) => {
|
|
90
121
|
if (this._disconnected) return;
|
|
91
122
|
console.log(`Error with connection for '${url}' - ${error}`);
|
|
123
|
+
this.stopPinging();
|
|
92
124
|
retry();
|
|
93
125
|
};
|
|
94
126
|
this._socket.onmessage = (ev) => {
|
|
@@ -96,7 +128,7 @@ export class ObservableQueryConnection<TDataType> implements IObservableQueryCon
|
|
|
96
128
|
console.log('Received message after closing connection');
|
|
97
129
|
return;
|
|
98
130
|
}
|
|
99
|
-
|
|
131
|
+
this.handleMessage(ev.data, dataReceived);
|
|
100
132
|
};
|
|
101
133
|
};
|
|
102
134
|
|
|
@@ -111,8 +143,67 @@ export class ObservableQueryConnection<TDataType> implements IObservableQueryCon
|
|
|
111
143
|
}
|
|
112
144
|
console.log(`Disconnecting '${this._url}'`);
|
|
113
145
|
this._disconnected = true;
|
|
146
|
+
this.stopPinging();
|
|
114
147
|
this._socket?.close();
|
|
115
148
|
console.log(`Connection for '${this._url}' closed`);
|
|
116
149
|
this._socket = undefined!;
|
|
117
150
|
}
|
|
151
|
+
|
|
152
|
+
private startPinging() {
|
|
153
|
+
this.stopPinging();
|
|
154
|
+
this._pingInterval = setInterval(() => {
|
|
155
|
+
this.sendPing();
|
|
156
|
+
}, this._pingIntervalMs);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private stopPinging() {
|
|
160
|
+
if (this._pingInterval) {
|
|
161
|
+
clearInterval(this._pingInterval);
|
|
162
|
+
this._pingInterval = undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private sendPing() {
|
|
167
|
+
if (this._disconnected || !this._socket || this._socket.readyState !== WebSocket.OPEN) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this._lastPingSentTime = Date.now();
|
|
172
|
+
const pingMessage: WebSocketMessage = {
|
|
173
|
+
type: WebSocketMessageType.Ping,
|
|
174
|
+
timestamp: this._lastPingSentTime
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
this._socket.send(JSON.stringify(pingMessage));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private handleMessage(rawData: string, dataReceived: DataReceived<TDataType>) {
|
|
181
|
+
try {
|
|
182
|
+
const message = JSON.parse(rawData) as WebSocketMessage;
|
|
183
|
+
|
|
184
|
+
// Handle messages based on type
|
|
185
|
+
if (message.type === WebSocketMessageType.Pong) {
|
|
186
|
+
this.handlePong(message);
|
|
187
|
+
} else if (message.type === WebSocketMessageType.Data || !message.type) {
|
|
188
|
+
// For backward compatibility, treat messages without a type as data messages
|
|
189
|
+
const data = message.data ?? message;
|
|
190
|
+
dataReceived(data as QueryResult<TDataType>);
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error('Error parsing WebSocket message:', error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private handlePong(message: WebSocketMessage) {
|
|
198
|
+
if (message.timestamp && this._lastPingSentTime) {
|
|
199
|
+
const latency = Date.now() - message.timestamp;
|
|
200
|
+
this._lastPongLatency = latency;
|
|
201
|
+
this._latencySamples.push(latency);
|
|
202
|
+
|
|
203
|
+
// Keep only the last 100 samples for average calculation
|
|
204
|
+
if (this._latencySamples.length > 100) {
|
|
205
|
+
this._latencySamples.shift();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
118
209
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents the type of WebSocket message.
|
|
6
|
+
*/
|
|
7
|
+
export enum WebSocketMessageType {
|
|
8
|
+
/**
|
|
9
|
+
* A data message containing query results.
|
|
10
|
+
*/
|
|
11
|
+
Data = 'Data',
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A ping message sent from client to server.
|
|
15
|
+
*/
|
|
16
|
+
Ping = 'Ping',
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A pong message sent from server to client in response to a ping.
|
|
20
|
+
*/
|
|
21
|
+
Pong = 'Pong'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Represents a WebSocket message envelope.
|
|
26
|
+
*/
|
|
27
|
+
export type WebSocketMessage = {
|
|
28
|
+
/**
|
|
29
|
+
* The type of message.
|
|
30
|
+
*/
|
|
31
|
+
type: WebSocketMessageType;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The timestamp when the message was sent (for ping/pong latency tracking).
|
|
35
|
+
*/
|
|
36
|
+
timestamp?: number;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The payload data (for data messages).
|
|
40
|
+
*/
|
|
41
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
42
|
+
data?: any;
|
|
43
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
44
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { ObservableQueryConnection } from '../../ObservableQueryConnection';
|
|
5
|
+
import * as sinon from 'sinon';
|
|
6
|
+
|
|
7
|
+
export class an_observable_query_connection {
|
|
8
|
+
connection: ObservableQueryConnection<string>;
|
|
9
|
+
url: URL;
|
|
10
|
+
microservice: string;
|
|
11
|
+
mockWebSocket: sinon.SinonStubbedInstance<WebSocket>;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.url = new URL('https://example.com/api/test');
|
|
15
|
+
this.microservice = 'test-microservice';
|
|
16
|
+
|
|
17
|
+
// Stub the WebSocket constructor
|
|
18
|
+
this.mockWebSocket = sinon.createStubInstance(WebSocket);
|
|
19
|
+
Object.defineProperty(this.mockWebSocket, 'readyState', {
|
|
20
|
+
value: WebSocket.OPEN,
|
|
21
|
+
writable: false
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Create connection with a short ping interval for testing (100ms)
|
|
25
|
+
this.connection = new ObservableQueryConnection<string>(this.url, this.microservice, 100);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { an_observable_query_connection } from './given/an_observable_query_connection';
|
|
5
|
+
import { given } from '../../given';
|
|
6
|
+
|
|
7
|
+
describe('when constructing', given(an_observable_query_connection, context => {
|
|
8
|
+
it('should have zero last ping latency', () => {
|
|
9
|
+
context.connection.lastPingLatency.should.equal(0);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should have zero average latency', () => {
|
|
13
|
+
context.connection.averageLatency.should.equal(0);
|
|
14
|
+
});
|
|
15
|
+
}));
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import { an_observable_query_for } from '../given/an_observable_query_for';
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
import * as sinon from 'sinon';
|
|
7
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
7
8
|
import { QueryResult } from '../../QueryResult';
|
|
8
9
|
|
|
9
10
|
describe('when performing with enumerable query', given(an_observable_query_for, context => {
|
|
10
11
|
let result: QueryResult<string[]>;
|
|
11
12
|
let fetchStub: sinon.SinonStub;
|
|
13
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
12
14
|
const mockResponse = {
|
|
13
15
|
data: ['item1', 'item2', 'item3'],
|
|
14
16
|
isSuccess: true,
|
|
@@ -27,7 +29,8 @@ describe('when performing with enumerable query', given(an_observable_query_for,
|
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
beforeEach(async () => {
|
|
30
|
-
|
|
32
|
+
fetchHelper = createFetchHelper();
|
|
33
|
+
fetchStub = fetchHelper.stubFetch();
|
|
31
34
|
fetchStub.resolves({
|
|
32
35
|
json: sinon.stub().resolves(mockResponse),
|
|
33
36
|
ok: true,
|
|
@@ -41,7 +44,7 @@ describe('when performing with enumerable query', given(an_observable_query_for,
|
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
afterEach(() => {
|
|
44
|
-
|
|
47
|
+
fetchHelper.restore();
|
|
45
48
|
});
|
|
46
49
|
|
|
47
50
|
it('should return successful result', () => {
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
import { an_observable_query_for } from '../given/an_observable_query_for';
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
import * as sinon from 'sinon';
|
|
7
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
7
8
|
import { Paging } from '../../Paging';
|
|
8
9
|
|
|
9
10
|
describe('when performing with paging', given(an_observable_query_for, context => {
|
|
10
11
|
let fetchStub: sinon.SinonStub;
|
|
12
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
11
13
|
const mockResponse = {
|
|
12
14
|
data: 'test-result',
|
|
13
15
|
isSuccess: true,
|
|
@@ -30,7 +32,8 @@ describe('when performing with paging', given(an_observable_query_for, context =
|
|
|
30
32
|
if ((globalThis.fetch as sinon.SinonStub)?.restore) {
|
|
31
33
|
(globalThis.fetch as sinon.SinonStub).restore();
|
|
32
34
|
}
|
|
33
|
-
|
|
35
|
+
fetchHelper = createFetchHelper();
|
|
36
|
+
fetchStub = fetchHelper.stubFetch();
|
|
34
37
|
fetchStub.resolves({
|
|
35
38
|
json: sinon.stub().resolves(mockResponse),
|
|
36
39
|
ok: true,
|
|
@@ -44,7 +47,7 @@ describe('when performing with paging', given(an_observable_query_for, context =
|
|
|
44
47
|
});
|
|
45
48
|
|
|
46
49
|
afterEach(() => {
|
|
47
|
-
|
|
50
|
+
fetchHelper.restore();
|
|
48
51
|
});
|
|
49
52
|
|
|
50
53
|
it('should include page parameter in URL', () => {
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import { an_observable_query_for } from '../given/an_observable_query_for';
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
import * as sinon from 'sinon';
|
|
7
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
7
8
|
import { QueryResult } from '../../QueryResult';
|
|
8
9
|
|
|
9
10
|
describe('with parameter descriptor values', given(an_observable_query_for, context => {
|
|
10
11
|
let result: QueryResult<string>;
|
|
11
12
|
let fetchStub: sinon.SinonStub;
|
|
13
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
12
14
|
const mockResponse = {
|
|
13
15
|
data: 'test-result',
|
|
14
16
|
isSuccess: true,
|
|
@@ -27,7 +29,8 @@ describe('with parameter descriptor values', given(an_observable_query_for, cont
|
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
beforeEach(async () => {
|
|
30
|
-
|
|
32
|
+
fetchHelper = createFetchHelper();
|
|
33
|
+
fetchStub = fetchHelper.stubFetch();
|
|
31
34
|
fetchStub.resolves({
|
|
32
35
|
json: sinon.stub().resolves(mockResponse),
|
|
33
36
|
ok: true,
|
|
@@ -45,7 +48,7 @@ describe('with parameter descriptor values', given(an_observable_query_for, cont
|
|
|
45
48
|
});
|
|
46
49
|
|
|
47
50
|
afterEach(() => {
|
|
48
|
-
|
|
51
|
+
fetchHelper.restore();
|
|
49
52
|
});
|
|
50
53
|
|
|
51
54
|
it('should return successful result', () => {
|
package/queries/for_ObservableQueryFor/when_performing/with_partial_parameter_descriptor_values.ts
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import { an_observable_query_for } from '../given/an_observable_query_for';
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
import * as sinon from 'sinon';
|
|
7
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
7
8
|
import { QueryResult } from '../../QueryResult';
|
|
8
9
|
|
|
9
10
|
describe('with partial parameter descriptor values', given(an_observable_query_for, context => {
|
|
10
11
|
let result: QueryResult<string>;
|
|
11
12
|
let fetchStub: sinon.SinonStub;
|
|
13
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
12
14
|
const mockResponse = {
|
|
13
15
|
data: 'test-result',
|
|
14
16
|
isSuccess: true,
|
|
@@ -27,7 +29,8 @@ describe('with partial parameter descriptor values', given(an_observable_query_f
|
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
beforeEach(async () => {
|
|
30
|
-
|
|
32
|
+
fetchHelper = createFetchHelper();
|
|
33
|
+
fetchStub = fetchHelper.stubFetch();
|
|
31
34
|
fetchStub.resolves({
|
|
32
35
|
json: sinon.stub().resolves(mockResponse),
|
|
33
36
|
ok: true,
|
|
@@ -45,7 +48,7 @@ describe('with partial parameter descriptor values', given(an_observable_query_f
|
|
|
45
48
|
});
|
|
46
49
|
|
|
47
50
|
afterEach(() => {
|
|
48
|
-
|
|
51
|
+
fetchHelper.restore();
|
|
49
52
|
});
|
|
50
53
|
|
|
51
54
|
it('should return successful result', () => {
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
import { an_observable_query_for } from '../given/an_observable_query_for';
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
import * as sinon from 'sinon';
|
|
7
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
7
8
|
import { QueryResult } from '../../QueryResult';
|
|
8
9
|
import { expect } from 'chai';
|
|
9
10
|
|
|
10
11
|
describe('when performing with route parameters and unused parameters', given(an_observable_query_for, context => {
|
|
11
12
|
let result: QueryResult<string>;
|
|
12
13
|
let fetchStub: sinon.SinonStub;
|
|
14
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
13
15
|
const mockResponse = {
|
|
14
16
|
data: 'test-result',
|
|
15
17
|
isSuccess: true,
|
|
@@ -28,7 +30,8 @@ describe('when performing with route parameters and unused parameters', given(an
|
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
beforeEach(async () => {
|
|
31
|
-
|
|
33
|
+
fetchHelper = createFetchHelper();
|
|
34
|
+
fetchStub = fetchHelper.stubFetch();
|
|
32
35
|
fetchStub.resolves({
|
|
33
36
|
json: sinon.stub().resolves(mockResponse),
|
|
34
37
|
ok: true,
|
|
@@ -43,7 +46,7 @@ describe('when performing with route parameters and unused parameters', given(an
|
|
|
43
46
|
});
|
|
44
47
|
|
|
45
48
|
afterEach(() => {
|
|
46
|
-
|
|
49
|
+
fetchHelper.restore();
|
|
47
50
|
});
|
|
48
51
|
|
|
49
52
|
it('should return successful result', () => {
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import { an_observable_query_for } from '../given/an_observable_query_for';
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
import * as sinon from 'sinon';
|
|
7
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
7
8
|
import { Sorting } from '../../Sorting';
|
|
8
9
|
import { SortDirection } from '../../SortDirection';
|
|
9
10
|
|
|
10
11
|
describe('when performing with sorting', given(an_observable_query_for, context => {
|
|
11
12
|
let fetchStub: sinon.SinonStub;
|
|
13
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
12
14
|
const mockResponse = {
|
|
13
15
|
data: 'test-result',
|
|
14
16
|
isSuccess: true,
|
|
@@ -27,7 +29,8 @@ describe('when performing with sorting', given(an_observable_query_for, context
|
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
beforeEach(async () => {
|
|
30
|
-
|
|
32
|
+
fetchHelper = createFetchHelper();
|
|
33
|
+
fetchStub = fetchHelper.stubFetch();
|
|
31
34
|
fetchStub.resolves({
|
|
32
35
|
json: sinon.stub().resolves(mockResponse),
|
|
33
36
|
ok: true,
|
|
@@ -41,7 +44,7 @@ describe('when performing with sorting', given(an_observable_query_for, context
|
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
afterEach(() => {
|
|
44
|
-
|
|
47
|
+
fetchHelper.restore();
|
|
45
48
|
});
|
|
46
49
|
|
|
47
50
|
it('should include sortBy parameter in URL', () => {
|
|
@@ -6,9 +6,12 @@ import { given } from '../../../given';
|
|
|
6
6
|
import * as sinon from 'sinon';
|
|
7
7
|
import { QueryResult } from '../../QueryResult';
|
|
8
8
|
|
|
9
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
10
|
+
|
|
9
11
|
describe('when performing with valid arguments', given(an_observable_query_for, context => {
|
|
10
12
|
let result: QueryResult<string>;
|
|
11
13
|
let fetchStub: sinon.SinonStub;
|
|
14
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
12
15
|
const mockResponse = {
|
|
13
16
|
data: 'test-result',
|
|
14
17
|
isSuccess: true,
|
|
@@ -27,7 +30,9 @@ describe('when performing with valid arguments', given(an_observable_query_for,
|
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
beforeEach(async () => {
|
|
30
|
-
|
|
33
|
+
// Setup fetch mock using helper
|
|
34
|
+
fetchHelper = createFetchHelper();
|
|
35
|
+
fetchStub = fetchHelper.stubFetch();
|
|
31
36
|
fetchStub.resolves({
|
|
32
37
|
json: sinon.stub().resolves(mockResponse),
|
|
33
38
|
ok: true,
|
|
@@ -42,7 +47,7 @@ describe('when performing with valid arguments', given(an_observable_query_for,
|
|
|
42
47
|
});
|
|
43
48
|
|
|
44
49
|
afterEach(() => {
|
|
45
|
-
|
|
50
|
+
fetchHelper.restore();
|
|
46
51
|
});
|
|
47
52
|
|
|
48
53
|
it('should return successful result', () => {
|
|
@@ -5,12 +5,14 @@ import { a_query_for } from '../given/a_query_for';
|
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
|
|
7
7
|
import * as sinon from 'sinon';
|
|
8
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
8
9
|
import { QueryResult } from '../../QueryResult';
|
|
9
10
|
|
|
10
11
|
describe('with abort controller', given(a_query_for, context => {
|
|
11
12
|
let result1: QueryResult<string>;
|
|
12
13
|
let result2: QueryResult<string>;
|
|
13
14
|
let fetchStub: sinon.SinonStub;
|
|
15
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
14
16
|
let firstAbortController: AbortController;
|
|
15
17
|
const mockResponse = {
|
|
16
18
|
data: 'test-result',
|
|
@@ -31,7 +33,8 @@ describe('with abort controller', given(a_query_for, context => {
|
|
|
31
33
|
|
|
32
34
|
beforeEach(async () => {
|
|
33
35
|
// Setup fetch mock
|
|
34
|
-
|
|
36
|
+
fetchHelper = createFetchHelper();
|
|
37
|
+
fetchStub = fetchHelper.stubFetch();
|
|
35
38
|
fetchStub.resolves({
|
|
36
39
|
json: sinon.stub().resolves(mockResponse),
|
|
37
40
|
ok: true,
|
|
@@ -49,7 +52,7 @@ describe('with abort controller', given(a_query_for, context => {
|
|
|
49
52
|
});
|
|
50
53
|
|
|
51
54
|
afterEach(() => {
|
|
52
|
-
|
|
55
|
+
fetchHelper.restore();
|
|
53
56
|
});
|
|
54
57
|
|
|
55
58
|
it('should create new abort controller on second call', () => {
|
|
@@ -5,11 +5,13 @@ import { a_query_for } from '../given/a_query_for';
|
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
|
|
7
7
|
import * as sinon from 'sinon';
|
|
8
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
8
9
|
import { QueryResult } from '../../QueryResult';
|
|
9
10
|
|
|
10
11
|
describe('with enumerable query', given(a_query_for, context => {
|
|
11
12
|
let result: QueryResult<string[]>;
|
|
12
13
|
let fetchStub: sinon.SinonStub;
|
|
14
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
13
15
|
const mockResponse = {
|
|
14
16
|
data: ['item1', 'item2'],
|
|
15
17
|
isSuccess: true,
|
|
@@ -29,7 +31,8 @@ describe('with enumerable query', given(a_query_for, context => {
|
|
|
29
31
|
|
|
30
32
|
beforeEach(async () => {
|
|
31
33
|
// Setup fetch mock
|
|
32
|
-
|
|
34
|
+
fetchHelper = createFetchHelper();
|
|
35
|
+
fetchStub = fetchHelper.stubFetch();
|
|
33
36
|
fetchStub.resolves({
|
|
34
37
|
json: sinon.stub().resolves(mockResponse),
|
|
35
38
|
ok: true,
|
|
@@ -43,7 +46,7 @@ describe('with enumerable query', given(a_query_for, context => {
|
|
|
43
46
|
});
|
|
44
47
|
|
|
45
48
|
afterEach(() => {
|
|
46
|
-
|
|
49
|
+
fetchHelper.restore();
|
|
47
50
|
});
|
|
48
51
|
|
|
49
52
|
it('should return successful result', () => {
|
|
@@ -5,15 +5,18 @@ import { a_query_for } from '../given/a_query_for';
|
|
|
5
5
|
import { given } from '../../../given';
|
|
6
6
|
|
|
7
7
|
import * as sinon from 'sinon';
|
|
8
|
+
import { createFetchHelper } from '../../../helpers/fetchHelper';
|
|
8
9
|
import { QueryResult } from '../../QueryResult';
|
|
9
10
|
|
|
10
11
|
describe('with fetch error', given(a_query_for, context => {
|
|
11
12
|
let result: QueryResult<string>;
|
|
12
13
|
let fetchStub: sinon.SinonStub;
|
|
14
|
+
let fetchHelper: { stubFetch: () => sinon.SinonStub; restore: () => void };
|
|
13
15
|
|
|
14
16
|
beforeEach(async () => {
|
|
15
17
|
// Setup fetch mock to reject
|
|
16
|
-
|
|
18
|
+
fetchHelper = createFetchHelper();
|
|
19
|
+
fetchStub = fetchHelper.stubFetch();
|
|
17
20
|
fetchStub.rejects(new Error('Network error'));
|
|
18
21
|
|
|
19
22
|
context.query.setOrigin('https://api.example.com');
|
|
@@ -29,7 +32,7 @@ describe('with fetch error', given(a_query_for, context => {
|
|
|
29
32
|
});
|
|
30
33
|
|
|
31
34
|
afterEach(() => {
|
|
32
|
-
|
|
35
|
+
fetchHelper.restore();
|
|
33
36
|
});
|
|
34
37
|
|
|
35
38
|
it('should return no success result', () => {
|