From 1fc8c8d671eee333c639bbab8a05a4e297610d0a Mon Sep 17 00:00:00 2001 From: Naz Date: Tue, 6 Sep 2022 14:30:18 +0800 Subject: [PATCH] Added more explicit adapter config syntax refs https://github.com/TryGhost/Toolbox/issues/384 - Existing adapter config was based on the notion there can only be one configuration per one adapter class. With adapter cache now allowing instantiating multiple adapter instances with the same base class it opened up a possibility to have shared configuration for a base class and then extend/override it in "feature" configurations (see tests in this commit for specific examples) --- .../adapter-manager/options-resolver.js | 14 ++- .../adapter-manager/options-resolver.test.js | 114 ++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/ghost/core/core/server/services/adapter-manager/options-resolver.js b/ghost/core/core/server/services/adapter-manager/options-resolver.js index c02782c15d..feef9264f5 100644 --- a/ghost/core/core/server/services/adapter-manager/options-resolver.js +++ b/ghost/core/core/server/services/adapter-manager/options-resolver.js @@ -5,10 +5,20 @@ module.exports = function resolveAdapterOptions(name, adapterServiceConfig) { let adapterName; let adapterConfig; - // CASE: load resource-specific adapter when there is an adapter feature name specified as well as custom feature config - if (feature && adapterSettings[feature] && adapterSettings[adapterSettings[feature]]) { + const hasFeatureConfig = feature && adapterSettings[feature]; + if (hasFeatureConfig && adapterSettings[adapterSettings[feature]]) { + // CASE: load resource-specific adapter when there is an adapter feature + // name (String) specified as well as custom feature config adapterName = adapterSettings[feature]; adapterConfig = adapterSettings[adapterName]; + } else if (hasFeatureConfig && adapterSettings[feature].adapter) { + // CASE: load resource-specific adapter when there is an adapter feature + // name (Object) specified as well as custom feature config + adapterName = adapterSettings[feature].adapter; + const commonAdapterConfig = {...adapterSettings[adapterName]}; + const featureAdapterConfig = {...adapterSettings[feature]}; + delete featureAdapterConfig.adapter; + adapterConfig = {...commonAdapterConfig, ...featureAdapterConfig}; } else { adapterName = adapterSettings.active; adapterConfig = adapterSettings[adapterName]; diff --git a/ghost/core/test/unit/server/services/adapter-manager/options-resolver.test.js b/ghost/core/test/unit/server/services/adapter-manager/options-resolver.test.js index d338e15794..ff61a21a3d 100644 --- a/ghost/core/test/unit/server/services/adapter-manager/options-resolver.test.js +++ b/ghost/core/test/unit/server/services/adapter-manager/options-resolver.test.js @@ -3,6 +3,22 @@ const should = require('should'); const resolveAdapterOptions = require('../../../../../core/server/services/adapter-manager/options-resolver'); describe('Adapter Manager: options resolver', function () { + it('creates empty configs for unknown adapter with a default (active) instance', function () { + const name = 'cache:images'; + + const adapterServiceConfig = { + cache: { + active: 'Memory' + } + }; + + const {adapterType, adapterName, adapterConfig} = resolveAdapterOptions(name, adapterServiceConfig); + + adapterType.should.equal('cache'); + adapterName.should.equal('Memory'); + should.equal(adapterConfig, undefined); + }); + it('returns default adapter configuration', function () { const name = 'storage'; const adapterServiceConfig = { @@ -71,4 +87,102 @@ describe('Adapter Manager: options resolver', function () { custom: 'configValue' }); }); + + it('combines configurations from shared adapter and feature adapter instances', function () { + const primaryAdapterName = 'cache:images'; + const secondaryAdapterName = 'cache:settings'; + const adapterServiceConfig = { + cache: { + Redis: { + commonConfigValue: 'common_config_value' + }, + images: { + adapter: 'Redis', + adapterConfigValue: 'images_redis_value' + }, + settings: { + adapter: 'Redis', + adapterConfigValue: 'settings_redis_value' + } + } + }; + + const {adapterType, adapterName, adapterConfig} = resolveAdapterOptions(primaryAdapterName, adapterServiceConfig); + + adapterType.should.equal('cache'); + adapterName.should.equal('Redis'); + adapterConfig.should.deepEqual({ + commonConfigValue: 'common_config_value', + adapterConfigValue: 'images_redis_value' + }); + + const {adapterType: secondAdapterType, adapterName: secondAdapterName, adapterConfig: secondAdapterConfig} = resolveAdapterOptions(secondaryAdapterName, adapterServiceConfig); + + secondAdapterType.should.equal('cache'); + secondAdapterName.should.equal('Redis'); + secondAdapterConfig.should.deepEqual({ + commonConfigValue: 'common_config_value', + adapterConfigValue: 'settings_redis_value' + }); + }); + + it('combines empty configuration from shared adapter and feature adapter instances', function () { + const primaryAdapterName = 'cache:images'; + const secondaryAdapterName = 'cache:settings'; + const adapterServiceConfig = { + cache: { + images: { + adapter: 'Redis', + adapterConfigValue: 'images_redis_value' + }, + settings: { + adapter: 'Redis', + adapterConfigValue: 'settings_redis_value' + } + } + }; + + const {adapterType, adapterName, adapterConfig} = resolveAdapterOptions(primaryAdapterName, adapterServiceConfig); + + adapterType.should.equal('cache'); + adapterName.should.equal('Redis'); + adapterConfig.should.deepEqual({ + adapterConfigValue: 'images_redis_value' + }); + + const {adapterType: secondAdapterType, adapterName: secondAdapterName, adapterConfig: secondAdapterConfig} = resolveAdapterOptions(secondaryAdapterName, adapterServiceConfig); + + secondAdapterType.should.equal('cache'); + secondAdapterName.should.equal('Redis'); + secondAdapterConfig.should.deepEqual({ + adapterConfigValue: 'settings_redis_value' + }); + }); + + it('gives priority to a feature config over a common adapter config', function () { + const primaryAdapterName = 'cache:images'; + const adapterServiceConfig = { + cache: { + Redis: { + commonConfigValue: 'common_config_value', + overrideMe: 'common_value' + }, + images: { + adapter: 'Redis', + adapterConfigValue: 'images_redis_value', + overrideMe: 'images_override' + } + } + }; + + const {adapterType, adapterName, adapterConfig} = resolveAdapterOptions(primaryAdapterName, adapterServiceConfig); + + adapterType.should.equal('cache'); + adapterName.should.equal('Redis'); + adapterConfig.should.deepEqual({ + commonConfigValue: 'common_config_value', + adapterConfigValue: 'images_redis_value', + overrideMe: 'images_override' + }); + }); });