How to setup new Config type

There doesn’t seem to be much documentation on the Config type, yet TenantConfig is marked as deprecated. How does one go about creating a new type that mixes in Config?

A few specific questions:

  1. If you also mixin Singleton for your config type, does that mean for each configuration value you want as part of the config you simply add a field to that Config type?
  2. What is the SeedData structure to set default values for a Config type?
  3. If you don’t mixin Singleton along with Config, how does that change the usage for your Config type?
  4. Will a Config type ever also be Persistable?
  5. Will a Config type ever also mixin SeedData?

I would like to know similar for the Cached and MetadataCache.

@huanwen.chen is probably well positioned to answer this one completely.

Answers to some of your questions:
(1.) Config is by default Singleton. And yes, any configurable values should be fields in your type that mixes Config.
(3.) No change, Config is Singleton by default.
(4.) Config should not be Persistable, as it already defines its own persistence strategy
(5.) I have not encountered a situation yet where SeedData is necessary

1 + 3. Looking at all of the types which mixin the Config type, there are several types that also mixin Singleton (e.g. AwsConfig, ClusterConfig) while some types do not explicitly mixin Singleton and don’t have Singleton included in the list of types it mixes in (e.g. FileSystemConfig, X509Certificate, ContainerRegistry) so I don’t understand this answer - it seems to me that the types which don’t mixin Singleton in addition to Config don’t have access to the Singleton.inst() method, does that mean you just need to use ConfigType.make() for those which don’t explicitly mixin Singleton?

4 + 5. The HardwareProfile type is both Persistable and mixes in SeedData in addition to Config, that is the reason why I asked this :slight_smile: I wanted to get an understanding of how that changes behavior

Side note: I am still not quite clear on how to properly seed one’s own Config type with default values, which was my question (2)

1 + 3. That’s my mistake. To be more precise, a Config will, by default, be treated conceptually as a singleton (won’t explicitly mix it) if the type has no other identifying mixin (e.g. Singleton, UserSingleton, Identifiable, Nameable…). As a rule of thumb, we recommend any type mixing Config to explicitly declare Singleton if applicable. And yes, make() or inst() can be used, as applicable.

4 + 5. Good eye! That was indeed a mistake in usage of Config that existed in 7.8, but has been rectified in 7.9. HardwareProfile is not a Config and for what it’s worth, also not SeedData.

  1. As far as I’m aware, this is a very recently added feature for 7.9 (i.e. to be able to seed Configs in application packages). I have no personal experience with it.
1 Like

Got it, thanks for the reply! That was very helpful @scott.kruyswyk it’s not that simple - and the reason why Config is complicated is that there are many use cases that we need to handle:

Singleton configuration - only one instance exists regardless of the context - e.g. DevEnvironmentConfig

Type configuration - i.e. instance of a configuration exists per sub-type of this config - note that this is not the same as Singleton - i.e. type that is configured may not be a Singleton - e.g. KvStoreConfig

UserSingleton - i.e. single config per user - JupyterConfig

Identifiable - i.e. configured by id of what it’s for - ServerConfig

Nameable - i.e. configured by name of what it’s for - e.g. DatastoreConfig

--- custom --- - i.e. configured by bit more complicated custom logic - e.g. CloudResource

Also note that Config is not a “user type” - i.e. users will not even have a permission to change it - so it’s best if all access to Config goes via Configurable.config or higher order methods on user types.

Please avoid documenting or distributing code snippets that use Config type directly.

Also there was comment above that it is recommended that all Config's to have mixin of one of Singleton etc… - that is not completely accurate see above case of "Type configs "


I’m not sure I fully follow your last statement. Does that mean applications shouldn’t create types that mix the Config type? If that is the case what is the recommendation for applications that currently use TenantConfig to store configurations which is deprecated.


My reply was specifically for this comment:

Config will, by default, be treated conceptually as a singleton (won’t explicitly mix it) if the type has no other identifying mixin (e.g. Singleton , UserSingleton , Identifiable , Nameable …). As a rule of thumb, we recommend any type mixing Config to explicitly declare Singleton if applicable.

Yes any package can declare any number of Config subtypes. All I’m saying it is hard to say what should be “default” mixin besides Config for this sub-type without knowing what is it actually for.

So question you should ask is this configuration for a tag (i.e. for all types), for a specific type in the type hierarchy, for a user, for an instance of it or some other type identified by id, for an instance of it or some other type identified by name or some other special case.

If you give me your requirements I can recommend a model.

I’d love to hear other’s use-cases as well, but from my experience the main thing I’m looking for is the ability to have reasonable default values for application logic which can be modified without the need to re-provision.

This is the main reason I currently leverage TenantConfig at the moment, and this question came from seeing that the TenantConfig type has been deprecated with an instruction to use Config instead without any real documentation on the subject.

1 Like

For replacement to TenantConfig mixing Config, Singleton makes sense.

Then you want to decide weather config can be overridden per tag or should stay per tenant only.

And finally you can use post default on config fields to provide defaults.


type DevEnvConfig mixes Config, Singleton {
  developmentBranch: !string post default "'develop'"

And in code use DevEnvConfig.inst().developmentBranch

1 Like

How do I make it per-tag vs per-tenant?

See documentation of Ann.Config annotation and ConfigOverride enum - especially maxOverride and minOverride

1 Like

Note also that inst() is not working in 7.8 for app-level types, but you can use make(). For example, AlertConfig mixes Config and Singleton:

c = AlertConfig.make().getConfig() // == false = true
c.setConfig('TAG') // not documented?
c2 = AlertConfig.make().getConfig() // == true (only in the same tag)
// in case you have trouble with the current config and want the default:
c2.evictFromCache() // not documented?

Note that setConfig is private for a reason: see setConfigValue which is recommended and documented API

FYI - setConfig will save all fields as override. I.e. if you have a TENANT override and then use setConfig with just one field change for TAG it will record all existing fields as TAG override.

However if you use setConfigValue then just one field will be overridden in TAG and rest will continue to come from TENANT

1 Like

@DavidT Thanks for the examples. For TenantConfig, we were able to reference it on types that mix in the REST Type by referencing the C3Vault in annotations on the header on the type.

How should we tackle this with the new Config type?

Example Type mixing REST:

type MarketoRest mixes REST {
...REST api methods...

related post:

@laura.foulquier (FYI)

In 7.9 we introduced a concept of configurable annotations. Currently two annotations are configurable. 1. Ann.Rest 2. Ann.Facade.

Let us take an example.

type ExternalAPI mixes REST

In this example at design time we decided to make as the url. Now, to update this at runtime you can do this
TypeAnnotationConfig.make({name: 'ExternalApi'}).setValue('rest', 'url', '')

The action also take a 4th parameter to specify the ConfigOverride. Currently, Ann.Rest and Ann.Facade annotations can be dynamically updated.

I tried using Cached and its static findById but I get the close-to-familiar cast error:

state = HWCWprocessState.findById('s00313')
c3.type.metadata.impl.PersistableImpl cannot be cast to c3.type.metadata.Cached


entity type HWCWprocessState mixes Cached schema name 'HWCWPS' {
    lastEvalTime: datetime post default '1970-01-01'
    eventsIgnored: int post default 0

Can I just use get, create and upsert? They seem to work.