Shared Config #

Shared config can be used to reduce duplication of config values that appear in different config blocks, including a service, plugin, or node-service configs.

Shared Config section of yml files #

Shared config blocks are defined within the top level shared config section. A unique value is used to identify the shared config block, which is then referenced within elsewhere in order to utilize the shared config values. The example below defines a SteamShared block that specifies values for the SteamCredentialsConfig class. The identity and order plugins utilize the shared values by saving the SteamShared key within the plugin config and using it to retrieve the shared config block within the onSharedConfigChanged handler described below.

unicorn/config/test/test.yml

social:
  shared:
    configs:
      SteamShared:
        class: "pragma.shared.SteamCredentialsConfig"
        config:
          steamWebAPIKey: "<encrypted-string>"
          appId: "1234567890"
  pluginConfigs:
    AccountService.identityProviderPlugins:
      plugins:    
        Steam:
          class: "pragma.account.SteamIdentityProviderPlugin"
          config:
            sharedConfigKey: "SteamShared"
    ThirdPartyNodeService.orderProviderPlugins:
      plugins:
        Steam:
          class: "pragma.order.SteamOrderProviderPlugin"
          config:
            sharedConfigKey: "SteamShared"

Listening for shared config changes #

Implement the SharedConfigHandler interface to listen for shared config updates.

If you have a plugin that is one of a ProviderPluginCollection you will want to use the getConfig function that allows you to pass a string to find the matching section of shared config. Id and Order provider plugins are the most common types of ProviderPluginCollection and the base class ProviderPluginConfig has the field sharedConfigKey to make it easy to specify the key in your yaml.

class SteamOrderProviderPlugin(
    override val service: Service,
    override val contentDataNodeService: ContentDataNodeService,
    // ...
) : /* ... , */ SharedConfigHandler {

    override var config: SteamOrderProviderPluginConfig = defaultConfig
    var steamCredentialsConfig: SteamCredentialsConfig? = null

  override suspend fun onSharedConfigChanged(sharedConfig: ConfigCollection) {
    steamCredentialsConfig = sharedConfig.getConfig(this.config.sharedConfigKey)
  }
  
  // ...
}

If there is only a single instance of a given config class in the shared config, the config object can be looked up with a generic getConfig function. If there is 0 entries for the type null is returned, multiple entries for the type is an exception.

class EpicTokenService(
  pragmaNode: PragmaNode,
  instanceId: UUID,
  // ...
) : /* ... , */ ConfigHandler<EpicTokenServiceConfig> {

    private var sharedConfig: EpicCredentialsConfig? = null
    
    override suspend fun onSharedConfigChanged(sharedConfig: ConfigCollection) {
        if (this.sharedConfig == null && sharedConfig.getConfig<EpicCredentialsConfig>() == null) {
            // error if shared config not available on startup
            error("Required config EpicCredentialsConfig not provided, exiting.")
        }
        this.sharedConfig = sharedConfig.getConfig<EpicCredentialsConfig>()
    }
  
  // ...
}

The function onSharedConfigChanged will be called during startup with the initial config and also when deploying config using the infra tools (or by editing the config while running locally).

Exceptions thrown in onSharedConfigChanged will prevent pragma from starting up if thrown during startup.