Skip to content

Commit

Permalink
adding new flag '-t --templates' (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
gulien authored Jun 17, 2018
1 parent eddae54 commit 7314053
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 58 deletions.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,45 @@ running `orbit generate [...] -p "my_key,my_file.yml;my_other_key,Some raw data"
**Note:** you are able to override a data source from the file `orbit-payload.yml` if
you set the same key in the `-p` flag.

##### `-t --templates`

The flag `-t` allows you to specify additional templates which are used in your template:

```
orbit generate [...] -t "template_1.txt"
orbit generate [...] -t "template_1.txt,template_2.yml"
orbit generate [...] -t "template_1.txt,template_2.yml,../../template_3.toml"
```

So, in order to generate a file from this template:

```
{{ template "additional_template.txt" }}
```

You should run:

```
orbit generate [...] -t "path/to/additional_template.txt"
```

If you don't want to specify the templates each time your running `orbit generate`,
you may also use the file `orbit-payload.yml` in the folder where your running your command:

```yaml
payload:
[...]
templates:
- template_1.txt
- template_2.yml
```

By doing so, running `orbit generate [...]` will be equivalent to
running `orbit generate [...] -t "template_1.txt,template_2.yml"`.

##### `-v --verbose`

Sets logging to info level.
Expand Down Expand Up @@ -326,6 +365,14 @@ It works the same as the `-p` flag from the `generate` command.

Of course, you may also create a file named `orbit-payload.yml` in the same folder where you're executing Orbit.

##### `-t --templates`

The flag `-t` allows you to specify additional templates which are used in your configuration file.

It works the same as the `-t` flag from the `generate` command.

Of course, you may also create a file named `orbit-payload.yml` in the same folder where you're executing Orbit.

##### `-v --verbose`

Sets logging to info level.
Expand Down
1 change: 1 addition & 0 deletions _tests/template-blue-origin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Launchers: New Shepard, New Glenn
1 change: 1 addition & 0 deletions _tests/template-spacex.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Launchers: Falcon 9, Falcon Heavy
3 changes: 3 additions & 0 deletions _tests/template-with-additional-templates.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{ template "template-spacex.txt" }}

{{ template "template-blue-origin.txt" }}
15 changes: 10 additions & 5 deletions app/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ type OrbitContext struct {

// Payload map contains data from various entries.
Payload map[string]interface{}

// Templates array contains the list of additional templates to parse.
Templates []string
}

// NewOrbitContext creates an instance of OrbitContext.
func NewOrbitContext(templateFilePath string, payload string) (*OrbitContext, error) {
func NewOrbitContext(templateFilePath string, payload string, templates string) (*OrbitContext, error) {
// as the data-driven template is mandatory, we must check its validity.
if templateFilePath == "" {
return nil, OrbitError.NewOrbitErrorf("no data-driven template given")
Expand All @@ -46,18 +49,20 @@ func NewOrbitContext(templateFilePath string, payload string) (*OrbitContext, er
return nil, err
}

if err := p.populateFromString(payload); err != nil {
if err := p.populateFromString(payload, templates); err != nil {
return nil, err
}

data, err := p.retrieveData()
payloadData, err := p.retrievePayloadData()
if err != nil {
return nil, err
}

ctx.Payload = data

ctx.Payload = payloadData
logger.Debugf("context has been populated with payload %s", ctx.Payload)

ctx.Templates = p.TemplatesEntries
logger.Debugf("context has been populated with templates %s", ctx.Templates)

return ctx, nil
}
14 changes: 7 additions & 7 deletions app/context/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,40 @@ import (
// with wrong parameters or no error if the parameters are OK.
func TestNewOrbitContext(t *testing.T) {
// case 1: uses an empty template file path.
if _, err := NewOrbitContext("", ""); err == nil {
if _, err := NewOrbitContext("", "", ""); err == nil {
t.Error("OrbitContext should not have been instantiated!")
}

// case 2: uses a non existing template file path.
if _, err := NewOrbitContext("non_existing_file", ""); err == nil {
if _, err := NewOrbitContext("non_existing_file", "", ""); err == nil {
t.Error("OrbitContext should not have been instantiated!")
}

// case 3: uses an existing template file path.
templateFilePath, _ := filepath.Abs("../../_tests/template.yml")
if _, err := NewOrbitContext(templateFilePath, ""); err != nil {
if _, err := NewOrbitContext(templateFilePath, "", ""); err != nil {
t.Error("OrbitContext should have been instantiated!")
}

// case 4: uses a broken payload string.
if _, err := NewOrbitContext(templateFilePath, "key"); err == nil {
if _, err := NewOrbitContext(templateFilePath, "key", ""); err == nil {
t.Error("OrbitContext should not have been instantiated!")
}

// case 5: uses a correct payload string.
if _, err := NewOrbitContext(templateFilePath, "key,value"); err != nil {
if _, err := NewOrbitContext(templateFilePath, "key,value", ""); err != nil {
t.Error("OrbitContext should have been instantiated!")
}

// case 6: uses a broken payload entry.
brokenPayloadEntryFilePath, _ := filepath.Abs("../../_tests/broken-data-source.yml")
if _, err := NewOrbitContext(templateFilePath, "key,"+brokenPayloadEntryFilePath); err == nil {
if _, err := NewOrbitContext(templateFilePath, "key,"+brokenPayloadEntryFilePath, ""); err == nil {
t.Error("OrbitContext should not have been instantiated!")
}

// case 7: uses a correct payload entry.
payloadEntryFilePath, _ := filepath.Abs("../../_tests/data-source.yml")
if _, err := NewOrbitContext(templateFilePath, "key,"+payloadEntryFilePath); err != nil {
if _, err := NewOrbitContext(templateFilePath, "key,"+payloadEntryFilePath, ""); err != nil {
t.Error("OrbitContext should have been instantiated!")
}
}
50 changes: 32 additions & 18 deletions app/context/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ type (
// orbitPayload contains the various entries provided by the user.
orbitPayload struct {
// PayloadEntries is a simple array of orbitPayloadEntry.
PayloadEntries []*orbitPayloadEntry `yaml:"payload"`
PayloadEntries []*orbitPayloadEntry `yaml:"payload,omitempty"`

// TemplatesEntries is a simple array of string.
TemplatesEntries []string `yaml:"templates,omitempty"`
}

// orbitPayloadEntry is an entry from a file or from a string.
orbitPayloadEntry struct {
// Key is the unique identifier of a value
// Key is the unique identifier of a value.
Key string `yaml:"key"`

// Value is a raw data or a file path.
Expand Down Expand Up @@ -61,34 +64,45 @@ func (p *orbitPayload) populateFromFile(filePath string) error {

// populateFromString populates the instance of orbitPayload
// with entries provided by a string.
func (p *orbitPayload) populateFromString(payload string) error {
func (p *orbitPayload) populateFromString(payload string, templates string) error {
// first, checks if a payload has been given.
if payload == "" {
logger.Debugf("no payload flag given, skipping")
if payload == "" && templates == "" {
logger.Debugf("no payload and templates flags given, skipping")
return nil
}

// the payload string should be in the following format:
// key,value;key,value.
entries := strings.Split(payload, ";")
for _, entry := range entries {
entry := strings.Split(entry, ",")
if len(entry) == 1 {
return OrbitError.NewOrbitErrorf("unable to process the payload entry %s", entry)
if payload != "" {
// the payload string should be in the following format:
// key,value;key,value.
entries := strings.Split(payload, ";")
for _, entry := range entries {
entry := strings.Split(entry, ",")
if len(entry) == 1 {
return OrbitError.NewOrbitErrorf("unable to process the payload entry %s", entry)
}

p.PayloadEntries = append(p.PayloadEntries, &orbitPayloadEntry{
Key: entry[0],
Value: strings.Join(entry[1:], ","),
})
}
}

p.PayloadEntries = append(p.PayloadEntries, &orbitPayloadEntry{
Key: entry[0],
Value: strings.Join(entry[1:], ","),
})
if templates != "" {
// the templates string should be in the following format:
// path,path,path.
entries := strings.Split(templates, ",")
for _, entry := range entries {
p.TemplatesEntries = append(p.TemplatesEntries, entry)
}
}

return nil
}

// retrieveData parses all the entries from the instance of orbitPayload
// retrievePayloadData parses all the payload entries from the instance of orbitPayload
// to retrieve the data which will be applied to a data-driven template.
func (p *orbitPayload) retrieveData() (map[string]interface{}, error) {
func (p *orbitPayload) retrievePayloadData() (map[string]interface{}, error) {
result := make(map[string]interface{})

for _, payloadEntry := range p.PayloadEntries {
Expand Down
20 changes: 10 additions & 10 deletions app/context/payload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,41 @@ func TestPopulateFromFile(t *testing.T) {
// Tests if populating an orbitPayload from a string throws an error
// with a wrong parameter or no error if the parameter is correct.
func TestPopulateFromString(t *testing.T) {
// case 1: uses an empty payload string.
// case 1: uses empty payload and templates strings.
p := &orbitPayload{}
if err := p.populateFromString(""); err != nil {
if err := p.populateFromString("", ""); err != nil {
t.Error("orbitPayload should have been skipped!")
}

// case 2: uses a broken payload string.
p = &orbitPayload{}
if err := p.populateFromString("key"); err == nil {
if err := p.populateFromString("key", ""); err == nil {
t.Error("orbitPayload should not have been populated!")
}

// case 3: uses a correct payload string.
// case 3: uses correct payload and templates strings.
p = &orbitPayload{}
if err := p.populateFromString("key,value"); err != nil {
if err := p.populateFromString("key,value", "path,path"); err != nil {
t.Error("orbitPayload should have been populated!")
}
}

// Tests if retrieving data from an orbitPayload throws an error
// with a wrong payload entry or no error if the payload entry is correct.
func TestRetrieveData(t *testing.T) {
func TestRetrievePayloadData(t *testing.T) {
// case 1: uses a broken payload entry.
brokenDataSourceFilePath, _ := filepath.Abs("../../_tests/broken-data-source.yml")
p := &orbitPayload{}
p.populateFromString("key," + brokenDataSourceFilePath)
if _, err := p.retrieveData(); err == nil {
p.populateFromString("key,"+brokenDataSourceFilePath, "")
if _, err := p.retrievePayloadData(); err == nil {
t.Error("orbitPayload should not have been hable to retrieve data!")
}

// case 2: uses a correct payload entry.
dataSourceFilePath, _ := filepath.Abs("../../_tests/data-source.yml")
p = &orbitPayload{}
p.populateFromString("key," + dataSourceFilePath)
if _, err := p.retrieveData(); err != nil {
p.populateFromString("key,"+dataSourceFilePath, "")
if _, err := p.retrievePayloadData(); err != nil {
t.Error("orbitPayload should have been hable to retrieve data!")
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ If no output file is given, prints the result to Stdout.
*/
func generate(cmd *cobra.Command, args []string) error {
// first, let's instantiate our Orbit context.
ctx, err := context.NewOrbitContext(templateFilePath, payload)
ctx, err := context.NewOrbitContext(templateFilePath, payload, templates)
if err != nil {
return err
}
Expand Down
11 changes: 8 additions & 3 deletions app/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,14 @@ Execute executes a data-driven template by applying it the data structure provid
Returns the resulting bytes.
*/
func (g *OrbitGenerator) Execute() (bytes.Buffer, error) {
var data bytes.Buffer

tmpl, err := template.New(filepath.Base(g.context.TemplateFilePath)).Funcs(g.funcMap).ParseFiles(g.context.TemplateFilePath)
var (
files []string
data bytes.Buffer
)

files = append(files, g.context.TemplateFilePath)
files = append(files, g.context.Templates...)
tmpl, err := template.New(filepath.Base(g.context.TemplateFilePath)).Funcs(g.funcMap).ParseFiles(files...)
if err != nil {
return data, OrbitError.NewOrbitErrorf("unable to parse the template file %s. Details:\n%s", g.context.TemplateFilePath, err)
}
Expand Down
Loading

0 comments on commit 7314053

Please sign in to comment.