Skip to content

Commit ae1a822

Browse files
authored
Removes need to use babel-register (#4865)
* Removes need to use babel-register - Adds watch to watch changes when running the test to regenerate - Tests are now pure node 8 * Adds timing to helper.js * Update contribution guide * Adds inline sourcemaps generation to restore coverage * nits
1 parent 74740ab commit ae1a822

File tree

86 files changed

+260
-225
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+260
-225
lines changed

.babelrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
"node": "8"
1010
}
1111
}]
12-
]
12+
],
13+
"sourceMaps": "inline"
1314
}

.nycrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
],
66
"exclude": [
77
"**/spec/**",
8-
"lib/"
8+
"src/"
99
]
1010
}
1111

CONTRIBUTING.md

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,49 @@
1-
### Contributing to Parse Server
1+
# Contributing to Parse Server
22

3-
#### Pull Requests Welcome!
3+
We really want Parse to be yours, to see it grow and thrive in the open source community.
44

5-
We really want Parse to be yours, to see it grow and thrive in the open source community.
5+
If you are not familiar with Pull Requests and want to know more about them, you can visit the [Creating a pull request](https://help.github.com/articles/creating-a-pull-request/) article. It contains detailed informations about the process.
66

7-
##### Please Do's
7+
## Setting up the project for debugging and contributing:
8+
9+
### Recommended setup:
10+
11+
* [vscode](https://code.visualstudio.com), the popular IDE.
12+
* [Jasmine Test Explorer](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer), a very practical test exploration plugin which let you run, debug and see the test results inline.
13+
14+
### Setting up you local machine:
15+
16+
* [Fork](https://github.com/parse-community/parse-server) this project and clone the fork on your local machine:
17+
18+
```sh
19+
$ git clone https://github.com/parse-community/parse-server
20+
$ cd parse-server # go into the clone directory
21+
$ npm install # install all the node dependencies
22+
$ code . # launch vscode
23+
$ npm run watch # run babel watching for local file changes
24+
```
25+
26+
Once you have babel running in watch mode, you can start making changes to parse-server.
27+
28+
### Good to know:
29+
30+
* The lib/ folder is not commited, so never make changes in there.
31+
* Always make changes to files in the `src/` folder.
32+
* All the tests should point to sources in the `lib/` folder.
33+
34+
### Troubleshooting:
35+
36+
*Question*: I modify the code in the src folder but it doesn't seem to have any effect.<br/>
37+
*Answer*: Check that `npm run watch` is running
38+
39+
*Question*: How do I use breakpoints and debug step by step?<br/>
40+
*Answer*: The easiest way is to install [Jasmine Test Explorer](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer), it will let you run selectively tests and debug them.
41+
42+
*Question*: How do I deploy my forked version on my servers?<br/>
43+
*Answer*: In your `package.json`, update the `parse-server` dependency to `https://github.com/MY_USERNAME/parse-server#MY_FEATURE`. Run `npm install`, commit the changes and deploy to your servers.
44+
45+
46+
### Please Do's
847

948
* Begin by reading the [Development Guide](http://docs.parseplatform.org/parse-server/guide/#development-guide) to learn how to get started running the parse-server.
1049
* Take testing seriously! Aim to increase the test coverage with every pull request. To obtain the test coverage of the project, run:
@@ -17,7 +56,7 @@ We really want Parse to be yours, to see it grow and thrive in the open source c
1756
* Lint your code by running `npm run lint` to make sure the code is not going to be rejected by the CI.
1857
* **Do not** publish the *lib* folder.
1958

20-
##### Run your tests against Postgres (optional)
59+
### Run your tests against Postgres (optional)
2160

2261
If your pull request introduces a change that may affect the storage or retrieval of objects, you may want to make sure it plays nice with Postgres.
2362

@@ -28,6 +67,6 @@ If your pull request introduces a change that may affect the storage or retrieva
2867
- `it_only_db('mongo')` // will make a test that only runs on mongo
2968
- `it_exclude_dbs(['postgres'])` // will make a test that runs against all DB's but postgres
3069

31-
##### Code of Conduct
70+
### Code of Conduct
3271

3372
This project adheres to the [Contributor Covenant Code of Conduct](https://github.com/parse-community/parse-server/blob/master/CODE_OF_CONDUCT.md). By participating, you are expected to honor this code.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"dev": "npm run build && node bin/dev",
7171
"lint": "flow && eslint --cache ./",
7272
"build": "babel src/ -d lib/ --copy-files",
73+
"watch": "babel --watch src/ -d lib/ --copy-files",
7374
"pretest": "npm run lint",
7475
"test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 jasmine",
7576
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 nyc jasmine",

spec/AccountLockoutPolicy.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use strict";
22

3-
const Config = require("../src/Config");
3+
const Config = require("../lib/Config");
44

55
const loginWithWrongCredentialsShouldFail = function(username, password) {
66
return new Promise((resolve, reject) => {

spec/AdaptableController.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

2-
const AdaptableController = require("../src/Controllers/AdaptableController").AdaptableController;
3-
const FilesAdapter = require("../src/Adapters/Files/FilesAdapter").default;
4-
const FilesController = require("../src/Controllers/FilesController").FilesController;
2+
const AdaptableController = require("../lib/Controllers/AdaptableController").AdaptableController;
3+
const FilesAdapter = require("../lib/Adapters/Files/FilesAdapter").default;
4+
const FilesController = require("../lib/Controllers/FilesController").FilesController;
55

66
const MockController = function(options) {
77
AdaptableController.call(this, options);

spec/AdapterLoader.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11

2-
const loadAdapter = require("../src/Adapters/AdapterLoader").loadAdapter;
2+
const loadAdapter = require("../lib/Adapters/AdapterLoader").loadAdapter;
33
const FilesAdapter = require("@parse/fs-files-adapter").default;
44
const S3Adapter = require("@parse/s3-files-adapter").default;
55
const ParsePushAdapter = require("@parse/push-adapter").default;
6-
const Config = require('../src/Config');
6+
const Config = require('../lib/Config');
77

88
describe("AdapterLoader", ()=>{
99

@@ -33,7 +33,7 @@ describe("AdapterLoader", ()=>{
3333
});
3434

3535
it("should instantiate an adapter from string that is module", (done) => {
36-
const adapterPath = require('path').resolve("./src/Adapters/Files/FilesAdapter");
36+
const adapterPath = require('path').resolve("./lib/Adapters/Files/FilesAdapter");
3737
const adapter = loadAdapter({
3838
adapter: adapterPath
3939
});

spec/AudienceRouter.spec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const auth = require('../src/Auth');
2-
const Config = require('../src/Config');
3-
const rest = require('../src/rest');
4-
const AudiencesRouter = require('../src/Routers/AudiencesRouter').AudiencesRouter;
1+
const auth = require('../lib/Auth');
2+
const Config = require('../lib/Config');
3+
const rest = require('../lib/rest');
4+
const AudiencesRouter = require('../lib/Routers/AudiencesRouter').AudiencesRouter;
55

66
describe('AudiencesRouter', () => {
77
it('uses find condition from request.body', (done) => {

spec/Auth.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
describe('Auth', () => {
2-
const Auth = require('../src/Auth.js').Auth;
2+
const Auth = require('../lib/Auth.js').Auth;
33

44
describe('getUserRoles', () => {
55
let auth;

spec/AuthenticationAdapters.spec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
const request = require('request');
2-
const Config = require("../src/Config");
3-
const defaultColumns = require('../src/Controllers/SchemaController').defaultColumns;
4-
const authenticationLoader = require('../src/Adapters/Auth');
2+
const Config = require("../lib/Config");
3+
const defaultColumns = require('../lib/Controllers/SchemaController').defaultColumns;
4+
const authenticationLoader = require('../lib/Adapters/Auth');
55
const path = require('path');
66

77
describe('AuthenticationProviders', function() {
88
["facebook", "facebookaccountkit", "github", "instagram", "google", "linkedin", "meetup", "twitter", "janrainengage", "janraincapture", "vkontakte"].map(function(providerName){
99
it("Should validate structure of " + providerName, (done) => {
10-
const provider = require("../src/Adapters/Auth/" + providerName);
10+
const provider = require("../lib/Adapters/Auth/" + providerName);
1111
jequal(typeof provider.validateAuthData, "function");
1212
jequal(typeof provider.validateAppId, "function");
1313
const authDataPromise = provider.validateAuthData({}, {});

spec/CLI.spec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
2-
import commander from '../src/cli/utils/commander';
3-
import definitions from '../src/cli/definitions/parse-server';
4-
import liveQueryDefinitions from '../src/cli/definitions/parse-live-query-server';
2+
const commander = require('../lib/cli/utils/commander').default;
3+
const definitions = require('../lib/cli/definitions/parse-server').default;
4+
const liveQueryDefinitions = require('../lib/cli/definitions/parse-live-query-server').default;
55

66
const testDefinitions = {
77
'arg0': 'PROGRAM_ARG_0',
@@ -173,7 +173,7 @@ describe('LiveQuery definitions', () => {
173173
if (typeof definition.env !== 'undefined') {
174174
expect(typeof definition.env).toBe('string');
175175
}
176-
expect(typeof definition.help).toBe('string');
176+
expect(typeof definition.help).toBe('string', `help for ${key} should be a string`);
177177
if (typeof definition.required !== 'undefined') {
178178
expect(typeof definition.required).toBe('boolean');
179179
}

spec/CacheController.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const CacheController = require('../src/Controllers/CacheController.js').default;
1+
const CacheController = require('../lib/Controllers/CacheController.js').default;
22

33
describe('CacheController', function() {
44
let FakeCacheAdapter;

spec/Client.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const Client = require('../src/LiveQuery/Client').Client;
2-
const ParseWebSocket = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocket;
1+
const Client = require('../lib/LiveQuery/Client').Client;
2+
const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket;
33

44
describe('Client', function() {
55
it('can be initialized', function() {

spec/ClientSDK.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const ClientSDK = require('../src/ClientSDK');
1+
const ClientSDK = require('../lib/ClientSDK');
22

33
describe('ClientSDK', () => {
44
it('should properly parse the SDK versions', () => {

spec/CloudCode.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use strict"
22
const Parse = require("parse/node");
33
const rp = require('request-promise');
4-
const InMemoryCacheAdapter = require('../src/Adapters/Cache/InMemoryCacheAdapter').InMemoryCacheAdapter;
4+
const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter').InMemoryCacheAdapter;
55

66
describe('Cloud Code', () => {
77
it('can load absolute cloud code file', done => {

spec/CloudCodeLogger.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const LoggerController = require('../src/Controllers/LoggerController').LoggerController;
2-
const WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
1+
const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
2+
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
33
const fs = require('fs');
44

55
const loremFile = __dirname + '/support/lorem.txt';

spec/DatabaseController.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const DatabaseController = require('../src/Controllers/DatabaseController.js');
1+
const DatabaseController = require('../lib/Controllers/DatabaseController.js');
22
const validateQuery = DatabaseController._validateQuery;
33

44
describe('DatabaseController', function() {

spec/EmailVerificationToken.spec.js

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

33
const request = require('request');
44
const requestp = require('request-promise');
5-
const Config = require('../src/Config');
5+
const Config = require('../lib/Config');
66

77
describe("Email Verification Token Expiration: ", () => {
88

spec/EnableSingleSchemaCache.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const auth = require('../src/Auth');
2-
const Config = require('../src/Config');
3-
const rest = require('../src/rest');
1+
const auth = require('../lib/Auth');
2+
const Config = require('../lib/Config');
3+
const rest = require('../lib/rest');
44

55
describe('Enable single schema cache', () => {
66
beforeEach((done) => {

spec/EventEmitterPubSub.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const EventEmitterPubSub = require('../src/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
1+
const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub;
22

33
describe('EventEmitterPubSub', function() {
44
it('can publish and subscribe', function() {

spec/FilesController.spec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
const LoggerController = require('../src/Controllers/LoggerController').LoggerController;
2-
const WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
3-
const GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
4-
const Config = require("../src/Config");
5-
const FilesController = require('../src/Controllers/FilesController').default;
1+
const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
2+
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
3+
const GridStoreAdapter = require("../lib/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
4+
const Config = require("../lib/Config");
5+
const FilesController = require('../lib/Controllers/FilesController').default;
66

77
const mockAdapter = {
88
createFile: () => {

spec/GridStoreAdapter.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const MongoClient = require("mongodb").MongoClient;
22
const GridStore = require("mongodb").GridStore;
33

4-
const GridStoreAdapter = require("../src/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
5-
const Config = require("../src/Config");
6-
const FilesController = require('../src/Controllers/FilesController').default;
4+
const GridStoreAdapter = require("../lib/Adapters/Files/GridStoreAdapter").GridStoreAdapter;
5+
const Config = require("../lib/Config");
6+
const FilesController = require('../lib/Controllers/FilesController').default;
77

88

99
// Small additional tests to improve overall coverage

spec/HTTPRequest.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

3-
const httpRequest = require("../src/cloud-code/httpRequest"),
4-
HTTPResponse = require('../src/cloud-code/HTTPResponse').default,
3+
const httpRequest = require("../lib/cloud-code/httpRequest"),
4+
HTTPResponse = require('../lib/cloud-code/HTTPResponse').default,
55
bodyParser = require('body-parser'),
66
express = require("express");
77

spec/InMemoryCache.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const InMemoryCache = require('../src/Adapters/Cache/InMemoryCache').default;
1+
const InMemoryCache = require('../lib/Adapters/Cache/InMemoryCache').default;
22

33

44
describe('InMemoryCache', function() {

spec/InMemoryCacheAdapter.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const InMemoryCacheAdapter = require('../src/Adapters/Cache/InMemoryCacheAdapter').default;
1+
const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter').default;
22

33
describe('InMemoryCacheAdapter', function() {
44
const KEY = 'hello';

spec/InstallationsRouter.spec.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const auth = require('../src/Auth');
2-
const Config = require('../src/Config');
3-
const rest = require('../src/rest');
4-
const InstallationsRouter = require('../src/Routers/InstallationsRouter').InstallationsRouter;
1+
const auth = require('../lib/Auth');
2+
const Config = require('../lib/Config');
3+
const rest = require('../lib/rest');
4+
const InstallationsRouter = require('../lib/Routers/InstallationsRouter').InstallationsRouter;
55

66
describe('InstallationsRouter', () => {
77
it('uses find condition from request.body', (done) => {

spec/Logger.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const logging = require('../src/Adapters/Logger/WinstonLogger');
1+
const logging = require('../lib/Adapters/Logger/WinstonLogger');
22
const winston = require('winston');
33

44
class TestTransport extends winston.Transport {

spec/LoggerController.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const LoggerController = require('../src/Controllers/LoggerController').LoggerController;
2-
const WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
1+
const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
2+
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
33

44
describe('LoggerController', () => {
55
it('can check process a query without throwing', (done) => {

spec/LogsRouter.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22

33
const request = require('request');
4-
const LogsRouter = require('../src/Routers/LogsRouter').LogsRouter;
5-
const LoggerController = require('../src/Controllers/LoggerController').LoggerController;
6-
const WinstonLoggerAdapter = require('../src/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
4+
const LogsRouter = require('../lib/Routers/LogsRouter').LogsRouter;
5+
const LoggerController = require('../lib/Controllers/LoggerController').LoggerController;
6+
const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter;
77

88
const loggerController = new LoggerController(new WinstonLoggerAdapter());
99

spec/Middlewares.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const middlewares = require('../src/middlewares');
2-
const AppCache = require('../src/cache').AppCache;
1+
const middlewares = require('../lib/middlewares');
2+
const AppCache = require('../lib/cache').AppCache;
33

44
describe('middlewares', () => {
55

spec/MongoSchemaCollectionAdapter.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const MongoSchemaCollection = require('../src/Adapters/Storage/Mongo/MongoSchemaCollection').default;
3+
const MongoSchemaCollection = require('../lib/Adapters/Storage/Mongo/MongoSchemaCollection').default;
44

55
describe('MongoSchemaCollection', () => {
66
it('can transform legacy _client_permissions keys to parse format', done => {

spec/MongoStorageAdapter.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
import MongoStorageAdapter from '../src/Adapters/Storage/Mongo/MongoStorageAdapter';
3+
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default;
44
const { MongoClient } = require('mongodb');
55
const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
66

spec/MongoTransform.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// These tests are unit tests designed to only test transform.js.
22
"use strict";
33

4-
const transform = require('../src/Adapters/Storage/Mongo/MongoTransform');
4+
const transform = require('../lib/Adapters/Storage/Mongo/MongoTransform');
55
const dd = require('deep-diff');
66
const mongodb = require('mongodb');
77

spec/NullCacheAdapter.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const NullCacheAdapter = require('../src/Adapters/Cache/NullCacheAdapter').default;
1+
const NullCacheAdapter = require('../lib/Adapters/Cache/NullCacheAdapter').default;
22

33
describe('NullCacheAdapter', function() {
44
const KEY = 'hello';

spec/OAuth1.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const OAuth = require("../src/Adapters/Auth/OAuth1Client");
1+
const OAuth = require("../lib/Adapters/Auth/OAuth1Client");
22

33
describe('OAuth', function() {
44
it("Nonce should have right length", (done) => {

0 commit comments

Comments
 (0)