Giter VIP home page Giter VIP logo

configuration's Introduction

Kitura

APIDoc Build Status - Master macOS Linux Apache 2 Slack Status

Configuration

Configuration is a Swift package for managing application configurations. Using Configuration, an application can easily load and merge configuration data from multiple sources and access them from one central configuration store.

Configuration supports configuration keys as paths. That is, a key is a qualified path selector written in the [parent]<separator>[child] syntax. This allows applications to retrieve configuration objects at any level of specificity.

Version

The latest release of Configuration (v3.x.x) runs on Swift 5.2 and newer, on both macOS and Ubuntu Linux.

Usage

Add dependencies

Add Configuration to the dependencies within your application's Package.swift file. Substitute "x.x.x" with the latest Configuration release.

.package(url: "https://github.com/Kitura/Configuration.git", from: "x.x.x")

Add Configuration to your target's dependencies:

.target(name: "example", dependencies: ["Configuration"]),

Initialize Configuration

The core of the Configuration package is the ConfigurationManager class. To manage your application's configurations, first create an instance of the ConfigurationManager class.

import Configuration

let manager = ConfigurationManager()

Using the ConfigurationManager instance, you can then load and retrieve configurations:

manager.load(file: "config.json").load(.environmentVariables)
let value = manager["path:to:configuration:value"]

Loading Configuration Data

Configuration has many methods to load configuration data.

NOTE: In all cases, configuration key paths are case sensitive.

From a Raw Any Object

manager.load([
    "hello": "world",
    "foo": [
        "bar": "baz"
    ]
])

From Command-line Arguments

manager.load(.commandLineArguments)

To inject configurations via the command-line at runtime, set configuration values when launching the executable, like so:

./myApp --path.to.configuration=value

You can set your preferred argument prefix (default: --) and path separator (default: .) strings when instantiating ConfigurationManager.

From Environment Variables

manager.load(.environmentVariables)

Then, to use it in your application, set environment variables like so:

PATH__TO__CONFIGURATION=value

You can set your preferred path separator (default: __) string when instantiating ConfigurationManager.

From a Data Object

let data = Data(...)
manager.load(data: data)

From a File

manager.load(file: "/path/to/file")

By default, the file argument is a path relative from the location of the executable (i.e., .build/debug/myApp); if file is an absolute path, then it will be treated as such. You can change the relative-from path using the optional relativeFrom parameter, like so:

// Resolve path against PWD
manager.load(file: "../path/to/file", relativeFrom: .pwd)

// or

// Resolve path against a custom path
manager.load(file: "../path/to/file", relativeFrom: .customPath("/path/to/somewhere/on/file/system"))

NOTE: The following relativeFrom options, .executable (default) and .pwd, will reference different file system locations if the application is run from inside Xcode than if it is run from the command-line. You can set a compiler flag, i.e. -DXCODE, in your xcodeproj and use the flag to change your configuration file loading logic.

NOTE: Because BasePath.project depends on the existence of a Package.swift file somewhere in a parent folder of the executable, changing its location using swift build --build-path is not supported.

From a URL

let url = URL(...)
manager.load(url: url)

NOTE: The URL MUST include a scheme, i.e., file://, http://, etc.

From Multiple Sources

You can chain these methods to load configuration data from multiple sources all at once. If the same configuration key exists in the multiple sources, the one most recently loaded will override the ones loaded earlier. In this simple example,

manager.load(["foo": "bar"]).load(["foo": "baz"])

the value for foo is now baz because ["foo": "baz"] was more recently loaded than ["foo": "bar"]. The same behavior applies to all other load functions.

NOTE: Currently, Configuration only supports JSON and PLIST formats for resources loaded from Data, file, or URL. You can write a custom deserializer to parse additional formats.

Accessing Configuration Data

To get individual configuration values after they have been loaded, use:

manager["path:to:configuration"]

The configuration store is represented as a tree, where the path elements in keys are delimited by colons (:). For instance, the key VCAP_SERVICES:cloudantNoSQLDB:0:credentials:host would traverse into VCAP_SERVICES, cloudantNoSQLDB, array index 0, credentials, and then host to grab the value. Here is a JSON example of the structure:

{
    "VCAP_SERVICES": {
        "cloudantNoSQLDB": [
            {
                "credentials": {
                    "host": <value-goes-here>
                }
            }
        ]
    }
}

The value returned is typed as Any?. Therefore, it's important to cast the value to the type you want to use. For instance:

let stringValue = manager["VCAP_SERVICES:cloudantNoSQLDB:0:credentials:host"] as? String

You can also retrieve configuration objects via partial paths; for example, if you use manager["VCAP_SERVICES:cloudantNoSQLDB:0:credentials"], the result is a dictionary of the key-values in credentials.

To get all configuration values in the configuration store, use:

manager.getConfigs()

The return value is a raw representation of all configuration values currently in the configuration store.

Acknowledgements

Configuration was inspired by nconf, a popular NodeJS hierarchical configuration manager.

API Documentation

For more information visit our API reference.

License

This library is licensed under Apache 2.0. Full license text is available in LICENSE.

configuration's People

Contributors

andrew-lees11 avatar dannys42 avatar djones6 avatar helenmasters avatar ianpartridge avatar rfdickerson avatar segabor avatar shihabmehboob avatar swiftdevops avatar tunniclm avatar youming-lin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

configuration's Issues

Fix unit tests for Swift 4/Xcode 9

  • Don't call XCTFail in class level setup and teardown
  • Rework tests that use symlinkInExecutableFolder as the code may not have the permissions to modify that folder

Support loading files relative to project root directory

Commonly we store configuration files in the project root (or a subdirectory of it like "config").
It is desirable to be able to specify:

manager.load(file: "config.json", relativeFrom: .project)

Additionally, it would be good to have this continue to work if the executable is not located in .build/debug or .build/release, for example, in some other part of the project tree. Therefore the project root should be where the Package.swift resides.

Improve Xcode tests to cover more of code base

Missing tests:

  1. load relative from .executable
  2. load relative from .project

Attempted to copy test config file to the executable directory, but that directory under swift test has permissions that prevent it from being written to by the test process. Possible solution is to write these tests in the TestProgram module and then swift build the module from within the unit test, and then run this executable, as opposed to the one that is build by swift test.

Support more robust determination of the executable location

Using CommandLine.arguments[0] is problematic for the case where the executable is run via a relative rather than absolute path, or when it is run via PATH. This causes problems loading config files in Bluemix.

Bundle.main.executableURL and friends offer a more robust solution is my testing. However, it is not yet supported on Linux Foundation. However,
URL(fileURLWithPath: "/proc/self/exe").resolvingSymlinksInPath() provides a fallback for Linux.

Auto-parse strings into objects

For example, the VCAP_SERVICES environment variable. Swift exposes environment variables as [String: String], which means that the value of the VCAP_SERVICES variable is a String. For easier manipulation, Configuration should parse such strings into [String: String] automatically by default.

Swift 4 Migration

  • SPM is changing executable location again. BasePath.project will be removed along with implicit Xcode support.
  • Update Package.swift to Swift 4 format.
  • Add new APIs to support Codable.

Cocoapods Support

I see that you're already supporting the Swift Package Manager. I think it would still be great addition if you could support Cocoapods as well. The main reason being that a lot of current libraries don't support the Swift Package Manager (but Cocoapods). Therefore users of your library would have more choices for integration.

Move TestProgram into XCT Tests

TestProgram was written to test code that has no easy way to be tested inside XCT test cases (i.e. code that parses env var and argv). We should revisit this issue and see if there is any way to test those code in XCT without having to build a separate executable.

Use warning log level when file cannot be loaded.

Currently the error log level is used when a file cannot be loaded. Not finding a file is a very common scenario in our apps. It would be better to use, say warning as the log level for logging the message that states that a file cannot be loaded.

Maintain type when overriding values via env var and argv

Env var and argv will assign non-JSON, non-PLIST to String type, always. This makes overriding numeric values with numeric values impossible. We should figure out a way to coerce the string values from env var and argv into the correct types.

Generated projects producing warning messages

When a project created by the generator is built, it produces the following warning in the logs:
[2018-11-15T14:49:46.057Z] [WARNING] [ConfigurationManager.swift:261 load(url:deserializerName:)] Unable to load data from URL /home/djones6/MyProj/config/mappings.json
The warning is due to attempts to load a mappings.json file from CloudEnvironment, and is intentional as the mappings.json file is important when a project is created with services. However, as a first experience of running a generated Kitura project, this looks unpleasant.

We could consider reducing the severity of this message, or introduce an empty mappings.json file into generated Kitura projects.

A second message comes from CoreFoundation on Linux:
2018-11-15 14:49:46.059 MyProj[15474:7f70f7c0] CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary on line 1. Parsing will be abandoned. Break on _CFPropertyListMissingSemicolon to debug.
Need to track down where this is coming from.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.