Skip to content

Commit aecec38

Browse files
authored
Merge pull request #10 from vuex-orm/feature/isPersisted
isPersisted
2 parents 31b6fe9 + 3207dd3 commit aecec38

File tree

9 files changed

+148
-12
lines changed

9 files changed

+148
-12
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ This plugin has an opinion how the GraphQL API schema should look like:
126126
You can see query examples in the [project wiki](https://github.com/vuex-orm/vuex-orm-apollo/wiki/Example-Queries).
127127

128128

129+
## Special record properties
130+
131+
This plugin adds a special property to your models: `$isPersisted`, which represents if this record is persisted on
132+
the server. It's true for all records except newly created ones.
133+
134+
129135

130136
## Known issues
131137

dist/vuex-orm-apollo.esm.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7867,7 +7867,11 @@ var QueryBuilder = /** @class */ (function () {
78677867
this.context.logger.log('Transformed data:', result);
78687868
this.context.logger.groupEnd();
78697869
}
7870-
return result;
7870+
else {
7871+
result['$isPersisted'] = true;
7872+
}
7873+
// MAke sure this is really a plain JS object. We had some issues in testing here.
7874+
return JSON.parse(JSON.stringify(result));
78717875
};
78727876
/**
78737877
* Generates the arguments string for a graphql query based on a given map.
@@ -8144,6 +8148,12 @@ var Context = /** @class */ (function () {
81448148
throw new Error('database param is required to initialize vuex-orm-apollo!');
81458149
}
81468150
this.collectModels();
8151+
this.logger.group('Context setup');
8152+
this.logger.log('components', this.components);
8153+
this.logger.log('options', this.options);
8154+
this.logger.log('database', this.database);
8155+
this.logger.log('models', this.models);
8156+
this.logger.groupEnd();
81478157
}
81488158
/**
81498159
* Returns a model by name
@@ -8168,8 +8178,17 @@ var Context = /** @class */ (function () {
81688178
this.database.entities.forEach(function (entity) {
81698179
var model = new Model(entity.model, _this);
81708180
_this.models.set(model.singularName, model);
8181+
_this.augmentModel(model);
81718182
});
81728183
};
8184+
Context.prototype.augmentModel = function (model) {
8185+
var originalFieldGenerator = model.baseModel.fields.bind(model.baseModel);
8186+
model.baseModel.fields = function () {
8187+
var originalFields = originalFieldGenerator();
8188+
originalFields['$isPersisted'] = model.baseModel.attr(false);
8189+
return originalFields;
8190+
};
8191+
};
81738192
return Context;
81748193
}());
81758194

@@ -8308,7 +8327,7 @@ var VuexORMApollo = /** @class */ (function () {
83088327
var state = _a.state, dispatch = _a.dispatch;
83098328
var id = _b.id, args = _b.args;
83108329
return __awaiter(this, void 0, void 0, function () {
8311-
var model, data, mutationName;
8330+
var model, data, mutationName, record;
83128331
return __generator(this, function (_c) {
83138332
switch (_c.label) {
83148333
case 0:
@@ -8321,8 +8340,10 @@ var VuexORMApollo = /** @class */ (function () {
83218340
return [4 /*yield*/, this.mutate(mutationName, args, dispatch, model, false)];
83228341
case 1:
83238342
_c.sent();
8324-
// TODO is this really necessary?
8325-
return [2 /*return*/, model.baseModel.getters('find')(id)];
8343+
record = model.baseModel.getters('find')(id);
8344+
record.$isPersisted = true;
8345+
record.$dispatch('update', { where: record.id, data: record });
8346+
return [2 /*return*/, record];
83268347
case 2: return [2 /*return*/];
83278348
}
83288349
});

src/context.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ export default class Context {
2626
}
2727

2828
this.collectModels();
29+
30+
this.logger.group('Context setup');
31+
this.logger.log('components', this.components);
32+
this.logger.log('options', this.options);
33+
this.logger.log('database', this.database);
34+
this.logger.log('models', this.models);
35+
this.logger.groupEnd();
2936
}
3037

3138
/**
@@ -49,8 +56,22 @@ export default class Context {
4956
*/
5057
private collectModels () {
5158
this.database.entities.forEach((entity: any) => {
52-
const model = new Model(entity.model as ORMModel, this);
59+
const model: Model = new Model(entity.model as ORMModel, this);
5360
this.models.set(model.singularName, model);
61+
62+
this.augmentModel(model);
5463
});
5564
}
65+
66+
private augmentModel (model: Model) {
67+
const originalFieldGenerator = model.baseModel.fields.bind(model.baseModel);
68+
69+
model.baseModel.fields = () => {
70+
const originalFields = originalFieldGenerator();
71+
72+
originalFields['$isPersisted'] = model.baseModel.attr(false);
73+
74+
return originalFields;
75+
};
76+
}
5677
}

src/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface ORMModel {
3636
fields (): any;
3737
dispatch (name: string, ...params: Array<any>): any;
3838
getters (name: string, ...params: Array<any>): any;
39+
attr (defaultValue: any): Field;
3940
}
4041

4142
export interface Field {

src/queryBuilder.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,12 @@ export default class QueryBuilder {
195195
if (!recursiveCall) {
196196
this.context.logger.log('Transformed data:', result);
197197
this.context.logger.groupEnd();
198+
} else {
199+
result['$isPersisted'] = true;
198200
}
199201

200-
return result;
202+
// MAke sure this is really a plain JS object. We had some issues in testing here.
203+
return JSON.parse(JSON.stringify(result));
201204
}
202205

203206
/**

src/vuex-orm-apollo.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,12 @@ export default class VuexORMApollo {
118118
const mutationName = `create${upcaseFirstLetter(model.singularName)}`;
119119
await this.mutate(mutationName, args, dispatch, model, false);
120120

121-
// TODO is this really necessary?
122-
return model.baseModel.getters('find')(id);
121+
const record = model.baseModel.getters('find')(id);
122+
123+
record.$isPersisted = true;
124+
record.$dispatch('update', { where: record.id, data: record });
125+
126+
return record;
123127
}
124128
}
125129

test/integration/VuexORMApollo.spec.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,4 +374,75 @@ mutation SignupUser($user: UserInput!, $captchaToken: String!) {
374374
`.trim() + "\n");
375375
});
376376
});
377+
378+
describe('$isPersisted', () => {
379+
it('is false for newly created records', async () => {
380+
let user = await store.dispatch('entities/users/insert', { data: { name: 'Snoopy' }} );
381+
expect(user.$isPersisted).toBeFalsy();
382+
383+
user = store.getters['entities/users/find'](user.id);
384+
expect(user.$isPersisted).toBeFalsy();
385+
});
386+
387+
it('is true for persisted records', async () => {
388+
const response = {
389+
data: {
390+
createUser: {
391+
__typename: 'user',
392+
id: 1,
393+
name: 'Charlie Brown',
394+
posts: {
395+
__typename: 'post',
396+
nodes: []
397+
}
398+
}
399+
}
400+
};
401+
402+
await sendWithMockFetch(response, async () => {
403+
await store.dispatch('entities/users/persist', { id: 1 });
404+
});
405+
406+
const user = store.getters['entities/users/find'](1);
407+
expect(user.$isPersisted).toBeTruthy();
408+
});
409+
410+
it('is true for fetched records', async () => {
411+
const response = {
412+
data: {
413+
user: {
414+
__typename: 'user',
415+
id: 1,
416+
name: 'Johnny Imba',
417+
posts: {
418+
__typename: 'post',
419+
nodes: [
420+
{
421+
__typename: 'post',
422+
id: 1,
423+
userId: 1,
424+
title: 'Example Post 1',
425+
content: 'Foo'
426+
},
427+
{
428+
__typename: 'post',
429+
id: 2,
430+
userId: 1,
431+
title: 'Example Post 2',
432+
content: 'Bar'
433+
}
434+
]
435+
}
436+
}
437+
}
438+
};
439+
440+
await sendWithMockFetch(response, async () => {
441+
await store.dispatch('entities/users/fetch', { filter: { id: 1 } });
442+
});
443+
444+
const user = store.getters['entities/users/find'](1);
445+
expect(user.$isPersisted).toBeTruthy();
446+
});
447+
});
377448
});

test/support/Helpers.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import _ from 'lodash';
22
import Vue from 'vue';
33
import Vuex from 'vuex';
44
import VuexORM, { Database, Model } from '@vuex-orm/core';
5-
import installVuexORMApollo from 'app';
65
import fetchMock from 'fetch-mock';
76
import VuexORMApolloPlugin from "app";
87

test/unit/QueryBuilder.spec.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ describe('QueryBuilder', () => {
163163

164164

165165
describe('.transformIncomingData', () => {
166-
it('transforms incoming data into Vuex-ORM a readable structure', () => {
166+
it('transforms incoming data into a Vuex-ORM readable structure', () => {
167167
const incomingData = {
168168
"contracts": {
169169
"nodes": [
@@ -218,8 +218,10 @@ describe('QueryBuilder', () => {
218218
const expectedData = {
219219
"contracts": [
220220
{
221+
"$isPersisted": true,
221222
"contractOptions": [
222223
{
224+
"$isPersisted": true,
223225
"description": "Very foo, much more bar",
224226
"id": 1,
225227
"name": "Foo Bar 1",
@@ -231,8 +233,10 @@ describe('QueryBuilder', () => {
231233
"slug": "contract-s",
232234
},
233235
{
236+
"$isPersisted": true,
234237
"contractOptions": [
235238
{
239+
"$isPersisted": true,
236240
"description": "Very foo, much more bar",
237241
"id": 1,
238242
"name": "Foo Bar 1",
@@ -244,8 +248,10 @@ describe('QueryBuilder', () => {
244248
"slug": "contract-m",
245249
},
246250
{
251+
"$isPersisted": true,
247252
"contractOptions": [
248253
{
254+
"$isPersisted": true,
249255
"description": "Very foo, much more bar",
250256
"id": 1,
251257
"name": "Foo Bar 1",
@@ -266,7 +272,7 @@ describe('QueryBuilder', () => {
266272

267273

268274
describe('.transformIncomingData', () => {
269-
it('transforms incoming data after a mutation into Vuex-ORM a readable structure', () => {
275+
it('transforms incoming data after a mutation into a Vuex-ORM readable structure', () => {
270276
const incomingData = {
271277
"createContract": {
272278
"id": "1",
@@ -286,8 +292,10 @@ describe('QueryBuilder', () => {
286292
};
287293
const expectedData = {
288294
"contract": {
295+
"$isPersisted": true,
289296
"contractOptions": [
290297
{
298+
"$isPersisted": true,
291299
"description": "Very foo, much more bar",
292300
"id": 1,
293301
"name": "Foo Bar 1",
@@ -301,7 +309,9 @@ describe('QueryBuilder', () => {
301309
};
302310

303311
const model = vuexOrmApollo.context.getModel('contract');
304-
expect(queryBuilder.transformIncomingData(incomingData, model, true)).toEqual(expectedData);
312+
const transformedData = queryBuilder.transformIncomingData(incomingData, model, true);
313+
314+
expect(transformedData).toEqual(expectedData);
305315
});
306316
});
307317

0 commit comments

Comments
 (0)