ConfigurationTransformer
open class ConfigurationTransformer : ConfigurationTransforming
ConfigurationTransformer
transforms a [String: Any]
configuration to typed objects and returns them in a [OnboardingInfoKey: Any]
. It uses other ConfigurationTransforming
instances for the transformation, making it a recursive transformer which can be used to process complex, deep configuration hierarchies.
The transformation process iterates through the keys of the configuration Dictionary and looks up the transformers using that key (in the transormer dictionary). When a transformer is found under the specified key, the corresponding sub-configuration is passed to it for processing. All transformers’ result is merged internally together, and the final result is a flat Dictionary of [OnboardingInfoKey: Any]
.
The SDK ships other transformers to help the transforming of any configuration and of course it can be used with custom transformers as well.
An example: Given the following sample structure:
let configuration : [String: Any] = [
"someURL": "https://www.sap.com",
"someString": "An arbitrary string",
"simpleStructure": [
"firstStringField": "first string value",
"secondURLField": "https://www.sap.com"
],
"complexStructure": [
"some.oauth.params.key": [
"authorizationEndpointURL": "https://someoauthservice.sap.hana.ondemand.com/oauth2/api/v1/authorize",
"clientID": "66962837-fd94-4dcd-acf7-ade549224e78",
"redirectURL": "https://someoauthservice.int.sap.hana.ondemand.com",
"tokenEndpointURL": "https://someoauthservice.int.sap.hana.ondemand.com/oauth2/api/v1/token"
],
"some.saml.params.key": [
"authenticationEndpoint": "https://somesamlservice.int.sap.hana.ondemand.com/SAMLAuthLauncher",
"finishEndpoint": "https://somesamlservice.int.sap.hana.ondemand.com/SAMLAuthLauncher?finishEndpointParam=someUnusedValue"
],
"DiscoveryServiceConfigForCertificate": [
"com.sap.mobilesecure.certificateService.attributesEndpoint": "http://some.dummy.url",
"com.sap.mobilesecure.certificateService.requestEndpoint": "http://some.dummy.url",
"com.sap.mobilesecure.certificateService.retireEndpoint": "http://some.dummy.url",
"com.sap.mobilesecure.certificateService.publicKeyPinSet": [],
"com.sap.mobilesecure.certificateService.authType": [
"type":"oauth",
"authorizationEndpoint": "http://some.dummy.url",
"tokenEndpoint": "http://some.dummy.url",
"client_id": "1234",
"redirect_uri": "http://some.dummy.url"
]
]
]
]
This configuration must be converted to a flat [OnboardingInfoKey: Any]
dictionary where the values are appropriate types (struct, URL, String, etc). So as a result we have to get the following object:
let transformedDict : [OnboardingInfoKey: Any] = [
.requiredURL: URL,
.requiredString String,
.simpleStructure: SimpleStructure,
.oauth2Parameters: OAuth2AuthenticationParameters,
.samlParameters: SAMLAuthenticationParameters,
.oauth2ParamsForCertificate: OAuth2AuthenticationParameters,
.discoveryServiceParametersForCertificate: SAPcpmsUserIdentityConfigurationParameters
]
To transform the source configuration dictionary to the expected result, the ConfigurationTransformer
must be used with appropriate transformers mirroring the structure:
// let's describe the given configuration with a transformation where we specify the possible keys, values as:
// source: typedtransformer( destination )
let configurationTransformers: [String: ConfigurationTransforming] = [
"someURL": BaseTransformer(targetKey: .requiredURL, transformer: transformToURL),
"someString": BaseTransformer(targetKey: .requiredString, transformer: transformToString),
"simpleStructure": BaseTransformer(targetKey: .simpleStructure, transformer: SimpleStructure.init(customConfig:)),
"complexStructure": DictionaryConfigurationTransformer(transformers: [
"some.oauth.params.key": BaseTransformer(targetKey: .oauth2Parameters, transformer: OAuth2AuthenticationParameters.init(cloudPlatformConfig:)),
"some.saml.params.key": BaseTransformer(targetKey: .samlParameters, transformer: SAMLAuthenticationParameters.init(cloudPlatformConfig:)),
"DiscoveryServiceConfigForCertificate": CustomDiscoveryServiceConfigurationTransformer(key: .discoveryServiceParametersForCertificate, oauthKey: .oauth2ParamsForCertificate)
])
]
and use the transformer and the transformed data:
let configurationTransformer = ConfigurationTransformer(transformers: configurationTransformers)
do {
let transformedDictionary = try configurationTransformer(config: configuration)
if let simpleStructure = transformedDictionary[.simpleStructure] as? SimpleStructure {
// do something
}
}
catch {
print("some error happened \(error)")
}
Of course as a prerequisite the OnboardingInfoKey
s must be declared, and the used transformers must exists.
// specifying onboarding info keys: declared by the onboarding steps
public extension OnboardingInfoKey {
public static let requiredURL = OnboardingInfoKey("requiredURLKey")
public static let requiredString = OnboardingInfoKey("requiredStringKey")
public static let simpleStructure = OnboardingInfoKey("simpleStructureKey")
public static let oauth2Parameters = OnboardingInfoKey("oauth2ParametersKey")
public static let samlParameters = OnboardingInfoKey("samlParametersKey")
public static let oauth2ParamsForCertificate = OnboardingInfoKey("oauth2ParametersForCertificateKey")
public static let discoveryServiceParametersForCertificate = OnboardingInfoKey("discoveryServiceParametersForCertificateKey")
}
// declaring a custom structure
public struct SimpleStructure {
let firstStringField: String
let secondURLField: URL
}
// writing a transformer for the structure which can be used directly or by the BaseTransformer with ConfigurationTransformer
public extension SimpleStructure {
public init(customConfig config: Any) throws {
guard let simpleConfig = config as? [String: String] else {
throw OnboardingError.invalidArgument("not a dictionary", config, source: "SimpleStructure")
}
guard let first = simpleConfig["firstStringField"], let secondStr = simpleConfig["secondURLField"] else {
throw OnboardingError.missingArgument("first or second parameter missing while parsing SimpleStructure", source: "SimpleStructure")
}
let second = try transformToURL(secondStr)
self.init(firstStringField: first, secondURLField: second)
}
}
open class CustomDiscoveryServiceConfigurationTransformer: ConfigurationTransforming {
let key : OnboardingInfoKey
let oauthKey : OnboardingInfoKey
public init(key : OnboardingInfoKey, oauthKey: OnboardingInfoKey) {
self.key = key
self.oauthKey = oauthKey
}
open func transform(config: Any) throws -> [OnboardingInfoKey: Any] {
guard let dict = config as? [String:Any] else {
throw OnboardingError.invalidArgument("not a dictionary", config, source: "CustomDiscoveryServiceConfigurationTransformer")
}
guard let params = SAPcpmsUserIdentityConfigurationParameters(certificateDiscoveryConfiguration: dict) else {
throw OnboardingError.invalidArgument("Cannot transform DiscoveryServiceConfigurationParser: Invalid structure", dict, source: "CustomDiscoveryServiceConfigurationTransformer")
}
guard let oauthParams = OAuth2AuthenticationParameters(certificateDiscoveryConfiguration: dict) else {
throw OnboardingError.missingArgument("Cannot transform DiscoveryServiceConfigurationParser: Invalid structure", source: "CustomDiscoveryServiceConfigurationTransformer")
}
return [key: params, oauthKey: oauthParams]// as [OnboardingInfoKey: Any]
}
}
In the SDK the different onboarding steps declare their required keys they use for accessign information in the info field of the OnboardingContext. Several default transformers are available as public funcs or default initializers. Several structures which is an input parameter for a step in the Foundation framework implements a convenience initializer with discoveryServiceConfig:
prefix which parse the default configuration declared by the SDK.
For example:
BaseTransformer
can be used with transformers resulting a single object, see a list of examples there.DiscoveryServiceCertificateConfigurationTransformer
-
the transformers used to transform the source dictionary. The keys are used to match the key entries in the source dictionary and the tranformers are used to tranform the data can be found under that key
Declaration
Swift
open var transformationMap: [String : ConfigurationTransforming]
-
Initializes a new instance of the class.
Declaration
Swift
public init(transformationMap: [String : ConfigurationTransforming])
Parameters
transformers
the transformers used to transform the source dictionary. The keys are used to match the key entries in the source dictionary and the tranformers are used to tranform the data can be found under that key
-
tranforms the source data which must be a [String: Any] dictionary using the associated
transformers
and returns with a [OnboardingInfoKey: Any] dictionary containing the transformed objects under the keys the transformers associatedThrows
OnboardingError.invalidArgument or errors thrown by tranformersDeclaration
Swift
open func transform(config: Any) throws -> [OnboardingInfoKey : Any]
Parameters
config
the source configuration to transform must be [String: Any] compatible
Return Value
[OnboardingInfoKey: Any] dictionary of the transformed data where the keys and values are associated by the
transformers