quark-config

Coditory Quark Configuration Library

License

License

Categories

Categories

config Application Layer Libs Configuration
GroupId

GroupId

com.coditory.quark
ArtifactId

ArtifactId

quark-config
Last Version

Last Version

0.1.2
Release Date

Release Date

Type

Type

pom.sha512
Description

Description

quark-config
Coditory Quark Configuration Library
Project URL

Project URL

https://github.com/coditory/quark-config
Project Organization

Project Organization

Coditory
Source Code Management

Source Code Management

https://github.com/coditory/quark-config

Download quark-config

Dependencies

runtime (2)

Group / Artifact Type Version
org.yaml : snakeyaml jar 1.28
com.google.code.gson : gson jar 2.8.6

Project Modules

There are no modules declared in this project.

Quark Config

Build Status Coverage Status Maven Central

Quark Config is a lightweight and single purpose java library for loading and manipulating configurations

The idea behind was to create a configuration library, similar to the one created in Spring Boot of typesafe, that is:

  • lightweight, without a burden of a framework
  • supports standard YAML or JSON format
  • provides developer-friendly API
  • hides secrets when formatting to string
  • provides basic expressions for references and default values
  • provides a collection of parsers for values such as java.util.Duration
  • loads an application multi source config with a one-liner

Installation

Add to your build.gradle:

dependencies {
    implementation "com.coditory.quark:quark-config:0.1.2"
}

Usage

Load application configuration

In most cases application should load the config with a one-liner:

public class Application {
    public static void main(String[] args) {
        Config config = ConfigFactory.loadApplicationConfig(args);
        System.out.println(config);
        // Sample output:
        // {application={port=8080, name=best-app}, db={password=***}}
    }
}

Config merging order

Application configuration sources are merged into a single config object according to the following order:

  • base config
    • application.{yml,json,properties} file from classpath
    • this is the only file that is required
  • profile config
    • application-${profile}.{yml,json,properties} file from classpath
    • set profile with argument --profile=$PROFILE
    • the default profile is local
  • external config
    • config file from system passed as an argument --config.external=$PATH_TO_CONFIG
  • config values from arguments
    • all arguments passed as --config.<config_key>=$VALUE become a config value

Example:

# file: application.yml
application:
  name: "best-app"
  port: ${env.PORT ? args.port ? 7070}

# file: application-local.yml
application:
  port: 8080

# file: application-prod.yml
application:
  port: 80
// load config with no arguments
ConfigFactory.loadApplicationConfig();
// Output: {application={port=8080, name=best-app}}

// load config with prod profile
ConfigFactory.loadApplicationConfig("--profile", "prod");
// Output: {application={port=80, name=best-app}}

// load config with non-existent profile
ConfigFactory.loadApplicationConfig("--profile", "other");
// Output: {application={port=7070, name=best-app}}

// resolve config with expression
ConfigFactory.loadApplicationConfig("--profile", "other", "--port", "7071");
// Output: {application={port=7071, name=best-app}}

// load config with config argument
ConfigFactory.loadApplicationConfig("--config.application.port", "8081");
// Output: {application={port=8081, name=best-app}}

For more examples see the test cases.

Config resolution variables

You can use following expression variables to resolve an application config:

  • ${env.*} - all system variables from System.getenv()
  • ${system.*} - all system variables from System.getProperties()
  • ${args.*} - all arguments

Creating a config

You can create a config object in multiple ways:

// Create from var args
Config.of(
    "application.name", "best-app",
    "application.port", 8080
);

// Create from map
Config.of(Map.of("application.port", 8080));

// Create empty config and add value
Config.empty()
    .withValue("application.port", 8080);

// Build own config with customizations
Config.builder()
    .withValue("application.name", "best-app")
    .withValue("application.port", 8080)
    .withSecretHidingValueMapper(customSecretHider)
    .withValueParser(customParser)
    .build();

Config object is immutable. Every time you add a value to a config, new instance is returned:

Config c1 = Config.of("application.port", 8080);
Config c2 = c1.withValue("application.name", "best-app");

assert c1 != c2
assert !c1.equals(c2)

Resolving a config value

// throws if config value is not defined
config.getInteger("application.port")

// returns a default value if config value is not defined
config.getInteger("application.port", 8080)

// returns null if config value is not defined
config.getIntegerOrNull("application.port")

// returns an Optional from config value
config.getIntegerAsOptional("application.port")

Quark Config provides a lot of parsers so String values may be retrieved as a non String objects:

Config.of(
    "port", "8080",
    "timeout", "1s",
    "locale", "pl-PL",
    "currency", "PLN",
    "timestamp", "2007-12-03T10:15:30.00Z"
)
assert config.getInteger("port") == 8080
assert config.getInteger("timeout") == Duration.parse("PT1S")
assert config.getInteger("locale") == new Locale("pl", "PL")
assert config.getInteger("currency") == Currency.getInstance("PLN")
assert config.getInteger("timestamp") == Instant.parse("2007-12-03T10:15:30.00Z")

Resolving a subconfig:

Config applicationConfig = config.getSubConfig("application")
applicationConfig.getInteger("port")

Merging configs

Two config objects can be merged together:

Config config = Config.of(
    "a", "A",
    "b", "B"
);
Config other = Config.of(
   "b", "X",
   "c", "Y"
);

config.withDefaults(other);
// {a=A, b=B, c=Y}

config.withValues(other);
// {a=A, b=X, c=Y}

Config expressions

Config values can use som basic expressions:

// Config with references to other value
Config.builder()
    .withValue("a", "${b}")
    .withValue("b", "B")
    .build()
    .resolveExpressions();
// {a=B, b=B}

// Config with a fallback value
Config.builder()
    .withValue("a", "${b ? X}")
    .build()
    .resolveExpressions();
// {a=X}

// Config with a double fallback value
Config
    .of("a", "${b ? c ? X}")
    .resolveExpressions();
// {a=X}

// Config with expression values
Config
    .of("a", "${b ? c ? X}")
    .resolveExpressions(Config.of(c, "C"));
// {a=C}

Load config from file

Config can be loaded from:

  • classpath file
  • file from file system

You don need to specify extension of the configuration file. Quark Config will automatically look for: *.yml, *.yaml, *.json, *.properties

ConfigFactory.loadFromClasspath("custom-config");
ConfigFactory.loadFromFileSystem("custom-config");

Parsing and formatting a config

Quarkus Config supports following formats:

  • yaml
  • json
  • properties
// parsing a config
ConfigFactory.parseYaml(yaml);
ConfigFactory.parseJson(json);
ConfigFactory.parseProperties(properties);

// formatting a config
ConfigFormatter.toYaml(config);
ConfigFormatter.toJson(config);
ConfigFormatter.toProperties(config);

// formatting a config with exposed secrets
ConfigFormatter.toYamlWithExposedSecrets(config);
ConfigFormatter.toJsonWithExposedSecrets(config);
ConfigFormatter.toPropertiesWithExposedSecrets(config);

// config.toString() method uses output of a Map with hidden secrets
config.toString()
com.coditory.quark

Coditory

Versions

Version
0.1.2
0.1.1
0.1.0