Skip to content

Commit 8855cad

Browse files
authored
Merge branch 'alpha' into snyk-upgrade-c97562de90cebc943957a538dd01b70b
2 parents 3c48274 + e0cca58 commit 8855cad

9 files changed

+215
-40
lines changed

changelogs/CHANGELOG_alpha.md

+14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## [5.2.1-alpha.2](https://github.com/parse-community/parse-server/compare/5.2.1-alpha.1...5.2.1-alpha.2) (2022-03-26)
2+
3+
4+
### Performance Improvements
5+
6+
* reduce database operations when using the constant parameter in Cloud Function validation ([#7892](https://github.com/parse-community/parse-server/issues/7892)) ([48bd512](https://github.com/parse-community/parse-server/commit/48bd512eeb47666967dff8c5e723ddc5b7801daa))
7+
8+
## [5.2.1-alpha.1](https://github.com/parse-community/parse-server/compare/5.2.0...5.2.1-alpha.1) (2022-03-26)
9+
10+
11+
### Bug Fixes
12+
13+
* return correct response when revert is used in beforeSave ([#7839](https://github.com/parse-community/parse-server/issues/7839)) ([f63fb2b](https://github.com/parse-community/parse-server/commit/f63fb2b338c908f0e7a648d338c26b9daa50c8f2))
14+
115
# [5.2.0-alpha.3](https://github.com/parse-community/parse-server/compare/5.2.0-alpha.2...5.2.0-alpha.3) (2022-03-24)
216

317

changelogs/CHANGELOG_beta.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# [5.2.0-beta.2](https://github.com/parse-community/parse-server/compare/5.2.0-beta.1...5.2.0-beta.2) (2022-03-24)
2+
3+
4+
### Bug Fixes
5+
6+
* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
7+
* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))
8+
19
# [5.2.0-beta.1](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0-beta.1) (2022-03-23)
210

311

changelogs/CHANGELOG_release.md

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# [5.2.0](https://github.com/parse-community/parse-server/compare/5.1.1...5.2.0) (2022-03-24)
2+
3+
4+
### Bug Fixes
5+
6+
* security bump minimist from 1.2.5 to 1.2.6 ([#7884](https://github.com/parse-community/parse-server/issues/7884)) ([c5cf282](https://github.com/parse-community/parse-server/commit/c5cf282d11ffdc023764f8e7539a2bd6bc246fe1))
7+
* sensitive keyword detection may produce false positives ([#7881](https://github.com/parse-community/parse-server/issues/7881)) ([0d6f9e9](https://github.com/parse-community/parse-server/commit/0d6f9e951d9e186e95e96d8869066ce7022bad02))
8+
9+
### Features
10+
11+
* improved LiveQuery error logging with additional information ([#7837](https://github.com/parse-community/parse-server/issues/7837)) ([443a509](https://github.com/parse-community/parse-server/commit/443a5099059538d379fe491793a5871fcbb4f377))
12+
113
## [5.1.1](https://github.com/parse-community/parse-server/compare/5.1.0...5.1.1) (2022-03-18)
214

315

ci/nodeEngineCheck.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,21 @@ class NodeEngineCheck {
7575

7676
// For each file
7777
for (const file of files) {
78-
7978
// Get node version
8079
const contentString = await fs.readFile(file, 'utf-8');
81-
const contentJson = JSON.parse(contentString);
82-
const version = ((contentJson || {}).engines || {}).node;
83-
84-
// Add response
85-
response.push({
86-
file: file,
87-
nodeVersion: version
88-
});
80+
try {
81+
const contentJson = JSON.parse(contentString);
82+
const version = ((contentJson || {}).engines || {}).node;
83+
84+
// Add response
85+
response.push({
86+
file: file,
87+
nodeVersion: version
88+
});
89+
} catch(e) {
90+
console.log(`Ignoring file because it is not valid JSON: ${file}`);
91+
core.warning(`Ignoring file because it is not valid JSON: ${file}`);
92+
}
8993
}
9094

9195
// If results should be cleaned by removing undefined node versions

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "parse-server",
3-
"version": "5.2.0-alpha.3",
3+
"version": "5.2.1-alpha.2",
44
"description": "An express module providing a Parse-compatible API server",
55
"main": "lib/index.js",
66
"repository": {

spec/CloudCode.spec.js

+134
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,110 @@ describe('Cloud Code', () => {
14941494
});
14951495
});
14961496

1497+
it('before save can revert fields', async () => {
1498+
Parse.Cloud.beforeSave('TestObject', ({ object }) => {
1499+
object.revert('foo');
1500+
return object;
1501+
});
1502+
1503+
Parse.Cloud.afterSave('TestObject', ({ object }) => {
1504+
expect(object.get('foo')).toBeUndefined();
1505+
return object;
1506+
});
1507+
1508+
const obj = new TestObject();
1509+
obj.set('foo', 'bar');
1510+
await obj.save();
1511+
1512+
expect(obj.get('foo')).toBeUndefined();
1513+
await obj.fetch();
1514+
1515+
expect(obj.get('foo')).toBeUndefined();
1516+
});
1517+
1518+
it('before save can revert fields with existing object', async () => {
1519+
Parse.Cloud.beforeSave(
1520+
'TestObject',
1521+
({ object }) => {
1522+
object.revert('foo');
1523+
return object;
1524+
},
1525+
{
1526+
skipWithMasterKey: true,
1527+
}
1528+
);
1529+
1530+
Parse.Cloud.afterSave(
1531+
'TestObject',
1532+
({ object }) => {
1533+
expect(object.get('foo')).toBe('bar');
1534+
return object;
1535+
},
1536+
{
1537+
skipWithMasterKey: true,
1538+
}
1539+
);
1540+
1541+
const obj = new TestObject();
1542+
obj.set('foo', 'bar');
1543+
await obj.save(null, { useMasterKey: true });
1544+
1545+
expect(obj.get('foo')).toBe('bar');
1546+
obj.set('foo', 'yolo');
1547+
await obj.save();
1548+
expect(obj.get('foo')).toBe('bar');
1549+
});
1550+
1551+
it('can unset in afterSave', async () => {
1552+
Parse.Cloud.beforeSave('TestObject', ({ object }) => {
1553+
if (!object.existed()) {
1554+
object.set('secret', true);
1555+
return object;
1556+
}
1557+
object.revert('secret');
1558+
});
1559+
1560+
Parse.Cloud.afterSave('TestObject', ({ object }) => {
1561+
object.unset('secret');
1562+
});
1563+
1564+
Parse.Cloud.beforeFind(
1565+
'TestObject',
1566+
({ query }) => {
1567+
query.exclude('secret');
1568+
},
1569+
{
1570+
skipWithMasterKey: true,
1571+
}
1572+
);
1573+
1574+
const obj = new TestObject();
1575+
await obj.save();
1576+
expect(obj.get('secret')).toBeUndefined();
1577+
await obj.fetch();
1578+
expect(obj.get('secret')).toBeUndefined();
1579+
await obj.fetch({ useMasterKey: true });
1580+
expect(obj.get('secret')).toBe(true);
1581+
});
1582+
1583+
it('should revert in beforeSave', async () => {
1584+
Parse.Cloud.beforeSave('MyObject', ({ object }) => {
1585+
if (!object.existed()) {
1586+
object.set('count', 0);
1587+
return object;
1588+
}
1589+
object.revert('count');
1590+
return object;
1591+
});
1592+
const obj = await new Parse.Object('MyObject').save();
1593+
expect(obj.get('count')).toBe(0);
1594+
obj.set('count', 10);
1595+
await obj.save();
1596+
expect(obj.get('count')).toBe(0);
1597+
await obj.fetch();
1598+
expect(obj.get('count')).toBe(0);
1599+
});
1600+
14971601
it('beforeSave should not sanitize database', async done => {
14981602
const { adapter } = Config.get(Parse.applicationId).database;
14991603
const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough();
@@ -1860,6 +1964,36 @@ describe('afterSave hooks', () => {
18601964
const myObject = new MyObject();
18611965
myObject.save().then(() => done());
18621966
});
1967+
1968+
it('should unset in afterSave', async () => {
1969+
Parse.Cloud.afterSave(
1970+
'MyObject',
1971+
({ object }) => {
1972+
object.unset('secret');
1973+
},
1974+
{
1975+
skipWithMasterKey: true,
1976+
}
1977+
);
1978+
const obj = new Parse.Object('MyObject');
1979+
obj.set('secret', 'bar');
1980+
await obj.save();
1981+
expect(obj.get('secret')).toBeUndefined();
1982+
await obj.fetch();
1983+
expect(obj.get('secret')).toBe('bar');
1984+
});
1985+
1986+
it('should unset', async () => {
1987+
Parse.Cloud.beforeSave('MyObject', ({ object }) => {
1988+
object.set('secret', 'hidden');
1989+
});
1990+
1991+
Parse.Cloud.afterSave('MyObject', ({ object }) => {
1992+
object.unset('secret');
1993+
});
1994+
const obj = await new Parse.Object('MyObject').save();
1995+
expect(obj.get('secret')).toBeUndefined();
1996+
});
18631997
});
18641998

18651999
describe('beforeDelete hooks', () => {

src/RestWrite.js

+31-28
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ function RestWrite(config, auth, className, query, data, originalData, clientSDK
9595
// Shared SchemaController to be reused to reduce the number of loadSchema() calls per request
9696
// Once set the schemaData should be immutable
9797
this.validSchemaController = null;
98+
this.pendingOps = {};
9899
}
99100

100101
// A convenient method to perform all the steps of processing the
@@ -225,18 +226,11 @@ RestWrite.prototype.runBeforeSaveTrigger = function () {
225226
return Promise.resolve();
226227
}
227228

228-
// Cloud code gets a bit of extra data for its objects
229-
var extraData = { className: this.className };
230-
if (this.query && this.query.objectId) {
231-
extraData.objectId = this.query.objectId;
232-
}
229+
const { originalObject, updatedObject } = this.buildParseObjects();
233230

234-
let originalObject = null;
235-
const updatedObject = this.buildUpdatedObject(extraData);
236-
if (this.query && this.query.objectId) {
237-
// This is an update for existing object.
238-
originalObject = triggers.inflate(extraData, this.originalData);
239-
}
231+
const stateController = Parse.CoreManager.getObjectStateController();
232+
const [pending] = stateController.getPendingOps(updatedObject._getStateIdentifier());
233+
this.pendingOps = { ...pending };
240234

241235
return Promise.resolve()
242236
.then(() => {
@@ -1531,20 +1525,7 @@ RestWrite.prototype.runAfterSaveTrigger = function () {
15311525
return Promise.resolve();
15321526
}
15331527

1534-
var extraData = { className: this.className };
1535-
if (this.query && this.query.objectId) {
1536-
extraData.objectId = this.query.objectId;
1537-
}
1538-
1539-
// Build the original object, we only do this for a update write.
1540-
let originalObject;
1541-
if (this.query && this.query.objectId) {
1542-
originalObject = triggers.inflate(extraData, this.originalData);
1543-
}
1544-
1545-
// Build the inflated object, different from beforeSave, originalData is not empty
1546-
// since developers can change data in the beforeSave.
1547-
const updatedObject = this.buildUpdatedObject(extraData);
1528+
const { originalObject, updatedObject } = this.buildParseObjects();
15481529
updatedObject._handleSaveResponse(this.response.response, this.response.status || 200);
15491530

15501531
this.config.database.loadSchema().then(schemaController => {
@@ -1569,8 +1550,15 @@ RestWrite.prototype.runAfterSaveTrigger = function () {
15691550
this.context
15701551
)
15711552
.then(result => {
1572-
if (result && typeof result === 'object') {
1553+
const jsonReturned = result && !result._toFullJSON;
1554+
if (jsonReturned) {
1555+
this.pendingOps = {};
15731556
this.response.response = result;
1557+
} else {
1558+
this.response.response = this._updateResponseWithData(
1559+
(result || updatedObject)._toFullJSON(),
1560+
this.data
1561+
);
15741562
}
15751563
})
15761564
.catch(function (err) {
@@ -1604,7 +1592,13 @@ RestWrite.prototype.sanitizedData = function () {
16041592
};
16051593

16061594
// Returns an updated copy of the object
1607-
RestWrite.prototype.buildUpdatedObject = function (extraData) {
1595+
RestWrite.prototype.buildParseObjects = function () {
1596+
const extraData = { className: this.className, objectId: this.query?.objectId };
1597+
let originalObject;
1598+
if (this.query && this.query.objectId) {
1599+
originalObject = triggers.inflate(extraData, this.originalData);
1600+
}
1601+
16081602
const className = Parse.Object.fromJSON(extraData);
16091603
const readOnlyAttributes = className.constructor.readOnlyAttributes
16101604
? className.constructor.readOnlyAttributes()
@@ -1642,7 +1636,7 @@ RestWrite.prototype.buildUpdatedObject = function (extraData) {
16421636
delete sanitized[attribute];
16431637
}
16441638
updatedObject.set(sanitized);
1645-
return updatedObject;
1639+
return { updatedObject, originalObject };
16461640
};
16471641

16481642
RestWrite.prototype.cleanUserAuthData = function () {
@@ -1662,6 +1656,15 @@ RestWrite.prototype.cleanUserAuthData = function () {
16621656
};
16631657

16641658
RestWrite.prototype._updateResponseWithData = function (response, data) {
1659+
const { updatedObject } = this.buildParseObjects();
1660+
const stateController = Parse.CoreManager.getObjectStateController();
1661+
const [pending] = stateController.getPendingOps(updatedObject._getStateIdentifier());
1662+
for (const key in this.pendingOps) {
1663+
if (!pending[key]) {
1664+
data[key] = this.originalData ? this.originalData[key] : { __op: 'Delete' };
1665+
this.storage.fieldsChangedByTrigger.push(key);
1666+
}
1667+
}
16651668
if (_.isEmpty(this.storage.fieldsChangedByTrigger)) {
16661669
return response;
16671670
}

src/triggers.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ async function builtInTriggerValidator(options, request, auth) {
738738
}
739739
if (opt.constant && request.object) {
740740
if (request.original) {
741-
request.object.set(key, request.original.get(key));
741+
request.object.revert(key);
742742
} else if (opt.default != null) {
743743
request.object.set(key, opt.default);
744744
}

0 commit comments

Comments
 (0)