Skip to content

Commit

Permalink
fix(@aws-amplify/datastore): return partial data when available (#7775)
Browse files Browse the repository at this point in the history
  • Loading branch information
amhinson authored Mar 3, 2021
1 parent 7a15725 commit 715aa7e
Show file tree
Hide file tree
Showing 3 changed files with 407 additions and 8 deletions.
80 changes: 80 additions & 0 deletions packages/datastore/__tests__/__snapshots__/sync.test.ts.snap
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",
},
],
}
`;
267 changes: 267 additions & 0 deletions packages/datastore/__tests__/sync.test.ts
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;
}
Loading

0 comments on commit 715aa7e

Please sign in to comment.