diff --git a/CHANGELOG.md b/CHANGELOG.md index ca20a427af..86b5081bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,7 +135,7 @@ ___ - Fix incorrect LiveQuery events triggered for multiple subscriptions on the same class with different events [#7341](https://github.com/parse-community/parse-server/pull/7341) - Fix select and excludeKey queries to properly accept JSON string arrays. Also allow nested fields in exclude (Corey Baker) [#7242](https://github.com/parse-community/parse-server/pull/7242) - Fix LiveQuery server crash when using $all query operator on a missing object key (Jason Posthuma) [#7421](https://github.com/parse-community/parse-server/pull/7421) - +- Added runtime deprecation warnings (Manuel Trezza) [#7451](https://github.com/parse-community/parse-server/pull/7451) ___ ## 4.5.0 [Full Changelog](https://github.com/parse-community/parse-server/compare/4.4.0...4.5.0) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a761f91955..201aeecf6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -182,7 +182,9 @@ If you change or remove an existing feature that would lead to a breaking change - Use a default value that falls back to existing behavior. - Add a deprecation definition in `Deprecator/Deprecations.js` that will output a deprecation warning log message on Parse Server launch, for example: > DeprecationWarning: The Parse Server option 'example' will be removed in a future release. - + +For deprecations that can only be determined ad-hoc during runtime, for example Parse Query syntax deprecations, use the `Deprecator.logRuntimeDeprecation()` method. + Deprecations become breaking changes after notifying developers through deprecation warnings for at least one entire previous major release. For example: - `4.5.0` is the current version - `4.6.0` adds a new optional feature and a deprecation warning for the existing feature diff --git a/spec/Deprecator.spec.js b/spec/Deprecator.spec.js index 7e0e28df3d..3af5d10c31 100644 --- a/spec/Deprecator.spec.js +++ b/spec/Deprecator.spec.js @@ -21,16 +21,28 @@ describe('Deprecator', () => { const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); await reconfigureServer(); - expect(logSpy.calls.all()[0].args[0]).toContain(deprecations[0].optionKey); - expect(logSpy.calls.all()[0].args[0]).toContain(deprecations[0].changeNewDefault); + expect(logSpy.calls.all()[0].args[0]).toEqual( + `DeprecationWarning: The Parse Server option '${deprecations[0].optionKey}' default will change to '${deprecations[0].changeNewDefault}' in a future version.` + ); }); it('does not log deprecation for new default if option is set manually', async () => { deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); - const logSpy = spyOn(Deprecator, '_log').and.callFake(() => {}); + const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {}); await reconfigureServer({ [deprecations[0].optionKey]: 'manuallySet' }); expect(logSpy).not.toHaveBeenCalled(); }); + + it('logs runtime deprecation', async () => { + const logger = require('../lib/logger').logger; + const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); + const options = { usage: 'Doing this', solution: 'Do that instead.' }; + + Deprecator.logRuntimeDeprecation(options); + expect(logSpy.calls.all()[0].args[0]).toEqual( + `DeprecationWarning: ${options.usage} is deprecated and will be removed in a future version. ${options.solution}` + ); + }); }); diff --git a/src/Deprecator/Deprecator.js b/src/Deprecator/Deprecator.js index 5ab0bb43ee..27033c946d 100644 --- a/src/Deprecator/Deprecator.js +++ b/src/Deprecator/Deprecator.js @@ -22,11 +22,41 @@ class Deprecator { // If default will change, only throw a warning if option is not set if (changeNewDefault != null && options[optionKey] == null) { - Deprecator._log({ optionKey, changeNewDefault, solution }); + Deprecator._logOption({ optionKey, changeNewDefault, solution }); } } } + /** + * Logs a deprecation warning for a parameter that can only be determined dynamically + * during runtime. + * + * Note: Do not use this to log deprecations of Parse Server options, but add such + * deprecations to `Deprecations.js` instead. See the contribution docs for more + * details. + * + * For consistency, the deprecation warning is composed of the following parts: + * + * > DeprecationWarning: `usage` is deprecated and will be removed in a future version. + * `solution`. + * + * - `usage`: The deprecated usage. + * - `solution`: The instruction to resolve this deprecation warning. + * + * For example: + * > DeprecationWarning: `Prefixing field names with dollar sign ($) in aggregation query` + * is deprecated and will be removed in a future version. `Reference field names without + * dollar sign prefix.` + * + * @param {Object} options The deprecation options. + * @param {String} options.usage The usage that is deprecated. + * @param {String} [options.solution] The instruction to resolve this deprecation warning. + * Optional. It is recommended to add an instruction for the convenience of the developer. + */ + static logRuntimeDeprecation(options) { + Deprecator._logGeneric(options); + } + /** * Returns the deprecation definitions. * @returns {Array} The deprecations. @@ -35,8 +65,24 @@ class Deprecator { return Deprecations; } + /** + * Logs a generic deprecation warning. + * + * @param {Object} options The deprecation options. + * @param {String} options.usage The usage that is deprecated. + * @param {String} [options.solution] The instruction to resolve this deprecation warning. + * Optional. It is recommended to add an instruction for the convenience of the developer. + */ + static _logGeneric({ usage, solution }) { + // Compose message + let output = `DeprecationWarning: ${usage} is deprecated and will be removed in a future version.`; + output += solution ? ` ${solution}` : ''; + logger.warn(output); + } + /** * Logs a deprecation warning for a Parse Server option. + * * @param {String} optionKey The option key incl. its path, e.g. `security.enableCheck`. * @param {String} envKey The environment key, e.g. `PARSE_SERVER_SECURITY`. * @param {String} changeNewKey Set the new key name if the current key will be replaced, @@ -48,7 +94,7 @@ class Deprecator { * automatically added to the message. It should only contain the instruction on how * to resolve this warning. */ - static _log({ optionKey, envKey, changeNewKey, changeNewDefault, solution }) { + static _logOption({ optionKey, envKey, changeNewKey, changeNewDefault, solution }) { const type = optionKey ? 'option' : 'environment key'; const key = optionKey ? optionKey : envKey; const keyAction =