Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(@aws-amplify/datastore): adds missing fields to items sent through observe/observeQuery #9973

Merged
merged 5 commits into from
Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,20 @@ describe('SQLiteAdapter', () => {
return await db.getAll('select * from MutationEvent', []);
}

async function clearOutbox() {
await pause(250);
const adapter = (DataStore as any).storageAdapter;
const db = (adapter as any).db;
return await db.executeStatements(['delete from MutationEvent']);
}

({ initSchema, DataStore } = require('@aws-amplify/datastore'));
addCommonQueryTests({
initSchema,
DataStore,
storageAdapter: SQLiteAdapter,
getMutations,
clearOutbox,
});

describe('something', () => {
Expand Down
7 changes: 7 additions & 0 deletions packages/datastore/__tests__/AsyncStorageAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import AsyncStorageAdapter from '../src/storage/adapter/AsyncStorageAdapter';
import {
DataStore as DataStoreType,
initSchema as initSchemaType,
syncClasses,
} from '../src/datastore/datastore';
import { PersistentModelConstructor, SortDirection } from '../src/types';
import { pause, Model, User, Profile, testSchema } from './helpers';
Expand All @@ -23,12 +24,18 @@ describe('AsyncStorageAdapter tests', () => {
return await adapter.getAll('sync_MutationEvent');
}

async function clearOutbox(adapter) {
await pause(250);
return await adapter.delete(syncClasses['MutationEvent']);
}

({ initSchema, DataStore } = require('../src/datastore/datastore'));
addCommonQueryTests({
initSchema,
DataStore,
storageAdapter: AsyncStorageAdapter,
getMutations,
clearOutbox,
});

describe('Query', () => {
Expand Down
260 changes: 258 additions & 2 deletions packages/datastore/__tests__/DataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ import {
PersistentModel,
PersistentModelConstructor,
} from '../src/types';
import { Comment, Model, Post, Metadata, testSchema, pause } from './helpers';
import {
Comment,
Model,
Post,
Profile,
Metadata,
User,
testSchema,
pause,
} from './helpers';

let initSchema: typeof initSchemaType;
let DataStore: typeof DataStoreType;
Expand Down Expand Up @@ -265,13 +274,17 @@ describe('DataStore observeQuery, with fake-indexeddb and fake sync', () => {

let Comment: PersistentModelConstructor<Comment>;
let Post: PersistentModelConstructor<Post>;
let User: PersistentModelConstructor<User>;
let Profile: PersistentModelConstructor<Profile>;

beforeEach(async () => {
({ initSchema, DataStore } = require('../src/datastore/datastore'));
const classes = initSchema(testSchema());
({ Comment, Post } = classes as {
({ Comment, Post, User, Profile } = classes as {
Comment: PersistentModelConstructor<Comment>;
Post: PersistentModelConstructor<Post>;
User: PersistentModelConstructor<User>;
Profile: PersistentModelConstructor<Profile>;
});

// This prevents pollution between tests. DataStore may have processes in
Expand Down Expand Up @@ -598,6 +611,249 @@ describe('DataStore observeQuery, with fake-indexeddb and fake sync', () => {
}
})();
});

test('attaches related belongsTo properties consistently with query() on INSERT', async done => {
try {
const expecteds = [5, 15];

for (let i = 0; i < 5; i++) {
await DataStore.save(
new Comment({
content: `comment content ${i}`,
post: await DataStore.save(
new Post({
title: `new post ${i}`,
})
),
})
);
}

const sub = DataStore.observeQuery(Comment).subscribe(
({ items, isSynced }) => {
const expected = expecteds.shift() || 0;
expect(items.length).toBe(expected);

for (let i = 0; i < expected; i++) {
expect(items[i].content).toEqual(`comment content ${i}`);
expect(items[i].post.title).toEqual(`new post ${i}`);
}

if (expecteds.length === 0) {
sub.unsubscribe();
done();
}
}
);

setTimeout(async () => {
for (let i = 5; i < 15; i++) {
await DataStore.save(
new Comment({
content: `comment content ${i}`,
post: await DataStore.save(
new Post({
title: `new post ${i}`,
})
),
})
);
}
}, 1);
} catch (error) {
done(error);
}
});

test('attaches related hasOne properties consistently with query() on INSERT', async done => {
try {
const expecteds = [5, 15];

for (let i = 0; i < 5; i++) {
await DataStore.save(
new User({
name: `user ${i}`,
profile: await DataStore.save(
new Profile({
firstName: `firstName ${i}`,
lastName: `lastName ${i}`,
})
),
})
);
}

const sub = DataStore.observeQuery(User).subscribe(
({ items, isSynced }) => {
const expected = expecteds.shift() || 0;
expect(items.length).toBe(expected);

for (let i = 0; i < expected; i++) {
expect(items[i].name).toEqual(`user ${i}`);
expect(items[i].profile.firstName).toEqual(`firstName ${i}`);
expect(items[i].profile.lastName).toEqual(`lastName ${i}`);
}

if (expecteds.length === 0) {
sub.unsubscribe();
done();
}
}
);

setTimeout(async () => {
for (let i = 5; i < 15; i++) {
await DataStore.save(
new User({
name: `user ${i}`,
profile: await DataStore.save(
new Profile({
firstName: `firstName ${i}`,
lastName: `lastName ${i}`,
})
),
})
);
}
}, 1);
} catch (error) {
done(error);
}
});

test('attaches related belongsTo properties consistently with query() on UPDATE', async done => {
try {
const expecteds = [
['old post 0', 'old post 1', 'old post 2', 'old post 3', 'old post 4'],
['new post 0', 'new post 1', 'new post 2', 'new post 3', 'new post 4'],
];

for (let i = 0; i < 5; i++) {
await DataStore.save(
new Comment({
content: `comment content ${i}`,
post: await DataStore.save(
new Post({
title: `old post ${i}`,
})
),
})
);
}

const sub = DataStore.observeQuery(Comment).subscribe(
({ items, isSynced }) => {
const expected = expecteds.shift() || [];
expect(items.length).toBe(expected.length);

for (let i = 0; i < expected.length; i++) {
expect(items[i].content).toContain(`comment content ${i}`);
expect(items[i].post.title).toEqual(expected[i]);
}

if (expecteds.length === 0) {
sub.unsubscribe();
done();
}
}
);

setTimeout(async () => {
let postIndex = 0;
const comments = await DataStore.query(Comment);
for (const comment of comments) {
const newPost = await DataStore.save(
new Post({
title: `new post ${postIndex++}`,
})
);

await DataStore.save(
Comment.copyOf(comment, draft => {
draft.content = `updated: ${comment.content}`;
draft.post = newPost;
})
);
}
}, 1);
} catch (error) {
done(error);
}
});

test('attaches related hasOne properties consistently with query() on UPDATE', async done => {
try {
const expecteds = [
[
'first name 0',
'first name 1',
'first name 2',
'first name 3',
'first name 4',
],
[
'new first name 0',
'new first name 1',
'new first name 2',
'new first name 3',
'new first name 4',
],
];

for (let i = 0; i < 5; i++) {
await DataStore.save(
new User({
name: `user ${i}`,
profile: await DataStore.save(
new Profile({
firstName: `first name ${i}`,
lastName: `last name ${i}`,
})
),
})
);
}

const sub = DataStore.observeQuery(User).subscribe(
({ items, isSynced }) => {
const expected = expecteds.shift() || [];
expect(items.length).toBe(expected.length);

for (let i = 0; i < expected.length; i++) {
expect(items[i].name).toContain(`user ${i}`);
expect(items[i].profile.firstName).toEqual(expected[i]);
}

if (expecteds.length === 0) {
sub.unsubscribe();
done();
}
}
);

setTimeout(async () => {
let userIndex = 0;
const users = await DataStore.query(User);
for (const user of users) {
const newProfile = await DataStore.save(
new Profile({
firstName: `new first name ${userIndex++}`,
lastName: `new last name ${userIndex}`,
})
);

await DataStore.save(
User.copyOf(user, draft => {
draft.name = `updated: ${user.name}`;
draft.profile = newProfile;
})
);
}
}, 1);
} catch (error) {
done(error);
}
});
});

describe('DataStore tests', () => {
Expand Down
7 changes: 7 additions & 0 deletions packages/datastore/__tests__/IndexedDBAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'fake-indexeddb/auto';
import {
DataStore as DataStoreType,
initSchema as initSchemaType,
syncClasses,
} from '../src/datastore/datastore';
import { PersistentModelConstructor, SortDirection } from '../src/types';
import {
Expand All @@ -29,12 +30,18 @@ describe('IndexedDBAdapter tests', () => {
return await adapter.getAll('sync_MutationEvent');
}

async function clearOutbox(adapter) {
await pause(250);
return await adapter.delete(syncClasses['MutationEvent']);
}

({ initSchema, DataStore } = require('../src/datastore/datastore'));
addCommonQueryTests({
initSchema,
DataStore,
storageAdapter: Adapter,
getMutations,
clearOutbox,
});

describe('Query', () => {
Expand Down
25 changes: 25 additions & 0 deletions packages/datastore/__tests__/commonAdapterTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function addCommonQueryTests({
DataStore,
storageAdapter,
getMutations,
clearOutbox,
}) {
describe('Common `query()` cases', () => {
let Model: PersistentModelConstructor<Model>;
Expand Down Expand Up @@ -341,5 +342,29 @@ export function addCommonQueryTests({
postId: mutations[0].modelId,
});
});

it('only includes changed fields in mutations', async () => {
const profile = await DataStore.save(
new Profile({ firstName: 'original first', lastName: 'original last' })
);

await clearOutbox(adapter);

await DataStore.save(
Profile.copyOf(profile, draft => {
draft.firstName = 'new first';
})
);

const mutations = await getMutations(adapter);

expect(mutations.length).toBe(1);
expectMutation(mutations[0], {
firstName: 'new first',
_version: v => v === undefined || v === null,
_lastChangedAt: v => v === undefined || v === null,
_deleted: v => v === undefined || v === null,
});
});
});
}
Loading