Skip to content

Commit 935b913

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 19d7556 + dcf5d24 commit 935b913

File tree

8 files changed

+419
-220
lines changed

8 files changed

+419
-220
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ ___
1717
- IMPROVE: Optimize queries on classes with pointer permissions. [#7061](https://github.com/parse-community/parse-server/pull/7061). Thanks to [Pedro Diaz](https://github.com/pdiaz)
1818
- IMPROVE: Parse Server will from now on be continuously tested against all relevant Postgres versions (minor versions). Added Postgres compatibility table to Parse Server docs. [#7176](https://github.com/parse-community/parse-server/pull/7176). Thanks to [Corey Baker](https://github.com/cbaker6).
1919
- FIX: request.context for afterFind triggers. [#7078](https://github.com/parse-community/parse-server/pull/7078). Thanks to [dblythy](https://github.com/dblythy)
20+
- NEW: `requireAnyUserRoles` and `requireAllUserRoles` for Parse Cloud validator. [#7097](https://github.com/parse-community/parse-server/pull/7097). Thanks to [dblythy](https://github.com/dblythy)
21+
- NEW: Added convenience method Parse.Cloud.sendEmail(...) to send email via email adapter in Cloud Code. [#7089](https://github.com/parse-community/parse-server/pull/7089). Thanks to [dblythy](https://github.com/dblythy)
2022
- FIX: Winston Logger interpolating stdout to console [#7114](https://github.com/parse-community/parse-server/pull/7114). Thanks to [dplewis](https://github.com/dplewis)
2123

2224
### 4.5.0

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ The full documentation for Parse Server is available in the [wiki](https://githu
4444
- [Getting Started](#getting-started)
4545
- [Running Parse Server](#running-parse-server)
4646
- [Compatibility](#compatibility)
47-
- [Node.js](#nodejs-support)
48-
- [MongoDB](#mongodb-support)
49-
- [PostgreSQL](#postgresql-support)
47+
- [Node.js](#nodejs)
48+
- [MongoDB](#mongodb)
49+
- [PostgreSQL](#postgresql)
5050
- [Locally](#locally)
5151
- [Docker Container](#docker-container)
5252
- [Saving an Object](#saving-an-object)
@@ -155,7 +155,7 @@ $ docker run --name my-mongo -d mongo
155155
$ docker run --name my-parse-server -v config-vol:/parse-server/config -p 1337:1337 --link my-mongo:mongo -d parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/test
156156
```
157157

158-
***Note:*** *If you want to use [Cloud Code](https://docs.parseplatform.org/cloudcode/guide/) feature, please add `-v cloud-code-vol:/parse-server/cloud --cloud /parse-server/cloud/main.js` to command above. Make sure the `main.js` file is available in the `cloud-code-vol` directory before run this command. Otherwise, an error will occur.*
158+
***Note:*** *If you want to use [Cloud Code](https://docs.parseplatform.org/cloudcode/guide/), add `-v cloud-code-vol:/parse-server/cloud --cloud /parse-server/cloud/main.js` to the command above. Make sure `main.js` is in the `cloud-code-vol` directory before starting Parse Server.*
159159

160160
You can use any arbitrary string as your application id and master key. These will be used by your clients to authenticate with the Parse Server.
161161

@@ -432,7 +432,7 @@ For the full list of configurable environment variables, run `parse-server --hel
432432

433433
## Available Adapters
434434

435-
All official adapters are distributed as scoped pacakges on [npm (@parse)](https://www.npmjs.com/search?q=scope%3Aparse).
435+
All official adapters are distributed as scoped packages on [npm (@parse)](https://www.npmjs.com/search?q=scope%3Aparse).
436436

437437
Some well maintained adapters are also available on the [Parse Server Modules](https://github.com/parse-server-modules) organization.
438438

@@ -452,9 +452,9 @@ Parse Server allows developers to choose from several options when hosting files
452452

453453
**Caution, this is an experimental feature that may not be appropriate for production.**
454454

455-
This feature deduplicates identical requests that are received by Parse Server mutliple times, typically due to network issues or network adapter access restrictions on mobile operating systems.
455+
This feature deduplicates identical requests that are received by Parse Server multiple times, typically due to network issues or network adapter access restrictions on mobile operating systems.
456456

457-
Identical requests are identified by their request header `X-Parse-Request-Id`. Therefore a client request has to include this header for deduplication to be applied. Requests that do not contain this header cannot be deduplicated and are processed normally by Parse Server. This means rolling out this feature to clients is seamless as Parse Server still processes request without this header when this feature is enbabled.
457+
Identical requests are identified by their request header `X-Parse-Request-Id`. Therefore a client request has to include this header for deduplication to be applied. Requests that do not contain this header cannot be deduplicated and are processed normally by Parse Server. This means rolling out this feature to clients is seamless as Parse Server still processes requests without this header when this feature is enabled.
458458

459459
> This feature needs to be enabled on the client side to send the header and on the server to process the header. Refer to the specific Parse SDK docs to see whether the feature is supported yet.
460460
@@ -474,7 +474,7 @@ let api = new ParseServer({
474474
| Parameter | Optional | Type | Default value | Example values | Environment variable | Description |
475475
|----------------------------|----------|-----------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
476476
| `idempotencyOptions` | yes | `Object` | `undefined` | | PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_OPTIONS | Setting this enables idempotency enforcement for the specified paths. |
477-
| `idempotencyOptions.paths` | yes | `Array<String>` | `[]` | `.*` (all paths, includes the examples below), <br>`functions/.*` (all functions), <br>`jobs/.*` (all jobs), <br>`classes/.*` (all classes), <br>`functions/.*` (all functions), <br>`users` (user creation / update), <br>`installations` (installation creation / update) | PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_PATHS | An array of path patterns that have to match the request path for request deduplication to be enabled. The mount path must not be included, for example to match the request path `/parse/functions/myFunction` specifiy the path pattern `functions/myFunction`. A trailing slash of the request path is ignored, for example the path pattern `functions/myFunction` matches both `/parse/functions/myFunction` and `/parse/functions/myFunction/`. |
477+
| `idempotencyOptions.paths` | yes | `Array<String>` | `[]` | `.*` (all paths, includes the examples below), <br>`functions/.*` (all functions), <br>`jobs/.*` (all jobs), <br>`classes/.*` (all classes), <br>`functions/.*` (all functions), <br>`users` (user creation / update), <br>`installations` (installation creation / update) | PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_PATHS | An array of path patterns that have to match the request path for request deduplication to be enabled. The mount path must not be included, for example to match the request path `/parse/functions/myFunction` specify the path pattern `functions/myFunction`. A trailing slash of the request path is ignored, for example the path pattern `functions/myFunction` matches both `/parse/functions/myFunction` and `/parse/functions/myFunction/`. |
478478
| `idempotencyOptions.ttl` | yes | `Integer` | `300` | `60` (60 seconds) | PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_TTL | The duration in seconds after which a request record is discarded from the database. Duplicate requests due to network issues can be expected to arrive within milliseconds up to several seconds. This value must be greater than `0`. |
479479

480480
### Notes <!-- omit in toc -->
@@ -554,7 +554,7 @@ Pros:
554554
- All files are complete in their content and can be easily opened and previewed by viewing the file in a browser.
555555
556556
Cons:
557-
- In most cases, a localized page differs only slighly from the default page, which could cause a lot of duplicate code that is difficult to maintain.
557+
- In most cases, a localized page differs only slightly from the default page, which could cause a lot of duplicate code that is difficult to maintain.
558558
559559
#### Localization with JSON Resource
560560
@@ -564,7 +564,7 @@ Pages are localized by adding placeholders in the HTML files and providing a JSO
564564
```js
565565
root/
566566
├── public/ // pages base path
567-
│ ├── example.html // the page containg placeholders
567+
│ ├── example.html // the page containing placeholders
568568
├── private/ // folder outside of public scope
569569
│ └── translations.json // JSON resource file
570570
```
@@ -689,7 +689,7 @@ Logs are also viewable in Parse Dashboard.
689689
690690
**Want to log each request and response?** Set the `VERBOSE` environment variable when starting `parse-server`. Usage :- `VERBOSE='1' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
691691
692-
**Want logs to be in placed in a different folder?** Pass the `PARSE_SERVER_LOGS_FOLDER` environment variable when starting `parse-server`. Usage :- `PARSE_SERVER_LOGS_FOLDER='<path-to-logs-folder>' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
692+
**Want logs to be placed in a different folder?** Pass the `PARSE_SERVER_LOGS_FOLDER` environment variable when starting `parse-server`. Usage :- `PARSE_SERVER_LOGS_FOLDER='<path-to-logs-folder>' parse-server --appId APPLICATION_ID --masterKey MASTER_KEY`
693693
694694
**Want to log specific levels?** Pass the `logLevel` parameter when starting `parse-server`. Usage :- `parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --logLevel LOG_LEVEL`
695695
@@ -738,7 +738,7 @@ $ docker run --name my-mongo -d mongo
738738
$ docker run --name my-parse-server --link my-mongo:mongo -v config-vol:/parse-server/config -p 1337:1337 -d parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/test --publicServerURL http://localhost:1337/parse --mountGraphQL --mountPlayground
739739
```
740740
741-
***Note:*** *If you want to use [Cloud Code](https://docs.parseplatform.org/cloudcode/guide/) feature, please add `-v cloud-code-vol:/parse-server/cloud --cloud /parse-server/cloud/main.js` to command above. Make sure the `main.js` file is available in the `cloud-code-vol` directory before run this command. Otherwise, an error will occur.*
741+
***Note:*** *If you want to use [Cloud Code](https://docs.parseplatform.org/cloudcode/guide/), add `-v cloud-code-vol:/parse-server/cloud --cloud /parse-server/cloud/main.js` to the command above. Make sure `main.js` is in the `cloud-code-vol` directory before starting Parse Server.*
742742
743743
After starting the server, you can visit http://localhost:1337/playground in your browser to start playing with your GraphQL API.
744744

spec/CloudCode.Validator.spec.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,150 @@ describe('cloud validator', () => {
878878
});
879879
});
880880

881+
it('basic validator requireAnyUserRoles', async function (done) {
882+
Parse.Cloud.define(
883+
'cloudFunction',
884+
() => {
885+
return true;
886+
},
887+
{
888+
requireUser: true,
889+
requireAnyUserRoles: ['Admin'],
890+
}
891+
);
892+
const user = await Parse.User.signUp('testuser', 'p@ssword');
893+
try {
894+
await Parse.Cloud.run('cloudFunction');
895+
fail('cloud validator should have failed.');
896+
} catch (e) {
897+
expect(e.message).toBe('Validation failed. User does not match the required roles.');
898+
}
899+
const roleACL = new Parse.ACL();
900+
roleACL.setPublicReadAccess(true);
901+
const role = new Parse.Role('Admin', roleACL);
902+
role.getUsers().add(user);
903+
await role.save({ useMasterKey: true });
904+
await Parse.Cloud.run('cloudFunction');
905+
done();
906+
});
907+
908+
it('basic validator requireAllUserRoles', async function (done) {
909+
Parse.Cloud.define(
910+
'cloudFunction',
911+
() => {
912+
return true;
913+
},
914+
{
915+
requireUser: true,
916+
requireAllUserRoles: ['Admin', 'Admin2'],
917+
}
918+
);
919+
const user = await Parse.User.signUp('testuser', 'p@ssword');
920+
try {
921+
await Parse.Cloud.run('cloudFunction');
922+
fail('cloud validator should have failed.');
923+
} catch (e) {
924+
expect(e.message).toBe('Validation failed. User does not match all the required roles.');
925+
}
926+
const roleACL = new Parse.ACL();
927+
roleACL.setPublicReadAccess(true);
928+
const role = new Parse.Role('Admin', roleACL);
929+
role.getUsers().add(user);
930+
931+
const role2 = new Parse.Role('Admin2', roleACL);
932+
role2.getUsers().add(user);
933+
await Promise.all([role.save({ useMasterKey: true }), role2.save({ useMasterKey: true })]);
934+
await Parse.Cloud.run('cloudFunction');
935+
done();
936+
});
937+
938+
it('allow requireAnyUserRoles to be a function', async function (done) {
939+
Parse.Cloud.define(
940+
'cloudFunction',
941+
() => {
942+
return true;
943+
},
944+
{
945+
requireUser: true,
946+
requireAnyUserRoles: () => {
947+
return ['Admin Func'];
948+
},
949+
}
950+
);
951+
const user = await Parse.User.signUp('testuser', 'p@ssword');
952+
try {
953+
await Parse.Cloud.run('cloudFunction');
954+
fail('cloud validator should have failed.');
955+
} catch (e) {
956+
expect(e.message).toBe('Validation failed. User does not match the required roles.');
957+
}
958+
const roleACL = new Parse.ACL();
959+
roleACL.setPublicReadAccess(true);
960+
const role = new Parse.Role('Admin Func', roleACL);
961+
role.getUsers().add(user);
962+
await role.save({ useMasterKey: true });
963+
await Parse.Cloud.run('cloudFunction');
964+
done();
965+
});
966+
967+
it('allow requireAllUserRoles to be a function', async function (done) {
968+
Parse.Cloud.define(
969+
'cloudFunction',
970+
() => {
971+
return true;
972+
},
973+
{
974+
requireUser: true,
975+
requireAllUserRoles: () => {
976+
return ['AdminA', 'AdminB'];
977+
},
978+
}
979+
);
980+
const user = await Parse.User.signUp('testuser', 'p@ssword');
981+
try {
982+
await Parse.Cloud.run('cloudFunction');
983+
fail('cloud validator should have failed.');
984+
} catch (e) {
985+
expect(e.message).toBe('Validation failed. User does not match all the required roles.');
986+
}
987+
const roleACL = new Parse.ACL();
988+
roleACL.setPublicReadAccess(true);
989+
const role = new Parse.Role('AdminA', roleACL);
990+
role.getUsers().add(user);
991+
992+
const role2 = new Parse.Role('AdminB', roleACL);
993+
role2.getUsers().add(user);
994+
await Promise.all([role.save({ useMasterKey: true }), role2.save({ useMasterKey: true })]);
995+
await Parse.Cloud.run('cloudFunction');
996+
done();
997+
});
998+
999+
it('basic requireAllUserRoles but no user', async function (done) {
1000+
Parse.Cloud.define(
1001+
'cloudFunction',
1002+
() => {
1003+
return true;
1004+
},
1005+
{
1006+
requireAllUserRoles: ['Admin'],
1007+
}
1008+
);
1009+
try {
1010+
await Parse.Cloud.run('cloudFunction');
1011+
fail('cloud validator should have failed.');
1012+
} catch (e) {
1013+
expect(e.message).toBe('Validation failed. Please login to continue.');
1014+
}
1015+
const user = await Parse.User.signUp('testuser', 'p@ssword');
1016+
const roleACL = new Parse.ACL();
1017+
roleACL.setPublicReadAccess(true);
1018+
const role = new Parse.Role('Admin', roleACL);
1019+
role.getUsers().add(user);
1020+
await role.save({ useMasterKey: true });
1021+
await Parse.Cloud.run('cloudFunction');
1022+
done();
1023+
});
1024+
8811025
it('basic beforeSave requireMaster', function (done) {
8821026
Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
8831027
requireMaster: true,

spec/CloudCode.spec.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,39 @@ describe('beforeSave hooks', () => {
17791779
const myObject = new MyObject();
17801780
myObject.save().then(() => done());
17811781
});
1782+
1783+
it('should respect custom object ids (#6733)', async () => {
1784+
Parse.Cloud.beforeSave('TestObject', req => {
1785+
expect(req.object.id).toEqual('test_6733');
1786+
});
1787+
1788+
await reconfigureServer({ allowCustomObjectId: true });
1789+
1790+
const req = request({
1791+
// Parse JS SDK does not currently support custom object ids (see #1097), so we do a REST request
1792+
method: 'POST',
1793+
url: 'http://localhost:8378/1/classes/TestObject',
1794+
headers: {
1795+
'X-Parse-Application-Id': 'test',
1796+
'X-Parse-REST-API-Key': 'rest',
1797+
},
1798+
body: {
1799+
objectId: 'test_6733',
1800+
foo: 'bar',
1801+
},
1802+
});
1803+
1804+
{
1805+
const res = await req;
1806+
expect(res.data.objectId).toEqual('test_6733');
1807+
}
1808+
1809+
const query = new Parse.Query('TestObject');
1810+
query.equalTo('objectId', 'test_6733');
1811+
const res = await query.find();
1812+
expect(res.length).toEqual(1);
1813+
expect(res[0].get('foo')).toEqual('bar');
1814+
});
17821815
});
17831816

17841817
describe('afterSave hooks', () => {

0 commit comments

Comments
 (0)