Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions spec/Deprecator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,29 @@ describe('Deprecator', () => {
`DeprecationWarning: ${options.usage} is deprecated and will be removed in a future version. ${options.solution}`
);
});

it('logs deprecation for nested option key with dot notation', async () => {
deprecations = [{ optionKey: 'databaseOptions.allowPublicExplain', changeNewDefault: 'false' }];

spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
const logger = require('../lib/logger').logger;
const logSpy = spyOn(logger, 'warn').and.callFake(() => {});

await reconfigureServer();
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 nested option key if option is set manually', async () => {
deprecations = [{ optionKey: 'databaseOptions.allowPublicExplain', changeNewDefault: 'false' }];

spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
const Config = require('../lib/Config');
const config = Config.get('test');
// Directly test scanParseServerOptions with nested option set
Deprecator.scanParseServerOptions({ databaseOptions: { allowPublicExplain: true } });
expect(logSpy).not.toHaveBeenCalled();
});
});
51 changes: 51 additions & 0 deletions spec/Utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,55 @@ describe('Utils', () => {
expect(result).toBe('{"name":"test","number":42,"nested":{"key":"value"}}');
});
});

describe('getNestedProperty', () => {
it('should get top-level property', () => {
const obj = { foo: 'bar' };
expect(Utils.getNestedProperty(obj, 'foo')).toBe('bar');
});

it('should get nested property with dot notation', () => {
const obj = { database: { options: { enabled: true } } };
expect(Utils.getNestedProperty(obj, 'database.options.enabled')).toBe(true);
});

it('should return undefined for non-existent property', () => {
const obj = { foo: 'bar' };
expect(Utils.getNestedProperty(obj, 'baz')).toBeUndefined();
});

it('should return undefined for non-existent nested property', () => {
const obj = { database: { options: {} } };
expect(Utils.getNestedProperty(obj, 'database.options.enabled')).toBeUndefined();
});

it('should return undefined when path traverses non-object', () => {
const obj = { database: 'string' };
expect(Utils.getNestedProperty(obj, 'database.options.enabled')).toBeUndefined();
});

it('should return undefined for null object', () => {
expect(Utils.getNestedProperty(null, 'foo')).toBeUndefined();
});

it('should return undefined for empty path', () => {
const obj = { foo: 'bar' };
expect(Utils.getNestedProperty(obj, '')).toBeUndefined();
});

it('should handle value of 0', () => {
const obj = { database: { timeout: 0 } };
expect(Utils.getNestedProperty(obj, 'database.timeout')).toBe(0);
});

it('should handle value of false', () => {
const obj = { database: { enabled: false } };
expect(Utils.getNestedProperty(obj, 'database.enabled')).toBe(false);
});

it('should handle value of empty string', () => {
const obj = { database: { name: '' } };
expect(Utils.getNestedProperty(obj, 'database.name')).toBe('');
});
});
});
3 changes: 2 additions & 1 deletion src/Deprecator/Deprecator.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logger from '../logger';
import Deprecations from './Deprecations';
import Utils from '../Utils';

/**
* The deprecator class.
Expand All @@ -21,7 +22,7 @@ class Deprecator {
const changeNewDefault = deprecation.changeNewDefault;

// If default will change, only throw a warning if option is not set
if (changeNewDefault != null && options[optionKey] == null) {
if (changeNewDefault != null && Utils.getNestedProperty(options, optionKey) == null) {
Deprecator._logOption({ optionKey, changeNewDefault, solution });
}
}
Expand Down
25 changes: 25 additions & 0 deletions src/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,31 @@ class Utils {
return value;
};
}

/**
* Gets a nested property value from an object using dot notation.
* @param {Object} obj The object to get the property from.
* @param {String} path The property path in dot notation, e.g. 'databaseOptions.allowPublicExplain'.
* @returns {any} The property value or undefined if not found.
* @example
* const obj = { database: { options: { enabled: true } } };
* Utils.getNestedProperty(obj, 'database.options.enabled');
* // Output: true
*/
static getNestedProperty(obj, path) {
if (!obj || !path) {
return undefined;
}
const keys = path.split('.');
let current = obj;
for (const key of keys) {
if (current == null || typeof current !== 'object') {
return undefined;
}
current = current[key];
}
return current;
}
}

module.exports = Utils;
Loading