-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(@aws-amplify/datastore): return partial data when available (#7775)
- Loading branch information
Showing
3 changed files
with
407 additions
and
8 deletions.
There are no files selected for viewing
80 changes: 80 additions & 0 deletions
80
packages/datastore/__tests__/__snapshots__/sync.test.ts.snap
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,80 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`Sync jitteredRetry should return all data 1`] = ` | ||
Object { | ||
"data": Object { | ||
"syncPosts": Object { | ||
"items": Array [ | ||
Object { | ||
"id": "1", | ||
"title": "Item 1", | ||
}, | ||
Object { | ||
"id": "2", | ||
"title": "Item 2", | ||
}, | ||
], | ||
}, | ||
}, | ||
} | ||
`; | ||
|
||
exports[`Sync jitteredRetry should return partial data and send Hub event when datastorePartialData is set 1`] = ` | ||
Object { | ||
"data": Object { | ||
"syncPosts": Object { | ||
"items": Array [ | ||
Object { | ||
"id": "1", | ||
"title": "Item 1", | ||
}, | ||
Object { | ||
"id": "3", | ||
"title": "Item 3", | ||
}, | ||
], | ||
}, | ||
}, | ||
"errors": Array [ | ||
Object { | ||
"message": "Item 2 error", | ||
}, | ||
], | ||
} | ||
`; | ||
|
||
exports[`Sync jitteredRetry should throw error and NOT return data or send Hub event when datastorePartialData is not set 1`] = ` | ||
Object { | ||
"data": Object { | ||
"syncPosts": Object { | ||
"items": Array [ | ||
Object { | ||
"id": "1", | ||
"title": "Item 1", | ||
}, | ||
null, | ||
Object { | ||
"id": "3", | ||
"title": "Item 3", | ||
}, | ||
], | ||
}, | ||
}, | ||
"errors": Array [ | ||
Object { | ||
"message": "Item 2 error", | ||
}, | ||
], | ||
} | ||
`; | ||
|
||
exports[`Sync jitteredRetry should throw error if no data is returned 1`] = ` | ||
Object { | ||
"data": null, | ||
"errors": Array [ | ||
Object { | ||
"message": "General error", | ||
}, | ||
], | ||
} | ||
`; |
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,267 @@ | ||
// These tests should be replaced once SyncEngine.partialDataFeatureFlagEnabled is removed. | ||
|
||
const sessionStorageMock = (() => { | ||
let store = {}; | ||
|
||
return { | ||
getItem(key) { | ||
return store[key] || null; | ||
}, | ||
setItem(key, value) { | ||
store[key] = value.toString(); | ||
}, | ||
removeItem(key) { | ||
delete store[key]; | ||
}, | ||
clear() { | ||
store = {}; | ||
}, | ||
}; | ||
})(); | ||
|
||
Object.defineProperty(window, 'sessionStorage', { | ||
value: sessionStorageMock, | ||
}); | ||
|
||
describe('Sync', () => { | ||
describe('jitteredRetry', () => { | ||
const defaultQuery = `query { | ||
syncPosts { | ||
items { | ||
id | ||
title | ||
count | ||
_version | ||
_lastChangedAt | ||
_deleted | ||
} | ||
nextToken | ||
startedAt | ||
} | ||
}`; | ||
const defaultVariables = {}; | ||
const defaultOpName = 'syncPosts'; | ||
const defaultModelDefinition = { name: 'Post' }; | ||
|
||
beforeEach(() => { | ||
window.sessionStorage.clear(); | ||
jest.resetModules(); | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('should return all data', async () => { | ||
window.sessionStorage.setItem('datastorePartialData', 'true'); | ||
const resolveResponse = { | ||
data: { | ||
syncPosts: { | ||
items: [ | ||
{ | ||
id: '1', | ||
title: 'Item 1', | ||
}, | ||
{ | ||
id: '2', | ||
title: 'Item 2', | ||
}, | ||
], | ||
}, | ||
}, | ||
}; | ||
|
||
const SyncProcessor = jitteredRetrySyncProcessorSetup({ | ||
resolveResponse, | ||
}); | ||
|
||
const data = await SyncProcessor.jitteredRetry({ | ||
query: defaultQuery, | ||
variables: defaultVariables, | ||
opName: defaultOpName, | ||
modelDefinition: defaultModelDefinition, | ||
}); | ||
|
||
expect(data).toMatchSnapshot(); | ||
}); | ||
|
||
it('should return partial data and send Hub event when datastorePartialData is set', async () => { | ||
window.sessionStorage.setItem('datastorePartialData', 'true'); | ||
const rejectResponse = { | ||
data: { | ||
syncPosts: { | ||
items: [ | ||
{ | ||
id: '1', | ||
title: 'Item 1', | ||
}, | ||
null, | ||
{ | ||
id: '3', | ||
title: 'Item 3', | ||
}, | ||
], | ||
}, | ||
}, | ||
errors: [ | ||
{ | ||
message: 'Item 2 error', | ||
}, | ||
], | ||
}; | ||
|
||
const hubDispatchMock = jest.fn(); | ||
const coreMocks = { | ||
Hub: { | ||
dispatch: hubDispatchMock, | ||
listen: jest.fn(), | ||
}, | ||
}; | ||
|
||
const SyncProcessor = jitteredRetrySyncProcessorSetup({ | ||
rejectResponse, | ||
coreMocks, | ||
}); | ||
|
||
const data = await SyncProcessor.jitteredRetry({ | ||
query: defaultQuery, | ||
variables: defaultVariables, | ||
opName: defaultOpName, | ||
modelDefinition: defaultModelDefinition, | ||
}); | ||
|
||
expect(data).toMatchSnapshot(); | ||
|
||
expect(hubDispatchMock).toHaveBeenCalledWith('datastore', { | ||
event: 'syncQueriesPartialSyncError', | ||
data: { | ||
errors: [ | ||
{ | ||
message: 'Item 2 error', | ||
}, | ||
], | ||
modelName: 'Post', | ||
}, | ||
}); | ||
}); | ||
|
||
it('should throw error and NOT return data or send Hub event when datastorePartialData is not set', async () => { | ||
const rejectResponse = { | ||
data: { | ||
syncPosts: { | ||
items: [ | ||
{ | ||
id: '1', | ||
title: 'Item 1', | ||
}, | ||
null, | ||
{ | ||
id: '3', | ||
title: 'Item 3', | ||
}, | ||
], | ||
}, | ||
}, | ||
errors: [ | ||
{ | ||
message: 'Item 2 error', | ||
}, | ||
], | ||
}; | ||
|
||
const hubDispatchMock = jest.fn(); | ||
const coreMocks = { | ||
Hub: { | ||
dispatch: hubDispatchMock, | ||
listen: jest.fn(), | ||
}, | ||
}; | ||
|
||
const SyncProcessor = jitteredRetrySyncProcessorSetup({ | ||
rejectResponse, | ||
coreMocks, | ||
}); | ||
|
||
try { | ||
await SyncProcessor.jitteredRetry({ | ||
query: defaultQuery, | ||
variables: defaultVariables, | ||
opName: defaultOpName, | ||
modelDefinition: defaultModelDefinition, | ||
}); | ||
} catch (e) { | ||
expect(e).toMatchSnapshot(); | ||
} | ||
}); | ||
|
||
it('should throw error if no data is returned', async () => { | ||
window.sessionStorage.setItem('datastorePartialData', 'true'); | ||
const rejectResponse = { | ||
data: null, | ||
errors: [ | ||
{ | ||
message: 'General error', | ||
}, | ||
], | ||
}; | ||
|
||
const SyncProcessor = jitteredRetrySyncProcessorSetup({ | ||
rejectResponse, | ||
}); | ||
|
||
try { | ||
await SyncProcessor.jitteredRetry({ | ||
query: defaultQuery, | ||
variables: defaultVariables, | ||
opName: defaultOpName, | ||
modelDefinition: defaultModelDefinition, | ||
}); | ||
} catch (e) { | ||
expect(e).toMatchSnapshot(); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
function jitteredRetrySyncProcessorSetup({ | ||
rejectResponse, | ||
resolveResponse, | ||
coreMocks, | ||
}: { | ||
rejectResponse?: any; | ||
resolveResponse?: any; | ||
coreMocks?: object; | ||
}) { | ||
jest.mock('@aws-amplify/api', () => ({ | ||
graphql: () => | ||
new Promise((res, rej) => { | ||
if (resolveResponse) { | ||
res(resolveResponse); | ||
} else if (rejectResponse) { | ||
rej(rejectResponse); | ||
} | ||
}), | ||
})); | ||
|
||
jest.mock('@aws-amplify/core', () => ({ | ||
...jest.requireActual('@aws-amplify/core'), | ||
// No need to retry any thrown errors right now, | ||
// so we're overriding jitteredExponentialRetry | ||
jitteredExponentialRetry: (fn, args) => fn(...args), | ||
...coreMocks, | ||
})); | ||
|
||
const SyncProcessorClass = require('../src/sync/processors/sync') | ||
.SyncProcessor; | ||
|
||
const testInternalSchema = { | ||
namespaces: {}, | ||
version: '', | ||
}; | ||
|
||
const SyncProcessor = new SyncProcessorClass( | ||
testInternalSchema, | ||
1000, // default maxRecordsToSync | ||
10000, // default syncPageSize | ||
null | ||
); | ||
|
||
return SyncProcessor; | ||
} |
Oops, something went wrong.