-
-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #482 from igorkamyshev/next-013
Release 0.13
- Loading branch information
Showing
34 changed files
with
744 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@farfetched/core": minor | ||
--- | ||
|
||
Add _Event_ `.forceDeactivate` to _Barrier_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@farfetched/core": minor | ||
--- | ||
|
||
Runtime deprecation warnings about `concurrency` field in `createJsonQuery` and `createJsonMutation` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@farfetched/core": minor | ||
--- | ||
|
||
Pass response original `headers` to `mapData` callback in `createJsonQuery` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@farfetched/core": minor | ||
--- | ||
|
||
Runtime deprecation warning in `attachOperation` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@farfetched/core": minor | ||
--- | ||
|
||
Add _Event_ `.performed` to _Barrier_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@farfetched/core": minor | ||
--- | ||
|
||
Pass response original `headers` to `mapData` callback in `createJsonMutation` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Barrier Circuit Breaker | ||
|
||
> Recipe based on the question from [issue #458](https://github.com/igorkamyshev/farfetched/issues/458) | ||
Let us assume we have a basic [_Barrier_](https://farfetched.dev/docs/api/barrier) that is activated on a 401 HTTP error code. The barrier is used to renew the token after failing to access the protected resource. | ||
|
||
```ts | ||
import { createBarrier, isHttpErrorCode } from '@farfetched/core'; | ||
|
||
const authBarrier = createBarrier({ | ||
activateOn: { | ||
failure: isHttpErrorCode(401), | ||
}, | ||
perform: [renewTokenMutation], | ||
}); | ||
``` | ||
|
||
::: tip | ||
|
||
It is a basic example based on the case-study [Auth token](/recipes/auth_token). | ||
|
||
::: | ||
|
||
In this setup, it is possible to get infinite loops if the token renewal in case of some mistake in [_Query_](/api/primitives/query) declaration. For example, if we made a typo in the header name, the [_Barrier_](https://farfetched.dev/docs/api/barrier) will be activated on every request, and the token will be renewed every time, which will not lead to successful [_Query_](/api/primitives/query) execution. | ||
|
||
```ts | ||
import { createJsonQuery, applyBarrier } from '@farfetched/core'; | ||
|
||
const buggyQuery = createJsonQuery({ | ||
request: { | ||
method: 'GET', | ||
url: 'https://api.salo.com/protected', | ||
headers: combine($authToken, (token) => ({ | ||
// 👇 typo in header name | ||
Authorisation: `Bearer ${token}`, | ||
})), | ||
}, | ||
// ... | ||
}); | ||
|
||
applyBarrier(buggyQuery, { barrier: authBarrier }); | ||
``` | ||
|
||
## Solution | ||
|
||
In this case, we can write some kind of circuit breaker that will stop the token renewal after a certain number of attempts. | ||
|
||
```ts | ||
function barrierCircuitBreaker(barrier, { maxAttempts }) { | ||
const $currentAttempt = createStore(0).on( | ||
// every time after the Barrier is performed | ||
barrier.performed, | ||
// increment the current attempt | ||
(attempt) => attempt + 1 | ||
); | ||
|
||
sample({ | ||
// If the number of attempts exceeds the limit, | ||
clock: $currentAttempt, | ||
filter: (currentAttempt) => currentAttempt >= maxAttempts, | ||
target: [ | ||
// force the Barrier to deactivate | ||
barrier.forceDeactivate, | ||
// and reset the current attempt counter | ||
$currentAttempt.reinit, | ||
], | ||
}); | ||
} | ||
``` | ||
|
||
This function can be applied to the existing [_Barrier_](https://farfetched.dev/docs/api/barrier) to limit the number of attempts to renew the token 👇 | ||
|
||
```ts | ||
barrierCircuitBreaker(authBarrier, { maxAttempts: 3 }); | ||
``` | ||
|
||
That is it, `authBarrier` will perform the token renewal only three times, and after that, it will be deactivated forcibly, so all [_Queries_](/api/primitives/query) will fail with the 401 HTTP error code. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# v0.13 Naiharn | ||
|
||
Mostly about improving and cleaning the APIs of Farfetched. We are preparing for the big release v1.0, so [as promised](/roadmap), all 0.X releases will be about improving the existing features and cleaning the APIs. | ||
|
||
![Naiharn](./naiharn.jpeg) | ||
|
||
> Photo by <a href="https://instagram.com/destroooooya">Maria Goroshko</a> | ||
::: details Why Naiharn? | ||
|
||
Naiharn is one of the most beautiful beaches in Phuket, Thailand. High season is coming and it's time to relax and enjoy the sun. This release is all about improving and cleaning the APIs of Farfetched and Naiharn is a perfect match for it 🏖️ | ||
::: | ||
|
||
## Migration guide | ||
|
||
### `attachOperation` operator | ||
|
||
This operator is deprecated since [v0.12](/releases/0-12) and will be removed in v0.14. Please read [this ADR](/adr/attach_operation_deprecation) for more information and migration guide. | ||
|
||
### `concurrency` operator | ||
|
||
Field `concurrency` in `createJsonQuery` and `createJsonMutation` is deprecated since [v0.12](/releases/0-12) and has to be replaced by the [`concurrency` operator](/api/operators/concurrency). Please read [this ADR](/adr/concurrency) for more information and migration guide. | ||
|
||
<!--@include: ./0-13.changelog.md--> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
packages/core/src/barrier_api/__tests__/barrier_circuit_breaker.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { describe, expect, test, vi } from 'vitest'; | ||
import { allSettled, createStore, createWatch, fork, sample } from 'effector'; | ||
|
||
import { createMutation } from '../../mutation/create_mutation'; | ||
import { createQuery } from '../../query/create_query'; | ||
import { httpError } from '../../errors/create_error'; | ||
import { isHttpErrorCode } from '../../errors/guards'; | ||
import { applyBarrier } from '../apply_barrier'; | ||
import { createBarrier } from '../create_barrier'; | ||
import { Barrier } from '../type'; | ||
|
||
describe('Barrier API', () => { | ||
test('barrier_circuit_breaker', async () => { | ||
// Setup from Recipe | ||
const renewTokenMutation = createMutation({ | ||
async handler(_: void) { | ||
return 1; | ||
}, | ||
}); | ||
|
||
const buggyQuery = createQuery({ | ||
async handler() { | ||
throw httpError({ | ||
status: 401, | ||
statusText: 'SORRY', | ||
response: 'Permanent error', | ||
}); | ||
}, | ||
}); | ||
|
||
const authBarrier = createBarrier({ | ||
activateOn: { | ||
failure: isHttpErrorCode(401), | ||
}, | ||
perform: [renewTokenMutation], | ||
}); | ||
|
||
applyBarrier(buggyQuery, { barrier: authBarrier }); | ||
|
||
function barrierCircuitBreaker( | ||
barrier: Barrier, | ||
{ maxAttempts }: { maxAttempts: number } | ||
) { | ||
const $currentAttempt = createStore(0).on( | ||
barrier.performed, | ||
(attempt) => attempt + 1 | ||
); | ||
|
||
sample({ | ||
clock: $currentAttempt, | ||
filter: (currentAttempt) => currentAttempt >= maxAttempts, | ||
target: [barrier.forceDeactivate, $currentAttempt.reinit], | ||
}); | ||
} | ||
|
||
barrierCircuitBreaker(authBarrier, { maxAttempts: 3 }); | ||
|
||
// Test setup | ||
const scope = fork(); | ||
|
||
const performedListener = vi.fn(); | ||
createWatch({ | ||
unit: authBarrier.performed, | ||
fn: performedListener, | ||
scope, | ||
}); | ||
|
||
const forceDeactivateListener = vi.fn(); | ||
createWatch({ | ||
unit: authBarrier.forceDeactivate, | ||
fn: forceDeactivateListener, | ||
scope, | ||
}); | ||
|
||
await allSettled(buggyQuery.refresh, { scope }); | ||
|
||
expect(performedListener).toBeCalledTimes(3); | ||
expect(forceDeactivateListener).toBeCalledTimes(1); | ||
|
||
expect(scope.getState(buggyQuery.$error)).toMatchInlineSnapshot(` | ||
{ | ||
"errorType": "HTTP", | ||
"explanation": "Request was finished with unsuccessful HTTP code", | ||
"response": "Permanent error", | ||
"status": 401, | ||
"statusText": "SORRY", | ||
} | ||
`); | ||
}); | ||
}); |
Oops, something went wrong.