Skip to content

Commit

Permalink
feat: make mutate generic (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver committed Nov 6, 2020
1 parent 4d402f3 commit 75be97f
Show file tree
Hide file tree
Showing 19 changed files with 373 additions and 72 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [12, 14]

steps:
Expand Down
11 changes: 3 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,9 @@ jobs:
release:
if: github.repository == 'timdeschryver/rx-query'
runs-on: ubuntu-latest
strategy:
matrix:
node: [12, 14]

steps:
- uses: actions/checkout@v1
- name: use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- uses: actions/setup-node@v1
- name: install
run: npm install
- name: build
Expand All @@ -30,3 +23,5 @@ jobs:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# run: npx semantic-release
- name: deploy storybook to GH pages
run: npm run deploy-storybook
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ In the view layer you will often see a structure like this, with a segment to re
```html
<ng-container *ngIf="characters$ | async as characters">
<ng-container [ngSwitch]="characters.status">
<div *ngSwitchCase="'loading'">
Loading ... ({{ characters.retries }})
</div>
<div *ngSwitchCase="'loading'">Loading ... ({{ characters.retries }})</div>

<div *ngSwitchCase="'error'">
Something went wrong ... ({{ characters.retries }})
Expand Down Expand Up @@ -301,9 +299,9 @@ Usage:

```ts
{
mutator: (data, params) =>
mutator: (data, queryOptions) =>
this.http
.post(`/persons/${params.id}`, data)
.post(`/persons/${queryOptions.queryParameters.id}`, data)
// 👇 important to let the request throw in order to rollback
.pipe(catchError((err) => throwError(err.statusText))),
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rx-query",
"version": "0.0.14",
"version": "0.0.15",
"description": "rx-query",
"author": "Tim Deschryver",
"repository": {
Expand Down
93 changes: 74 additions & 19 deletions rx-query/__tests__/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Revalidator,
resetQueryCache,
refreshQuery,
NOOP_MUTATE,
} from '..';

beforeEach(() => {
Expand All @@ -28,6 +29,7 @@ it('first loads then succeeds', async () => {
expect(valuesWithoutMutate(values)).toEqual([
{
status: 'loading',
retries: 0,
},
{
status: 'success',
Expand All @@ -49,6 +51,7 @@ it('retries then errors', async () => {
expect(valuesWithoutMutate(values)).toEqual([
{
status: 'loading',
retries: 0,
},
...Array.from({ length: DEFAULT_QUERY_CONFIG.retries as number }).map(
(_, i) => ({
Expand Down Expand Up @@ -79,6 +82,7 @@ it('can override default error config with retries', async () => {
expect(valuesWithoutMutate(values)).toEqual([
{
status: 'loading',
retries: 0,
},
{
status: 'loading',
Expand Down Expand Up @@ -110,6 +114,7 @@ it('can override default error config with custom retry', async () => {
expect(valuesWithoutMutate(values)).toEqual([
{
status: 'loading',
retries: 0,
},
...Array.from({ length: 5 }).map((_, i) => ({
status: 'loading',
Expand Down Expand Up @@ -151,13 +156,13 @@ it('retrieves data when params change and caches previous results', async () =>

expect(valuesWithoutMutate(values)).toEqual([
// true is already in cache, so it refreshes
{ status: 'refreshing', data: true },
{ status: 'refreshing', data: true, retries: 0 },
{ status: 'success', data: true },
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: false },

// true again -> refresh the cache
{ status: 'refreshing', data: true },
{ status: 'refreshing', data: true, retries: 0 },
{ status: 'success', data: true },
]);

Expand Down Expand Up @@ -185,12 +190,13 @@ it('keepPreviousData uses previous data until it receives new data', async () =>
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: 0 },
// while 1 is loading use previous data and set status as refreshing
{
data: 0,
status: 'refreshing',
retries: 0,
},
// 1 receievs a response
{
Expand All @@ -201,6 +207,7 @@ it('keepPreviousData uses previous data until it receives new data', async () =>
{
data: 1,
status: 'refreshing',
retries: 0,
},
// 2 receievs a response
{
Expand All @@ -211,6 +218,7 @@ it('keepPreviousData uses previous data until it receives new data', async () =>
{
data: 2,
status: 'refreshing',
retries: 0,
},
// 3 receievs a response
{
Expand Down Expand Up @@ -246,13 +254,13 @@ it('groups cache continues to live until cacheTime resolves', async () => {
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: true },
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: false },

// true was already cached and while being unsubscribed to, the cache remains
{ status: 'refreshing', data: true },
{ status: 'refreshing', data: true, retries: 0 },
{ status: 'success', data: true },
]);
});
Expand Down Expand Up @@ -283,13 +291,13 @@ it('groups clean up after last unsubscribe', async () => {
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: true },
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: false },

// true was unsubscribed too, so it loses its cache
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: true },
]);
});
Expand All @@ -311,9 +319,9 @@ it('ignores following params with same key', async () => {
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: 'same' },
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: 'other' },
]);
});
Expand Down Expand Up @@ -343,13 +351,13 @@ it('can disable cache', async () => {
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: true },
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: false },

// no cache -> data is undefined
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: true },
]);
});
Expand All @@ -369,6 +377,7 @@ it('invokes query on refresh', async () => {
expect(valuesWithoutMutate(values)).toEqual([
{
status: 'loading',
retries: 0,
},
{
status: 'success',
Expand All @@ -377,6 +386,7 @@ it('invokes query on refresh', async () => {
{
status: 'refreshing',
data: 20,
retries: 0,
},
{
status: 'success',
Expand All @@ -385,6 +395,7 @@ it('invokes query on refresh', async () => {
{
status: 'refreshing',
data: 21,
retries: 0,
},
{
status: 'success',
Expand All @@ -393,6 +404,7 @@ it('invokes query on refresh', async () => {
{
status: 'refreshing',
data: 22,
retries: 0,
},
{
status: 'success',
Expand All @@ -401,6 +413,7 @@ it('invokes query on refresh', async () => {
{
status: 'refreshing',
data: 23,
retries: 0,
},
{
status: 'success',
Expand Down Expand Up @@ -428,6 +441,7 @@ it('invokes query on focus', async () => {
expect(valuesWithoutMutate(values)).toEqual([
{
status: 'loading',
retries: 0,
},
{
status: 'success',
Expand All @@ -437,6 +451,7 @@ it('invokes query on focus', async () => {
{
status: 'refreshing',
data: 20,
retries: 0,
},
{
status: 'success',
Expand Down Expand Up @@ -475,7 +490,7 @@ it('can disable refresh on data when data is still fresh', async () => {
expect(valuesWithoutMutate(values)).toEqual([
// doesn't fire a load, nor a refresh
{ status: 'success', data: true },
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: false },
]);

Expand All @@ -496,6 +511,7 @@ it('can refresh on demand', async () => {
expect(valuesWithoutMutate(values)).toEqual([
{
status: 'loading',
retries: 0,
},
{
status: 'success',
Expand All @@ -504,6 +520,7 @@ it('can refresh on demand', async () => {
{
status: 'refreshing',
data: 20,
retries: 0,
},
{
status: 'success',
Expand All @@ -512,6 +529,7 @@ it('can refresh on demand', async () => {
{
status: 'refreshing',
data: 21,
retries: 0,
},
{
status: 'success',
Expand All @@ -520,6 +538,7 @@ it('can refresh on demand', async () => {
{
status: 'refreshing',
data: 22,
retries: 0,
},
{
status: 'success',
Expand All @@ -528,6 +547,7 @@ it('can refresh on demand', async () => {
{
status: 'refreshing',
data: 23,
retries: 0,
},
{
status: 'success',
Expand Down Expand Up @@ -559,7 +579,7 @@ it('can mutate data (data overwrite)', async () => {
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{
status: 'success',
data: { name: 'initial', description: 'just a description' },
Expand Down Expand Up @@ -594,7 +614,7 @@ it('can mutate data (updator fn)', async () => {
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{
status: 'success',
data: { name: 'initial', description: 'just a description' },
Expand All @@ -606,6 +626,41 @@ it('can mutate data (updator fn)', async () => {
]);
});

it('can mutate data (NOOP_MUTATE ignores the value)', async () => {
const values = [];
setTimeout(() => {
revalidate.next({
key: 'test',
data: () => NOOP_MUTATE,
trigger: 'mutate-success',
config: DEFAULT_QUERY_CONFIG,
});
}, 10);
for await (const value of eachValueFrom(
query('test', () =>
of({ name: 'initial', description: 'just a description' }),
).pipe(
takeWhile((x, i) => {
return i < 2;
}, true),
),
)) {
values.push(value);
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading', retries: 0 },
{
status: 'success',
data: { name: 'initial', description: 'just a description' },
},
{
status: 'success',
data: { name: 'initial', description: 'just a description' },
},
]);
});

it('rollbacks when a mutation errors', async () => {
const values = [];
const events: Revalidator[] = [
Expand Down Expand Up @@ -655,7 +710,7 @@ it('rollbacks when a mutation errors', async () => {
}

expect(valuesWithoutMutate(values)).toEqual([
{ status: 'loading' },
{ status: 'loading', retries: 0 },
{ status: 'success', data: 'initial' },
{ status: 'mutating', data: 'new value' },
{ status: 'mutating', data: 'new value 2' },
Expand Down
Loading

0 comments on commit 75be97f

Please sign in to comment.