Skip to content

Commit 7a87e09

Browse files
authored
Merge branch 'master' into greenkeeper/initial
2 parents 36869e1 + 223d7aa commit 7a87e09

16 files changed

+450
-20
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![Npm Version][npm-svg]][npm-link]
77
[![License][license-svg]][license-link]
88

9-
A library that gives you access to the powerful Parse cloud platform from your JavaScript app. For more information on Parse and its features, see [the website](https://parse.com) or [the JavaScript guide](https://parseplatform.github.io/docs/js/guide/).
9+
A library that gives you access to the powerful Parse cloud platform from your JavaScript app. For more information on Parse and its features, see [the website](http://parseplatform.org) or [the JavaScript guide](https://docs.parseplatform.org/js/guide/).
1010

1111
## Getting Started
1212

@@ -36,18 +36,20 @@ For React Native applications, include `'parse/react-native'`:
3636
var Parse = require('parse/react-native');
3737
```
3838

39+
> As of `v1.9.3`, Parse-SDK-JS supports React Native 0.43+. Please use `v1.9.2` for previous versions of React Native.
40+
3941
## License
4042

4143
```
4244
Copyright (c) 2015-present, Parse, LLC.
4345
All rights reserved.
4446
4547
This source code is licensed under the BSD-style license found in the
46-
LICENSE file in the root directory of this source tree. An additional grant
48+
LICENSE file in the root directory of this source tree. An additional grant
4749
of patent rights can be found in the PATENTS file in the same directory.
4850
```
4951

50-
As of April 5, 2017, Parse, LLC has transferred this code to the parse-community organization, and will no longer be contributing to or distributing this code.
52+
As of April 5, 2017, Parse, LLC has transferred this code to the parse-community organization, and will no longer be contributing to or distributing this code.
5153

5254
[build-status-svg]: https://travis-ci.org/parse-community/Parse-SDK-JS.svg?branch=master
5355
[build-status-link]: https://travis-ci.org/parse-community/Parse-SDK-JS

integration/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"dependencies": {
44
"express": "^4.13.4",
55
"mocha": "^2.4.5",
6-
"parse-server": "2.2.17"
6+
"parse-server": "2.4.2"
77
},
88
"scripts": {
99
"test": "mocha --reporter dot -t 5000"

integration/test/ParseGeoBoxTest.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ describe('Geo Box', () => {
1111
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
1212
Parse.Storage._clear();
1313
});
14-
15-
beforeEach((done) => {
14+
15+
beforeEach((done) => {
1616
clear().then(() => {
17-
done();
17+
Parse.User.logOut().then(() => { done() }, () => { done() });
1818
});
1919
});
2020

integration/test/ParseGeoPointTest.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const TestObject = Parse.Object.extend('TestObject');
99
const TestPoint = Parse.Object.extend('TestPoint');
1010

1111
describe('Geo Point', () => {
12-
before(() => {
12+
before((done) => {
1313
Parse.initialize('integration');
1414
Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse');
1515
Parse.Storage._clear();
@@ -28,8 +28,8 @@ describe('Geo Point', () => {
2828

2929
return Parse.Object.saveAll([sacramento, honolulu, sf]);
3030
}).then(() => {
31-
done();
32-
});
31+
return Parse.User.logOut();
32+
}).then(() => { done() }, () => { done() });
3333
});
3434

3535
it('can save geo points', (done) => {

integration/test/ParseObjectTest.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,8 +1183,8 @@ describe('Parse Object', () => {
11831183
}).then(() => {
11841184
assert.equal(user.createdAt.getTime(), sameUser.createdAt.getTime());
11851185
assert.equal(user.updatedAt.getTime(), sameUser.updatedAt.getTime());
1186-
done();
1187-
});
1186+
return Parse.User.logOut().then(() => { done(); }, () => { done(); });
1187+
}).catch(done.fail);
11881188
});
11891189

11901190
it('can fetchAllIfNeeded', (done) => {

integration/test/ParseQueryTest.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ describe('Parse Query', () => {
1919
}
2020
return Parse.Object.saveAll(numbers);
2121
}).then(() => {
22-
done();
23-
});
22+
return Parse.User.logOut();
23+
})
24+
.then(() => { done() }, () => { done() });
2425
});
2526

2627
it('can do basic queries', (done) => {
@@ -1182,6 +1183,10 @@ describe('Parse Query', () => {
11821183
assert(o.get('x') <= 15);
11831184
});
11841185
done();
1186+
})
1187+
.catch(err => {
1188+
console.dir(err);
1189+
done.fail();
11851190
});
11861191
});
11871192

src/ParseError.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export default class ParseError {
1919
this.code = code;
2020
this.message = message;
2121
}
22+
23+
toString() {
24+
return 'ParseError: ' + this.code + ' ' + this.message;
25+
}
2226
}
2327

2428
/**

src/ParseLiveQuery.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ const DefaultLiveQueryController = {
196196
subscription.on('delete', (object) => {
197197
subscriptionWrap.emit('delete', object);
198198
});
199+
subscription.on('close', (object) => {
200+
subscriptionWrap.emit('close', object);
201+
});
202+
subscription.on('error', (object) => {
203+
subscriptionWrap.emit('error', object);
204+
});
199205

200206
this.resolve();
201207
});

src/ParseQuery.js

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,70 @@ function quote(s: string) {
4444
return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E';
4545
}
4646

47+
/**
48+
* Handles pre-populating the result data of a query with select fields,
49+
* making sure that the data object contains keys for all objects that have
50+
* been requested with a select, so that our cached state updates correctly.
51+
*/
52+
function handleSelectResult(data: any, select: Array<string>){
53+
var serverDataMask = {};
54+
55+
select.forEach((field) => {
56+
let hasSubObjectSelect = field.indexOf(".") !== -1;
57+
if (!hasSubObjectSelect && !data.hasOwnProperty(field)){
58+
// this field was selected, but is missing from the retrieved data
59+
data[field] = undefined
60+
} else if (hasSubObjectSelect) {
61+
// this field references a sub-object,
62+
// so we need to walk down the path components
63+
let pathComponents = field.split(".");
64+
var obj = data;
65+
var serverMask = serverDataMask;
66+
67+
pathComponents.forEach((component, index, arr) => {
68+
// add keys if the expected data is missing
69+
if (!obj[component]) {
70+
obj[component] = (index == arr.length-1) ? undefined : {};
71+
}
72+
obj = obj[component];
73+
74+
//add this path component to the server mask so we can fill it in later if needed
75+
if (index < arr.length-1) {
76+
if (!serverMask[component]) {
77+
serverMask[component] = {};
78+
}
79+
}
80+
});
81+
}
82+
});
83+
84+
if (Object.keys(serverDataMask).length > 0) {
85+
// When selecting from sub-objects, we don't want to blow away the missing
86+
// information that we may have retrieved before. We've already added any
87+
// missing selected keys to sub-objects, but we still need to add in the
88+
// data for any previously retrieved sub-objects that were not selected.
89+
90+
let serverData = CoreManager.getObjectStateController().getServerData({id:data.objectId, className:data.className});
91+
92+
function copyMissingDataWithMask(src, dest, mask, copyThisLevel){
93+
//copy missing elements at this level
94+
if (copyThisLevel) {
95+
for (var key in src) {
96+
if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) {
97+
dest[key] = src[key]
98+
}
99+
}
100+
}
101+
for (var key in mask) {
102+
//traverse into objects as needed
103+
copyMissingDataWithMask(src[key], dest[key], mask[key], true);
104+
}
105+
}
106+
107+
copyMissingDataWithMask(serverData, data, serverDataMask, false);
108+
}
109+
}
110+
47111
/**
48112
* Creates a new parse Parse.Query for the given Parse.Object subclass.
49113
* @class Parse.Query
@@ -273,6 +337,8 @@ export default class ParseQuery {
273337

274338
let controller = CoreManager.getQueryController();
275339

340+
let select = this._select;
341+
276342
return controller.find(
277343
this.className,
278344
this.toJSON(),
@@ -285,7 +351,15 @@ export default class ParseQuery {
285351
if (!data.className) {
286352
data.className = override;
287353
}
288-
return ParseObject.fromJSON(data, true);
354+
355+
// Make sure the data object contains keys for all objects that
356+
// have been requested with a select, so that our cached state
357+
// updates correctly.
358+
if (select) {
359+
handleSelectResult(data, select);
360+
}
361+
362+
return ParseObject.fromJSON(data, !select);
289363
});
290364
})._thenRunCallbacks(options);
291365
}
@@ -371,6 +445,8 @@ export default class ParseQuery {
371445
var params = this.toJSON();
372446
params.limit = 1;
373447

448+
var select = this._select;
449+
374450
return controller.find(
375451
this.className,
376452
params,
@@ -383,7 +459,15 @@ export default class ParseQuery {
383459
if (!objects[0].className) {
384460
objects[0].className = this.className;
385461
}
386-
return ParseObject.fromJSON(objects[0], true);
462+
463+
// Make sure the data object contains keys for all objects that
464+
// have been requested with a select, so that our cached state
465+
// updates correctly.
466+
if (select) {
467+
handleSelectResult(objects[0], select);
468+
}
469+
470+
return ParseObject.fromJSON(objects[0], !select);
387471
})._thenRunCallbacks(options);
388472
}
389473

src/ParseUser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ export default class ParseUser extends ParseObject {
655655
static logOut() {
656656
if (!canUseCurrentUser) {
657657
throw new Error(
658-
'There is no current user user on a node.js server environment.'
658+
'There is no current user on a node.js server environment.'
659659
);
660660
}
661661

src/StorageController.react-native.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import ParsePromise from './ParsePromise';
1313
// RN packager nonsense
14-
import { AsyncStorage } from 'react-native/Libraries/react-native/react-native.js';
14+
import { AsyncStorage } from 'react-native/Libraries/react-native/react-native-implementation';
1515

1616
var StorageController = {
1717
async: 1,

src/__tests__/ParseError-test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2015-present, Parse, LLC.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
jest.dontMock('../ParseError');
11+
12+
var ParseError = require('../ParseError').default;
13+
14+
describe('ParseError', () => {
15+
it('have sensible string representation', () => {
16+
var error = new ParseError(123, 'some error message');
17+
18+
expect(error.toString()).toMatch('ParseError');
19+
expect(error.toString()).toMatch('123');
20+
expect(error.toString()).toMatch('some error message');
21+
});
22+
});

src/__tests__/ParseLiveQuery-test.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@ jest.dontMock('../ParseLiveQuery');
1111
jest.dontMock('../CoreManager');
1212
jest.dontMock('../ParsePromise');
1313
jest.dontMock('../LiveQueryClient');
14+
jest.dontMock('../LiveQuerySubscription');
1415
jest.dontMock('../ParseObject');
16+
jest.dontMock('../ParsePromise');
17+
jest.dontMock('../ParseQuery');
18+
jest.dontMock('../EventEmitter');
1519

1620
const ParseLiveQuery = require('../ParseLiveQuery');
1721
const CoreManager = require('../CoreManager');
1822
const ParsePromise = require('../ParsePromise').default;
23+
const ParseQuery = require('../ParseQuery').default;
1924

2025
describe('ParseLiveQuery', () => {
2126
beforeEach(() => {
@@ -99,4 +104,83 @@ describe('ParseLiveQuery', () => {
99104
done();
100105
});
101106
});
107+
108+
it('subscribes to all subscription events', (done) => {
109+
110+
CoreManager.set('UserController', {
111+
currentUserAsync() {
112+
return ParsePromise.as({
113+
getSessionToken() {
114+
return 'token';
115+
}
116+
});
117+
}
118+
});
119+
CoreManager.set('APPLICATION_ID', 'appid');
120+
CoreManager.set('JAVASCRIPT_KEY', 'jskey');
121+
CoreManager.set('LIVEQUERY_SERVER_URL', null);
122+
123+
const controller = CoreManager.getLiveQueryController();
124+
125+
controller.getDefaultLiveQueryClient().then((client) => {
126+
127+
const query = new ParseQuery("ObjectType");
128+
query.equalTo("test", "value");
129+
const ourSubscription = controller.subscribe(query, "close");
130+
131+
var isCalled = {};
132+
["open",
133+
"close",
134+
"error",
135+
"create",
136+
"update",
137+
"enter",
138+
"leave",
139+
"delete"].forEach((key) =>{
140+
ourSubscription.on(key, () => {
141+
isCalled[key] = true;
142+
});
143+
});
144+
145+
// controller.subscribe() completes asynchronously,
146+
// so we need to give it a chance to complete before finishing
147+
setTimeout(() => {
148+
try {
149+
client.connectPromise.resolve();
150+
var actualSubscription = client.subscriptions.get(1);
151+
152+
expect(actualSubscription).toBeDefined();
153+
154+
actualSubscription.emit("open");
155+
expect(isCalled["open"]).toBe(true);
156+
157+
actualSubscription.emit("close");
158+
expect(isCalled["close"]).toBe(true);
159+
160+
actualSubscription.emit("error");
161+
expect(isCalled["error"]).toBe(true);
162+
163+
actualSubscription.emit("create");
164+
expect(isCalled["create"]).toBe(true);
165+
166+
actualSubscription.emit("update");
167+
expect(isCalled["update"]).toBe(true);
168+
169+
actualSubscription.emit("enter");
170+
expect(isCalled["enter"]).toBe(true);
171+
172+
actualSubscription.emit("leave");
173+
expect(isCalled["leave"]).toBe(true);
174+
175+
actualSubscription.emit("delete");
176+
expect(isCalled["delete"]).toBe(true);
177+
178+
done();
179+
} catch(e){
180+
done.fail(e);
181+
}
182+
}, 1);
183+
});
184+
185+
});
102186
});

0 commit comments

Comments
 (0)