Skip to content

Basic Usage

The UNIFYConnect v6+ configuration system, in its basic usage, is similar to the previous, XML based system, in that the data structure configuration is provided to resources (connectors or agents) represents the serialised form of the configuration (JSON) and provides an API that allows access to and control over the configuration structure and values.

Data Structure

Interfaces

InterfaceDescription
IConfigValueThe base interface for the other configuration interfaces and objects (enums excluded, see below). Some configuration methods accept or return IConfigValue. Has the Type property that denotes the type of configuration value.
IConfigObjectA structured configuration value type, containing a set of keyed configuration values. Has many methods for writing values of different types, and reading configuration values in a type-safe way. Also features the SchemaVersion property (see Versioning Objects section below).
IConfigArrayA structured configuration value type, containing a set of configuration values, of any kind. Has many methods for reading and writing configuration values of appropriate types.
IConfigValue<T>Represents a single, configuration value. While no restrictions are placed on the T type parameter, and custom implementations of this interface could be created IConfigObject and IConfigObject only accept a specific set of implementation types.
IConfigEnum<T>A configuration value type for representing enums. This interfaces, and its implementations, do not implement IConfigValue like the other value types, but instead wrap IConfigValue<string>. Enum values are converted to string values when serialised, and do not persist any information about the original enum type. This means care should be taken to ensure the correct enum type is used when reading enum configuration values. Supports flag enums

IConfigValue<T> Implementations

A specific set of data types have IConfigValue<T> implementations, and structured value types are designed to work with and only accept implementations of these types. Custom implementations are not supported.

With a IConfigValue<T> accessible, the Value property can be used to access the internal value for use.

TypeImplementationsNotes
boolConfigBoolean
System.DateTime<code></code>ConfigDateTime
doubleConfigFloatingPointNamed as such as double is a double-precision floating point. If this type needs to represent a float value, use the GetSingleValue extension method.
System.GuidConfigGuid
longConfigIntegerNamed as such as double is a 64bit integer value. If this type needs to represent a int or short value, use the GetInt32Value or GetIn16Value extension methods.
stringConfigString, SecureConfigStringUse SecureConfigString for value that should be encrypted. Instantiate this type with the static Create function.
System.TimeSpanConfigTimeSpan

Optional Configuration Values

A set of structs that represents a IConfigValue or IConfigEnum<T> that may or may not be set. Features a variety of methods for examining the optional value and accessing the internal value, if it is set.

C#
// Initialisers
var setValue = ConfigValueOption.Some<string>(aStringValue);
var notSetValue = ConfigValueOption.None<string>();
var setEnum = ConfigEnumOption.Some<Locations>(anEnumValue);
var notSetEnum = ConfigEnumOption.None<Locations>();
var setObject = ConfigObjectOption.Some(anObjectValue);
var notSetObject = ConfigObjectOption.None();
var setArray = ConfigArrayOption.Some(anArrayValue);
var notSetArray = ConfigArrayOption.None();

// State testing
setValue.IsSome()        // true
setValue.IsNone()        // false
notSetValue.IsSome()     // false
notSetValue.IsNone()     // true

// Unwrapping 
// Not all methods listed: many methods have variants and overloads with different features
// Use IDE tools and method documentation for more information.
setValue.Unwrap()                      // throws if None
setValue.UnwrapValue()                 // throws if None, returns the inner value
setValue.UnwrapOr(anotherValue)        // returns value if Some, or the other value passed in
setValue.UnwrapValueOr(anotherValue)   // combination of `UnwrapValue` and `UnwrapOr`
setValue.UnwrapOrThrow(ThrowSomeEx)    // like `Unwrap` but throws a custom exception

// Conditional unwrapping
setValue.TryUnwrap(out IConfigValue<string> value) // returns true with `value` populated if Some
setValue.TryValueUnwrap(out string value) // returns true with `value` populated with inner value if Some

// Selecting
setValue.Match(OnSome, OnNone)   // calls appropriate void function
var result = setValue.Switch(ConvertValue, GetDefaultValue) // calls appropriate returning function

Reading Configuration Values

Both IConfigObject and IConfigArray implementations have a variety of methods for reading configuration values of different types.

C#
// Getting: will throw an exception if value no value, or one of incompatible type is found
IConfigValue<string> usernameValue = configObject.GetString("username");
IConfigValue<long> codeValue = configObject.GetInteger("code");
IConfigObject nestedObject = configObject.GetObject("nested");
IConfigValue<Guid> resourceId = configArray.GetGuidAt(0);
IConfigArray nestedArray = configArray.GetArrayAt(3);

// Maybe Getting: returns ConfigValueOption, ConfigEnumOption, ConfigArrayOption or ConfigObjectOption
ConfigValueOption<string> usernameValueMaybe = configObject.MaybeGetString("username");
ConfigValueOption<long> codeValueMaybe = configObject.MaybeGetInteger("code");
ConfigObjectOption nestedObjectMaybe = configObject.MaybeGetObject("nested");
ConfigValueOption<guid> resourceIdMaybe = configArray.MaybeGetGuidAt(0);
ConfigArrayOption nestedArrayMaybe = configArray.MaybeGetArrayAt(3);

Writing Configuration Values

Both IConfigObject and IConfigArray implementations have a variety of methods for writing configuration values of different types. For most configuration value types, there are convenience overloads that take the internal value only, and handle the construction of the proper, configuration value type.

C#
configObject.Set("username", new StringValue("john01"));
configObject.Set("code", 45109);

configArray.Add(true);                   // append to end
configArray.SetAt(3, DateTime.UtcNow)    // set at specific index

Initialising configuration objects and arrays are handled differently to single values. Instead, IConfigObject and IConfigArray have initialisation method that create a new structure of the desired type, insert it, then return it to populated.

C#
IConfigObject objInObj = parentObject.InitObject("nestedObj", new Version(1, 0));
IConfigArray arrayInObj = parentObject.InitArray("nestedArray");

IConfigObject objInArray1 = parentArray.InitObject(new Version(1, 0));        // append
IConfigObject objInArray2 = parentArray.InitObjectAt(3, new Version(1, 0));   // at index
IConfigArray arrayInArray1 = parentArray.InitArray();       // append
IConfigArray arrayInArray2 = parentArray.InitArrayAt(5);    // at index

Versioning Objects

As a connector or agent changes over time, its configuration schema is also likely to change. To manage these changes, IConfigObject has a version property that identifies the schema used to serialise it. This allows schema changes to be handled gracefully when reading configuration, including which values should be expected and when a default should be substituted.

Configuration object versions are 2-value version number in dot notation (ie 1.4). Connector plug-in maintainers can choose a version incrementing strategy that suits, however the recommended approach is to follow Semantic Versioning, where the major version is incremented for non-backwards-compatible changes, and the minor version changed for all others.

The scope of a configuration objects version should be the key, type and required status of all it's properties, and members of array properties. The schema of nested objects do not fall under the scope of it's parent's version, unless it changes to such an extent that it is considered a different type altogether.

Usage - Reading

When reading the properties of a configuration object, the version should be used to know when specific values should or shouldn't be read, the type they should be, or if the value should be expected or optional. The version may also be switched upon to enable separate reading routines to be used for different versions. Any new required properties, whether previously existing or not, should be assigned a default value here.

C#
// checking
int id = configObject.SchemaVersion > new Version(1, 5)
    ? factoryInformation.Extended.GetInteger("pageSize").GetInt32Value()
    : DefaultPageSize;

// switching
var connectorInformation = configObject.SchemaVersion switch
{
    { Major: >=3 } => CreateConnectorInformationV3(configObject),
    { Major: >=2 } => CreateConnectorInformationV1(configObject),,
    _ => CreateConnectorInformationV1(configObject),
};

Usage - Writing

Each configuration object should be written in their latest schema, with no consideration given to any previous schema versions, or the format of the configuration that currently exists.

C#
IConfigObject configObject = new ConfigObject(ExampleConfigConstants.ConnectorExtendedVersion);
configObject.SetInteger("pageSize", 100);

Usage - Legacy Configuration Adapter

Refer to Legacy Extended Configuration Adapter for more information.