-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(merge): add a merge command (#13)
the cli-command "merge" merges multiple decK files into one.
- Loading branch information
Showing
16 changed files
with
579 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Launch Package", | ||
"type": "go", | ||
"request": "launch", | ||
"mode": "auto", | ||
"program": "main.go", | ||
"args": ["merge", | ||
"merge/merge_testfiles/file1.yml", | ||
"merge/merge_testfiles/file2.yml", | ||
"merge/merge_testfiles/file3.yml" | ||
] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
Copyright © 2023 NAME HERE <EMAIL ADDRESS> | ||
*/ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/kong/go-apiops/filebasics" | ||
"github.com/kong/go-apiops/merge" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// Executes the CLI command "openapi2kong" | ||
func executeMerge(cmd *cobra.Command, args []string) { | ||
outputFilename, err := cmd.Flags().GetString("output-file") | ||
if err != nil { | ||
log.Fatalf(fmt.Sprintf("failed getting cli argument 'output-file'; %%w"), err) | ||
} | ||
|
||
var asYaml bool | ||
{ | ||
outputFormat, err := cmd.Flags().GetString("format") | ||
if err != nil { | ||
log.Fatalf(fmt.Sprintf("failed getting cli argument 'format'; %%w"), err) | ||
} | ||
if outputFormat == "yaml" { | ||
asYaml = true | ||
} else if outputFormat == "json" { | ||
asYaml = false | ||
} else { | ||
log.Fatalf("expected '--format' to be either 'yaml' or 'json', got: '%s'", outputFormat) | ||
} | ||
} | ||
|
||
// do the work: read/merge | ||
filebasics.MustWriteSerializedFile(outputFilename, merge.MustFiles(args), asYaml) | ||
} | ||
|
||
// | ||
// | ||
// Define the CLI data for the openapi2kong command | ||
// | ||
// | ||
|
||
var mergeCmd = &cobra.Command{ | ||
Use: "merge [flags] filename [...filename]", | ||
Short: "Merges multiple decK files into one", | ||
Long: `Merges multiple decK files into one. | ||
The files can be either json or yaml format. Will merge all top-level arrays by simply | ||
concatenating them. Any other keys will be copied. The files will be processed in the order | ||
provided. No checks on content will be done, eg. duplicates, nor any validations. | ||
If the input files are not compatible an error will be returned. Compatibility is | ||
determined by the '_transform' and '_format_version' fields.`, | ||
Run: executeMerge, | ||
Args: cobra.MinimumNArgs(1), | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(mergeCmd) | ||
mergeCmd.Flags().StringP("output-file", "o", "-", "output file to write. Use - to write to stdout") | ||
mergeCmd.Flags().StringP("format", "", "yaml", "output format: json or yaml") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package deckformat | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/kong/go-apiops/jsonbasics" | ||
) | ||
|
||
const ( | ||
VersionKey = "_format_version" | ||
TransformKey = "_transform" | ||
) | ||
|
||
// CompatibleTransform checks if 2 files are compatible, by '_transform' keys. | ||
// Returns nil if compatible, and error otherwise. | ||
func CompatibleTransform(data1 map[string]interface{}, data2 map[string]interface{}) error { | ||
if data1 == nil { | ||
panic("expected 'data1' to be non-nil") | ||
} | ||
if data2 == nil { | ||
panic("expected 'data2' to be non-nil") | ||
} | ||
|
||
transform1 := true // this is the default value | ||
if data1[TransformKey] != nil { | ||
var err error | ||
if transform1, err = jsonbasics.GetBoolField(data1, TransformKey); err != nil { | ||
return err | ||
} | ||
} | ||
transform2 := true // this is the default value | ||
if data2[TransformKey] != nil { | ||
var err error | ||
if transform2, err = jsonbasics.GetBoolField(data2, TransformKey); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if transform1 != transform2 { | ||
return errors.New("files with '_transform: true' (default) and '_transform: false' are not compatible") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// CompatibleVersion checks if 2 files are compatible, by '_format_version'. Version is compatible | ||
// if they are the same major. Missing versions are assumed to be compatible. | ||
// Returns nil if compatible, and error otherwise. | ||
func CompatibleVersion(data1 map[string]interface{}, data2 map[string]interface{}) error { | ||
if data1 == nil { | ||
panic("expected 'data1' to be non-nil") | ||
} | ||
if data2 == nil { | ||
panic("expected 'data2' to be non-nil") | ||
} | ||
|
||
if data1[VersionKey] == nil { | ||
if data2[VersionKey] == nil { | ||
return nil // neither given , so assume compatible | ||
} | ||
// data1 omitted, just validate data2 has a proper version, any version will do | ||
_, _, err := ParseFormatVersion(data2) | ||
return err | ||
} | ||
|
||
// data1 has a version | ||
if data2[VersionKey] == nil { | ||
// data2 omitted, just validate data1 has a proper version, any version will do | ||
_, _, err := ParseFormatVersion(data1) | ||
return err | ||
} | ||
|
||
// both versions given, go parse them | ||
major1, minor1, err1 := ParseFormatVersion(data1) | ||
if err1 != nil { | ||
return err1 | ||
} | ||
major2, minor2, err2 := ParseFormatVersion(data2) | ||
if err2 != nil { | ||
return err2 | ||
} | ||
|
||
if major1 != major2 { | ||
return fmt.Errorf("major versions are incompatible; %d.%d and %d.%d", major1, minor1, major2, minor2) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// CompatibleFile returns nil if the files are compatible. An error otherwise. | ||
// see CompatibleVersion and CompatibleTransform for what compatibility means. | ||
func CompatibleFile(data1 map[string]interface{}, data2 map[string]interface{}) error { | ||
err := CompatibleTransform(data1, data2) | ||
if err != nil { | ||
return fmt.Errorf("files are incompatible; %w", err) | ||
} | ||
err = CompatibleVersion(data1, data2) | ||
if err != nil { | ||
return fmt.Errorf("files are incompatible; %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
// parseFormatVersion parses field `_format_version` and returns major+minor. | ||
// Field must be present, a string, and have an 'x.y' format. Returns an error otherwise. | ||
func ParseFormatVersion(data map[string]interface{}) (int, int, error) { | ||
// get the file version and check it | ||
v, err := jsonbasics.GetStringField(data, VersionKey) | ||
if err != nil { | ||
return 0, 0, errors.New("expected field '._format_version' to be a string in 'x.y' format") | ||
} | ||
elem := strings.Split(v, ".") | ||
if len(elem) > 2 { | ||
return 0, 0, errors.New("expected field '._format_version' to be a string in 'x.y' format") | ||
} | ||
|
||
majorVersion, err := strconv.Atoi(elem[0]) | ||
if err != nil { | ||
return 0, 0, errors.New("expected field '._format_version' to be a string in 'x.y' format") | ||
} | ||
|
||
minorVersion := 0 | ||
if len(elem) > 1 { | ||
minorVersion, err = strconv.Atoi(elem[1]) | ||
if err != nil { | ||
return 0, 0, errors.New("expected field '._format_version' to be a string in 'x.y' format") | ||
} | ||
} | ||
|
||
return majorVersion, minorVersion, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.