GithubHelp home page GithubHelp logo

miracl / conflate Goto Github PK

View Code? Open in Web Editor NEW
45.0 4.0 6.0 5 MB

Library providing routines to merge and validate JSON, YAML and/or TOML files

License: Apache License 2.0

Go 100.00%
configuration config configuration-management golang golang-library json json-schema yaml toml merge structs

conflate's Introduction

gophers

CONFLATE

Library providing routines to merge and validate JSON, YAML, TOML files and/or structs (godoc)

Typical use case: Make your application configuration files multi-format, modular, templated, sparse, location-independent and validated

Build Status Coverage Status Go Report Card

Description

Conflate is a library and cli-tool, that provides the following features :

  • merge data from multiple formats (JSON/YAML/TOML/go structs) and multiple locations (filesystem paths and urls)
  • validate the merged data against a JSON schema
  • apply any default values defined in a JSON schema to the merged data
  • expand environment variables inside the data
  • marshal merged data to multiple formats (JSON/YAML/TOML/go structs)

It supports draft-04, draft-06 and draft-07 of JSON Schema. If the key $schema is missing, or the draft version is not explicitly set, a hybrid mode is used which merges together functionality of all drafts into one mode. Improvements, ideas and bug fixes are welcomed.

Getting started

Run the following command, which will build and install the latest binary in $GOPATH/bin

go install github.com/miracl/conflate/...@latest

Alternatively, you can install one of the pre-built release binaries from https://github.com/miracl/conflate/releases

Usage of Library

Please refer to the godoc and the example code

Usage of CLI Tool

Help can be obtained in the usual way :

$conflate --help
Usage of conflate:
  -data value
    	The path/url of JSON/YAML/TOML data, or 'stdin' to read from standard input
  -defaults
    	Apply defaults from schema to data
  -expand
    	Expand environment variables in files
  -format string
    	Output format of the data JSON/YAML/TOML
  -includes string
    	Name of includes array. Blank string suppresses expansion of includes arrays (default "includes")
  -noincludes
    	Switches off conflation of includes. Overrides any --includes setting.
  -schema string
    	The path/url of a JSON v4 schema file
  -validate
    	Validate the data against the schema
  -version
    	Display the version number

To conflate the following file ... :

$cat ./testdata/valid_parent.json
{
  "includes": [
    "valid_child.json",
    "valid_sibling.json"
  ],
  "parent_only" : "parent",
  "parent_child" : "parent",
  "parent_sibling" : "parent",
  "all": "parent"
}

...run the following command, which will merge valid_parent.json, valid_child.json, valid_sibling.json :

$conflate -data ./testdata/valid_parent.json -format JSON
{
  "all": "parent",
  "child_only": "child",
  "parent_child": "parent",
  "parent_only": "parent",
  "parent_sibling": "parent",
  "sibling_child": "sibling",
  "sibling_only": "sibling"
}

Note how the includes are loaded remotely as relative paths.

Also, note values in a file override values in any included files, and that an included file overrides values in any included file above it in the includes list.

If you instead host a file somewhere else, then just use a URL :

$conflate -data https://raw.githubusercontent.com/miracl/conflate/master/testdata/valid_parent.json -format JSON
{
  "all": "parent",
  "child_only": "child",
  "parent_child": "parent",
  "parent_only": "parent",
  "parent_sibling": "parent",
  "sibling_child": "sibling",
  "sibling_only": "sibling"
}

The includes here are also loaded as relative urls and follow exactly the same merging rules.

To output in a different format use the -format option, e.g. TOML :

$conflate -data ./testdata/valid_parent.json -format TOML
all = "parent"
child_only = "child"
parent_child = "parent"
parent_only = "parent"
parent_sibling = "parent"
sibling_child = "sibling"
sibling_only = "sibling"

To additionally use defaults from a JSON schema and validate the conflated data against the schema, use -defaults and -validate respectively :

$cat ./testdata/blank.yaml

$conflate -data ./testdata/blank.yaml -schema ./testdata/test.schema.json -validate -format YAML
Schema validation failed : The document is not valid against the schema : Invalid type. Expected: object, given: null (#)

$conflate -data ./testdata/blank.yaml -schema ./testdata/test.schema.json -defaults -validate -format YAML
all: parent
child_only: child
parent_child: parent
parent_only: parent
parent_sibling: parent
sibling_child: sibling
sibling_only: sibling

Note any defaults are applied before validation is performed, as you would expect.

If you don't want to intrusively embed an "includes" array inside your JSON, you can instead provide multiple data files which are processed from left-to-right :

$conflate -data ./testdata/valid_child.json -data ./testdata/valid_sibling.json -format JSON
{
  "all": "sibling",
  "child_only": "child",
  "parent_child": "child",
  "parent_sibling": "sibling",
  "sibling_child": "sibling",
  "sibling_only": "sibling"
}

Or alternatively, you can create a top-level JSON file containing only the includes array. For fun, lets choose to use YAML for the top-level file, and output TOML :

$cat toplevel.yaml
includes:
  - testdata/valid_child.json
  - testdata/valid_sibling.json

$conflate -data toplevel.yaml -format TOML
all = "sibling"
child_only = "child"
parent_child = "child"
parent_sibling = "sibling"
sibling_child = "sibling"
sibling_only = "sibling"

If you want to read a file from stdin you can do the following. Here we pipe in some TOML to override a value to demonstrate :

$echo 'all="MY OVERRIDDEN VALUE"' |  conflate -data ./testdata/valid_parent.json -data stdin  -format JSON
{
  "all": "MY OVERRIDDEN VALUE",
  "child_only": "child",
  "parent_child": "parent",
  "parent_only": "parent",
  "parent_sibling": "parent",
  "sibling_child": "sibling",
  "sibling_only": "sibling"
}

Note that in all cases -data sources are processed from left-to-right, with values in right files overriding values in left files, so the following doesn't work :

$echo 'all="MY OVERRIDDEN VALUE"' |  conflate -data stdin -data ./testdata/valid_parent.json  -format JSON
{
  "all": "parent",
  "child_only": "child",
  "parent_child": "parent",
  "parent_only": "parent",
  "parent_sibling": "parent",
  "sibling_child": "sibling",
  "sibling_only": "sibling"
}

You can optionally expand environment variables in the files like this :

$export echo MYVALUE="some value"
$export echo MYJSONMAP='{ "item1" : "value1" }'
$echo '{ "my_value": "$MYVALUE", "my_map": $MYJSONMAP }' | conflate -data stdin -expand -format JSON
{
  "my_map": {
    "item1": "value1"
  },
  "my_value": "some value"
}

Acknowledgements

Images derived from originals by Renee French https://golang.org/doc/gopher/

conflate's People

Contributors

andy-miracl avatar nicorikken 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

Watchers

 avatar  avatar  avatar  avatar

conflate's Issues

default object created always

For a schema such as

{
  "title": "test",
  "type": "object",
  "properties": {
    "obj": {
      "type": "object",
      "properties": {
        "x" : {
          "type": "integer",
          "default": 1
        }
      }
    }    
  }
}

If you supply a file consisting of {} then you should not expect {"obj":{"x":1}}, as there is no default for obj, only a default for obj.x.

You should need to either explicitly add an obj in the data i.e. {"obj": {}} or add a default for obj in the schema :

{
  "title": "test",
  "type": "object",
  "properties": {
    "obj": {
      "type": "object",
      "default": {},      <------------- HERE
      "properties": {
        "x" : {
          "type": "integer",
          "default": 1
        }
      }
    }    
  }
}

Currently this doesnt work as expected.

Failure to load v4 meta schema occasionally fails

Its not clear why this fails but if you turn off your internet access and then create schema.json containing only {}, then the following command occasionally fails :

conflate --schema schema.json --validate 

Whilst it is a mystery why this fails only occasionally, we never want conflate to retrieve the meta schema from the internet as we have it hard-coded.

Removing the id attribute of the metaSchema in schema.go appears to prevent this occurring. Seems to be a consequence of schema/metaschema validation deep in gojsonschema.

allow the cli tool to read from stdin

Support reading from stdin. We need to be able to specify where in the list of other files it should be inserted though i.e.

echo '{"x": 123}' | ./conflate ---data firstfile.json --data stdin --alaterfile.json --format JSON

support anyOf, allOf, oneOf, and not, in applyDefaults

This is subtle, e.g. with anyOf it is possible that the data matches several subschemas each with different default values. Do we apply (a) the first default, (b) the defaults in turn (ordering of map is not necessarily the same as the original schema data file), (c) the first default providing there are no other schemas providing defaults, (d) no defaults (unless there is a single matching subschema).

Similar issue for allOf, do we apply the defaults of each subschema in turn, overriding where necessary?

For oneOf, we must check that the data matches a unique subschema, if it matches more than one then we cannot reliably determine which default (from which subschema) to apply.

not is easy, since the data must never match any of the subschemas we know never to apply any of these defaults.

e.g.
https://spacetelescope.github.io/understanding-json-schema/reference/combining.html

panic: reflect: call of reflect.flag.mustBeExported on zero Value

Hi everyone,

I'm in the progress of migrating a project from perl to go and have problems with a previously working JSON schema file with conflate. I first tried gojsonschema and the schema was valid there, but had no default value integration, so I turned to conflate.

I ran into a panic (with my code and the conflate binary)

$ ~/go/bin/conflate -data testdata/data.yaml -schema schema/data.schema.json -validate -defaults
panic: reflect: call of reflect.flag.mustBeExported on zero Value

goroutine 1 [running]:
reflect.flag.mustBeExportedSlow(0x0)
        /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:222 +0xad
reflect.flag.mustBeExported(...)
        /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:216
reflect.Value.Set(0x147fbe0, 0xc000267c50, 0x194, 0x0, 0x0, 0x0)
        /usr/local/Cellar/go/1.13.4/libexec/src/reflect/value.go:1532 +0x56
github.com/miracl/conflate.applyDefaultsRecursive(0xc00026a850, 0xb, 0x14816e0, 0xc0001dab10, 0x1453340, 0xc000267c50, 0x14816e0, 0xc0001dade0, 0x0, 0x0)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/schema.go:208 +0xa1f
github.com/miracl/conflate.applyObjectDefaults(0xc00026a780, 0x6, 0x14816e0, 0xc0001dab10, 0x14816e0, 0xc0001da0f0, 0xc0001dad80, 0x0, 0x0)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/schema.go:247 +0x294
github.com/miracl/conflate.applyDefaultsRecursive(0xc00026a780, 0x6, 0x14816e0, 0xc0001dab10, 0x1453340, 0xc0002678e0, 0x14816e0, 0xc0001dad80, 0x0, 0x0)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/schema.go:215 +0x959
github.com/miracl/conflate.applyObjectDefaults(0x150ad41, 0x1, 0x14816e0, 0xc0001dab10, 0x14816e0, 0xc0001da060, 0xc0001dab10, 0x10bd, 0x12bd)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/schema.go:247 +0x294
github.com/miracl/conflate.applyDefaultsRecursive(0x150ad41, 0x1, 0x14816e0, 0xc0001dab10, 0x1453340, 0xc0000ba980, 0x14816e0, 0xc0001dab10, 0xc000188417, 0x0)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/schema.go:215 +0x959
github.com/miracl/conflate.applyDefaults(0x1453340, 0xc0000ba980, 0x14816e0, 0xc0001dab10, 0x0, 0x0)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/schema.go:160 +0x6c
github.com/miracl/conflate.(*Schema).ApplyDefaults(0xc000267820, 0x1453340, 0xc0000ba980, 0x0, 0x0)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/schema.go:77 +0x54
github.com/miracl/conflate.(*Conflate).ApplyDefaults(...)
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/conflate.go:112
main.main()
        /Users/andreas/go/pkg/mod/github.com/miracl/[email protected]/conflate/main.go:72 +0x7f2

with this schema properties:

...
"host" : {
          "type": [ "string", "null" ],
          "description": "Hostname",
          "pattern": "^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])$",
          "default": null
        },
...

If I change it to use only string (and allow the empty string in the validation), it works:

"host" : {
          "type": "string",
          "description": "Hostname",
          "pattern": "^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9]|)$",
          "default": ""
        },

It's not a big limitation, but I need to change my previously working schema file in order to get it to work with conflate.

Is this a bug worth noting?

Best,
Andreas

Cant read from pipes

i.e. the following command fails

$conflate --data <(echo '{"x": 123}') --format JSON
Failed to load url : 500 : file:///dev/fd/63

applying defaults to a blank file causes panic

e.g. the following command fails :

$ echo  | ./conflate --defaults --schema ../testdata/test.schema.json --data stdinN
panic: assignment to entry in nil map

goroutine 1 [running]:
github.com/miracl/conflate.applyDefaultsRecursive(0x84ac91, 0x1, 0x7b0e40, 0xc420085620, 0x7e1960, 0xc420231170, 0x0, 0x0)
	/home/andy/Documents/projects/GO/src/github.com/miracl/conflate/schema.go:119 +0x645
github.com/miracl/conflate.applyDefaults(0x7b0e40, 0xc420085620, 0x7e1960, 0xc420231170, 0x0, 0x0)
	/home/andy/Documents/projects/GO/src/github.com/miracl/conflate/schema.go:56 +0x5e
github.com/miracl/conflate.(*Conflate).ApplyDefaults(0xc420085620, 0x0, 0x1c)
	/home/andy/Documents/projects/GO/src/github.com/miracl/conflate/conflate.go:146 +0x56
main.main()
	/home/andy/Documents/projects/GO/src/stash.certivox.com/scm/msso/conflate/conflate/main.go:51 +0x617

Expand() expands unknown/unset env vars to blank strings

In some json there may be data which contains strings like $somename that do not represent environment variables e.g. data that represents code such as variables in golang templates. Secondly, there might be a case where the variable replace takes place in two phases under two different environments, where one environment contains some of the variables and the other environment contains the other variables. Hence, it is sensible that we should only perform replacements where we have the variable defined in the environment, else we should ignore it.

Improve error code

Remove error raw strings from main code, and create custom error type. Also rename context to make the code more understandable.

Expand should recursively expand env vars

If replacement of an env var sets a new env var then that too should be replaced, until no further expansions occur. Need to limit this to prevent possibility of infinite recursion.

support supplementary oneOf, anyOf, allOf, noneOf section in schema with valid 'type'

If you have a schem with a valid type and also oneOf anyOf, allOf or noneOf, then defaults are not applied.

A typical use case is when you have an object schema that is required to have one field or another, but not both, (see https://stackoverflow.com/questions/24023536/json-schema-how-do-i-require-one-field-or-another-or-one-of-two-others-but-no). You can do this kind of thing with the following type of schema schema :

{
    "title": "test",
    "type": "object",
    "properties": {
      "obj1": {
        "type": "object",
        "properties": {
          "prop1": {
            "type": "string",
            "default": "val1"
          }
        }
      },
      "obj2": {
        "type": "object",
        "properties": {
          "prop2": {
            "type": "string",
            "default": "val2"
          }
        }
      }
    },
    "oneOf": [
      {
        "required": [
          "obj1"
        ]
      },
      {
        "required": [
          "obj2"
        ]
      }       
    ]
}

However, conflate does not apply the object property defaults in the case of json input data such as :

{
  "obj1": {} 
}

Conflate should output :

{
  "obj1": {
    "prop1": "val1"
  }
}

idea: allow for environment variable substitution in config files

We could support substitution of environment variables of the form ${MYVAR} within config file values. This would be optional and would require a new function in the conflate interface.

e.g. if MYVAR=myvalue in the environment, then conflate.ApplyEnvironment() would transform this :

{
  "x": "$MYVAR"
}

Would resolve to :

{
  "x": "myvalue"
}

Since it would also be useful to expand environment variables into non-string fields, the expansion should be performed on the raw data, not on the individual fields.

e.g. if MYVAR=123 then the following is not valid JSON :

{
  "x": $MYVAR
}

However it does expand into valid json

{
  "x": 123
}

In a similar way environment variables can be used to inject whole sections of data into the JSON file if required, although probably not recommended.

handling list as entity

First off, thanks for this package! In my use case, it works almost too well.
Say i have two sources, each with a list property:
source.A:

group_members:
  - alice

source.B:

group_members:
  - bob

what i end up with by default is a list ["alice", "bob"]. I'm wondering if there is any way to treat lists as a whole, such that if i merge A then B, that source.B list is exclusive (ie just bob)?

Support arbitrary marshallers/unmarshallers

We should add a list of marshallers to each Conflate instance. This should be editable by the client so it can edit the marshallers that should be used to parse the config... i.e. a client might not want to support TOML, or might want to support XML, or some other custom format...

apply basic indentation formatting to JSON output

Currently the MarshalJSON output generates JSON in an unformatted form. This makes it difficult to read without using a third-party formatter. It is preferable therefore to use some kind of default formatting.

support $ref in applyDefaults

We could do this by expanding the schema out to replace al the $ref with the appropriate concrete schemas. This would be the simplest implementation (as it would leave the applyDefaults code intact, but it would bloat the schema document.

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.