Skip to content

Commit

Permalink
Refactor common code between orb and config commands
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomorain committed Jul 2, 2018
1 parent 916757f commit b558ce3
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 177 deletions.
103 changes: 103 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package api

import (
"context"
"io/ioutil"
"strings"

"github.com/CircleCI-Public/circleci-cli/client"
"github.com/CircleCI-Public/circleci-cli/logger"
"github.com/pkg/errors"
"github.com/spf13/viper"
)

// ConfigResponse is a structure that matches the result of the GQL
// query, so that we can use mapstructure to convert from
// nested maps to a strongly typed struct.
type ConfigResponse struct {
Valid bool
SourceYaml string
OutputYaml string

Errors []struct {
Message string
}
}

// ToError returns an error created from any error messages, or nil.
func (response ConfigResponse) ToError() error {
messages := []string{}

for i := range response.Errors {
messages = append(messages, response.Errors[i].Message)
}

return errors.New(strings.Join(messages, ": "))
}

func loadYaml(path string) (string, error) {

config, err := ioutil.ReadFile(path)

if err != nil {
return "", errors.Wrapf(err, "Could not load config file at %s", path)
}

return string(config), nil
}

func buildAndOrbQuery(ctx context.Context, logger *logger.Logger, configPath string, response interface{}, query string) error {
config, err := loadYaml(configPath)
if err != nil {
return err
}

request := client.NewAuthorizedRequest(viper.GetString("token"), query)
request.Var("config", config)
client := client.NewClient(viper.GetString("endpoint"), logger)

err = client.Run(ctx, request, response)

if err != nil {
return errors.Wrap(err, "Unable to validate config")
}

return nil
}

// ConfigQuery calls the GQL API to validate and expand config
func ConfigQuery(ctx context.Context, logger *logger.Logger, configPath string) (*ConfigResponse, error) {
var response struct {
BuildConfig struct {
ConfigResponse
}
}
return &response.BuildConfig.ConfigResponse, buildAndOrbQuery(ctx, logger, configPath, &response, `
query ValidateConfig ($config: String!) {
buildConfig(configYaml: $config) {
valid,
errors { message },
sourceYaml,
outputYaml
}
}`)
}

// OrbQuery validated and expands an orb.
func OrbQuery(ctx context.Context, logger *logger.Logger, configPath string) (*ConfigResponse, error) {
var response struct {
OrbConfig struct {
ConfigResponse
}
}

return &response.OrbConfig.ConfigResponse, buildAndOrbQuery(ctx, logger, configPath, &response, `
query ValidateOrb ($config: String!) {
orbConfig(orbYaml: $config) {
valid,
errors { message },
sourceYaml,
outputYaml
}
}`)
}
104 changes: 8 additions & 96 deletions cmd/config.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package cmd

import (
"bytes"
"context"
"io/ioutil"

"github.com/CircleCI-Public/circleci-cli/client"
"github.com/pkg/errors"

"github.com/machinebox/graphql"
"github.com/CircleCI-Public/circleci-cli/api"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// Path to the config.yml file to operate on.
Expand Down Expand Up @@ -40,99 +34,17 @@ func init() {
configCmd.AddCommand(expandCommand)
}

// Define a structure that matches the result of the GQL
// query, so that we can use mapstructure to convert from
// nested maps to a strongly typed struct.
type buildConfigResponse struct {
BuildConfig struct {
Valid bool
SourceYaml string
OutputYaml string

Errors []struct {
Message string
}
}
}

func queryAPI(ctx context.Context, query string, variables map[string]string, response interface{}) error {

request := client.NewAuthorizedRequest(viper.GetString("token"), query)

for varName, varValue := range variables {
request.Var(varName, varValue)
}

client := graphql.NewClient(viper.GetString("endpoint"))

return client.Run(ctx, request, response)
}

func loadYaml(path string) (string, error) {

config, err := ioutil.ReadFile(path)

if err != nil {
return "", errors.Wrapf(err, "Could not load config file at %s", path)
}

return string(config), nil
}

func (response buildConfigResponse) processErrors() error {
var buffer bytes.Buffer

buffer.WriteString("\n")
for i := range response.BuildConfig.Errors {
buffer.WriteString("-- ")
buffer.WriteString(response.BuildConfig.Errors[i].Message)
buffer.WriteString(",\n")
}

return errors.New(buffer.String())
}

func configQuery(ctx context.Context) (*buildConfigResponse, error) {

query := `
query ValidateConfig ($config: String!) {
buildConfig(configYaml: $config) {
valid,
errors { message },
sourceYaml,
outputYaml
}
}`

config, err := loadYaml(configPath)
if err != nil {
return nil, err
}

variables := map[string]string{
"config": config,
}

var response buildConfigResponse
err = queryAPI(ctx, query, variables, &response)
if err != nil {
return nil, errors.Wrap(err, "Unable to validate config")
}

return &response, nil
}

func validateConfig(cmd *cobra.Command, args []string) error {

ctx := context.Background()
response, err := configQuery(ctx)
response, err := api.ConfigQuery(ctx, Logger, configPath)

if err != nil {
return err
}

if !response.BuildConfig.Valid {
return response.processErrors()
if !response.Valid {
return response.ToError()
}

Logger.Infof("Config file at %s is valid", configPath)
Expand All @@ -142,16 +54,16 @@ func validateConfig(cmd *cobra.Command, args []string) error {
func expandConfig(cmd *cobra.Command, args []string) error {
ctx := context.Background()

response, err := configQuery(ctx)
response, err := api.ConfigQuery(ctx, Logger, configPath)

if err != nil {
return err
}

if !response.BuildConfig.Valid {
return response.processErrors()
if !response.Valid {
return response.ToError()
}

Logger.Info(response.BuildConfig.OutputYaml)
Logger.Info(response.OutputYaml)
return nil
}
6 changes: 2 additions & 4 deletions cmd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ var _ = Describe("Config", func() {

Expect(err).ShouldNot(HaveOccurred())
Eventually(session.Err).Should(gbytes.Say("Error:"))
Eventually(session.Err).Should(gbytes.Say("-- invalid_config"))
Eventually(session.Err).Should(gbytes.Say("invalid_config"))
Eventually(session).ShouldNot(gexec.Exit(0))
})
})
Expand Down Expand Up @@ -179,9 +179,7 @@ var _ = Describe("Config", func() {
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expect(err).ShouldNot(HaveOccurred())
Eventually(session.Err).Should(gbytes.Say("Error:"))
Eventually(session.Err).Should(gbytes.Say("-- error1,"))
Eventually(session.Err).Should(gbytes.Say("-- error2,"))
Eventually(session.Err).Should(gbytes.Say("Error: error1: error2"))
Eventually(session).ShouldNot(gexec.Exit(0))
})
})
Expand Down
72 changes: 8 additions & 64 deletions cmd/orb.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package cmd

import (
"bytes"
"context"
"io/ioutil"

"github.com/CircleCI-Public/circleci-cli/api"
"github.com/CircleCI-Public/circleci-cli/client"
"github.com/pkg/errors"

Expand Down Expand Up @@ -129,73 +128,18 @@ query ListOrbs ($after: String!) {
}
}
return nil

}

func loadOrbYaml(path string) (string, error) {

orb, err := ioutil.ReadFile(path)

if err != nil {
return "", errors.Wrapf(err, "Could not load orb file at %s", path)
}

return string(orb), nil
}

func (response orbConfigResponse) processErrors() error {
var buffer bytes.Buffer

buffer.WriteString("\n")
for i := range response.OrbConfig.Errors {
buffer.WriteString("-- ")
buffer.WriteString(response.OrbConfig.Errors[i].Message)
buffer.WriteString(",\n")
}

return errors.New(buffer.String())
}

func orbValidateQuery(ctx context.Context) (*orbConfigResponse, error) {

query := `
query ValidateOrb ($orb: String!) {
orbConfig(orbYaml: $orb) {
valid,
errors { message },
sourceYaml,
outputYaml
}
}`

orb, err := loadOrbYaml(orbPath)
if err != nil {
return nil, err
}

variables := map[string]string{
"orb": orb,
}

var response orbConfigResponse
err = queryAPI(ctx, query, variables, &response)
if err != nil {
return nil, errors.Wrap(err, "Unable to validate orb")
}

return &response, nil
}

func validateOrb(cmd *cobra.Command, args []string) error {
ctx := context.Background()
response, err := orbValidateQuery(ctx)
response, err := api.OrbQuery(ctx, Logger, orbPath)

if err != nil {
return err
}

if !response.OrbConfig.Valid {
return response.processErrors()
if !response.Valid {
return response.ToError()
}

Logger.Infof("Orb at %s is valid", orbPath)
Expand All @@ -205,16 +149,16 @@ func validateOrb(cmd *cobra.Command, args []string) error {
func expandOrb(cmd *cobra.Command, args []string) error {
ctx := context.Background()

response, err := orbValidateQuery(ctx)
response, err := api.OrbQuery(ctx, Logger, orbPath)

if err != nil {
return err
}

if !response.OrbConfig.Valid {
return response.processErrors()
if !response.Valid {
return response.ToError()
}

Logger.Info(response.OrbConfig.OutputYaml)
Logger.Info(response.OutputYaml)
return nil
}
Loading

0 comments on commit b558ce3

Please sign in to comment.