diff --git a/2.3.0.md b/2.3.0.md deleted file mode 100644 index 42a19263fc..0000000000 --- a/2.3.0.md +++ /dev/null @@ -1,82 +0,0 @@ -# Upgrading Parse Server to version 2.3.0 - -Parse Server version 2.3.0 begins using unique indexes to ensure the User's username and email are unique. This is not a backwards incompatible change, but it may in some cases cause a significant performance regression until the index finishes building. Building the unique index before upgrading your Parse Server version will eliminate the performance impact, and is a recommended step before upgrading any app to Parse Server 2.3.0. New apps starting with version 2.3.0 do not need to take any steps before beginning their project. - -If you are using MongoDB in Cluster or Replica Set mode, we recommend reading Mongo's [documentation on index building](https://docs.mongodb.com/v3.0/tutorial/build-indexes-on-replica-sets/) first. If you are not using these features, you can execute the following commands from the Mongo shell to build the unique index. You may also want to create a backup first. - -```js -// Select the database that your Parse App uses -use parse; - -// Select the collection your Parse App uses for users. For migrated apps, this probably includes a collectionPrefix. -var coll = db['your_prefix:_User']; - -// You can check if the indexes already exists by running coll.getIndexes() -coll.getIndexes(); - -// The indexes you want should look like this. If they already exist, you can skip creating them. -{ - "v" : 1, - "unique" : true, - "key" : { - "username" : 1 - }, - "name" : "username_1", - "ns" : "parse.your_prefix:_User", - "background" : true, - "sparse" : true -} - -{ - "v" : 1, - "unique" : true, - "key" : { - "email" : 1 - }, - "name" : "email_1", - "ns" : "parse.your_prefix:_User", - "background" : true, - "sparse" : true -} - -// Create the username index. -// "background: true" is mandatory and avoids downtime while the index builds. -// "sparse: true" is also mandatory because Parse Server uses sparse indexes. -coll.ensureIndex({ username: 1 }, { background: true, unique: true, sparse: true }); - -// Create the email index. -// "background: true" is still mandatory. -// "sparse: true" is also mandatory both because Parse Server uses sparse indexes, and because email addresses are not required by the Parse API. -coll.ensureIndex({ email: 1 }, { background: true, unique: true, sparse: true }); -``` - -There are some issues you may run into during this process: - -## Mongo complains that the index already exists, but with different options - -In this case, you will need to remove the incorrect index. If your app relies on the existence of the index in order to be performant, you can create a new index, with "-1" for the direction of the field, so that it counts as different options. Then, drop the conflicting index, and create the unique index. - -## There is already non-unique data in the username or email field - -This is possible if you have explicitly set some user's emails to null. If this is bogus data, and those null fields should be unset, you can unset the null emails with this command. If your app relies on the difference between null and unset emails, you will need to upgrade your app to treat null and unset emails the same before building the index and upgrading to Parse Server 2.3.0. - -```js -coll.update({ email: { $exists: true, $eq: null } }, { $unset: { email: '' } }, { multi: true }) -``` - -## There is already non-unique data in the username or email field, and it's not nulls - -This is possible due to a race condition in previous versions of Parse Server. If you have this problem, it is unlikely that you have a lot of rows with duplicate data. We recommend you clean up the data manually, by removing or modifying the offending rows. - -This command, can be used to find the duplicate data: - -```js -coll.aggregate([ - {$match: {"username": {"$ne": null}}}, - {$group: {_id: "$username", uniqueIds: {$addToSet: "$_id"}, count: {$sum: 1}}}, - {$match: {count: {"$gt": 1}}}, - {$project: {id: "$uniqueIds", username: "$_id", _id : 0} }, - {$unwind: "$id" }, - {$out: '_duplicates'} // Save the list of duplicates to a new, "_duplicates" collection. Remove this line to just output the list. -], {allowDiskUse:true}) -``` diff --git a/3.0.0.md b/3.0.0.md deleted file mode 100644 index e34d2eeb9f..0000000000 --- a/3.0.0.md +++ /dev/null @@ -1,226 +0,0 @@ -# Upgrading Parse Server to version 3.0.0 - -parse-server 3.0.0 comes also with the JS SDK version 2.0 which requires a migration. Follow the migration guide [here](https://github.com/parse-community/Parse-SDK-JS/blob/master/2.0.0.md). - -With the 3.0.0, parse-server has completely revamped it's cloud code interface. Gone are the backbone style calls, welcome promises and async/await. - -In order to leverage those new nodejs features, you'll need to run at least node 8.10. We've dropped the support of node 6 a few months ago, so if you haven't made the change yet, now would be a really good time to take the plunge. - -## Migrating Cloud Code Hooks - -### Synchronous validations: - -```js -// before -Parse.Cloud.beforeSave('MyClassName', function(request, response) { - if (!passesValidation(request.object)) { - response.error('Ooops something went wrong'); - } else { - response.success(); - } -}); - -// after -Parse.Cloud.beforeSave('MyClassName', (request) => { - if (!passesValidation(request.object)) { - throw 'Ooops something went wrong'; - } -}); -``` - -All methods are wrapped in promises, so you can freely `throw` `Error`, `Parse.Error` or `string` to mark an error. - -### Asynchronous validations: - -For asynchronous code, you can use promises or async / await. - -Consider the following beforeSave call that would replace the contents of a fileURL with a proper copy of the image as a Parse.File. - -```js -// before -Parse.Cloud.beforeSave('Post', function(request, response) { - Parse.Cloud.httpRequest({ - url: request.object.get('fileURL'), - success: function(contents) { - const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') }); - file.save({ - success: function() { - request.object.set('file', file); - response.success(); - }, - error: response.error - }); - }, - error: response.error - }); -}); -``` - -As we can see the current way, with backbone style callbacks is quite tough to read and maintain. -It's also not really trivial to handle errors, as you need to pass the response.error to each error handlers. - -Now it can be modernized with promises: - -```js -// after (with promises) -Parse.Cloud.beforeSave('MyClassName', (request) => { - return Parse.Cloud.httpRequest({ - url: request.object.get('fileURL') - }).then((contents) => { - const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') }); - request.object.set('file', file); - return file.save(); - }); -}); -``` - -And you can even make it better with async/await. - -```js -// after with async/await -Parse.Cloud.beforeSave('MyClassName', async (request) => { - const contents = await Parse.Cloud.httpRequest({ - url: request.object.get('fileURL') - }); - - const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') }); - request.object.set('file', file); - await file.save(); -}); -``` - -## Aborting hooks and functions - -In order to abort a `beforeSave` or any hook, you now need to throw an error: - -```js -// after with async/await -Parse.Cloud.beforeSave('MyClassName', async (request) => { - // valid, will result in a Parse.Error(SCRIPT_FAILED, 'Something went wrong') - throw 'Something went wrong' - // also valid, will fail with Parse.Error(SCRIPT_FAILED, 'Something else went wrong') - throw new Error('Something else went wrong') - // using a Parse.Error is also valid - throw new Parse.Error(1001, 'My error') -}); -``` - -## Migrating Functions - -Cloud Functions can be migrated the same way as cloud code hooks. -In functions, the response object is not passed anymore, as it is in cloud code hooks - -Continuing with the image downloader example: - -```js -// before -Parse.Cloud.define('downloadImage', function(request, response) { - Parse.Cloud.httpRequest({ - url: request.params.url, - success: function(contents) { - const file = new Parse.File(request.params.name, { base64: contents.buffer.toString('base64') }); - file.save({ - success: function() { - response.success(file); - }, - error: response.error - }); - }, - error: response.error - }); -}); -``` - -You would call this method with: - -```js -Parse.Cloud.run('downloadImage',{ - url: 'https://example.com/file', - name: 'my-file' -}); -``` - -To migrate this function you would follow the same practices as the ones before, and we'll jump right away to the async/await implementation: - -```js -// after with async/await -Parse.Cloud.define('downloadImage', async (request) => { - const { - url, name - } = request.params; - const response = await Parse.Cloud.httpRequest({ url }); - - const file = new Parse.File(name, { base64: response.buffer.toString('base64') }); - await file.save(); - return file; -}); -``` - -## Migrating jobs - -As with hooks and functions, jobs don't have a status object anymore. -The message method has been moved to the request object. - -```js -// before -Parse.Cloud.job('downloadImageJob', function(request, status) { - var query = new Parse.Query('ImagesToDownload'); - query.find({ - success: function(images) { - var done = 0; - for (var i = 0; i { - request.message('Doing ' + image.get('url')); - const contents = await Parse.Cloud.httpRequest({ - url: image.get('url') - }); - request.message('Got ' + image.get('url')); - const file = new Parse.File(image.get('name'), { base64: contents.buffer.toString('base64') }); - await file.save(); - request.message('Saved ' + image.get('url')); - }); - await Promise.all(promises); -}); - -``` - -As you can see the new implementation is more concise, easier to read and maintain. - -If you encounter a problem or discover an issue with this guide, or with any Parse Community project, feel free to [reach out on github](https://github.com/parse-community/parse-server/issues/new/choose) - diff --git a/README.md b/README.md index 634a7b25e0..546e3afe0c 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,6 @@ A big *thank you* 🙏 to our [sponsors](#sponsors) and [backers](#backers) who - [Using automatically generated operations](#using-automatically-generated-operations) - [Customizing your GraphQL Schema](#customizing-your-graphql-schema) - [Learning more](#learning-more) -- [Upgrading to Parse Server 3.0](#upgrading-to-parse-server-30) - [Contributing](#contributing) - [Contributors](#contributors) - [Sponsors](#sponsors) @@ -1154,12 +1153,6 @@ You also have a very powerful tool inside your GraphQL Playground. Please look a Additionally, the [GraphQL Learn Section](https://graphql.org/learn/) is a very good source to learn more about the power of the GraphQL language. -# Upgrading to Parse Server 3.0 - -Starting Parse Server 3.0, Parse Server uses the Parse JavaScript SDK 2.0. In short, the Parse JavaScript SDK 2.0 removes the backbone style callbacks as well as the `Parse.Promise` object in favor of native promises. All the Cloud Code interfaces also have been updated to reflect those changes, and all backbone style response objects are removed and replaced by promise style resolution. - -We have written up a [migration guide](3.0.0.md) to help you transition to the next major release. - # Contributing Please see the [Contributing Guide](CONTRIBUTING.md).