Skip to content

Commit

Permalink
Merge pull request #114 from CircleCI-Public/CIRCLE-13454-machine-rea…
Browse files Browse the repository at this point in the history
…dable-output

Circle 13454 machine readable output
  • Loading branch information
Zachary Scott authored Sep 21, 2018
2 parents b82e865 + ac530e9 commit ae5bb22
Show file tree
Hide file tree
Showing 11 changed files with 727 additions and 93 deletions.
71 changes: 54 additions & 17 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,39 @@ type CreateOrbResponse struct {
GQLResponseErrors
}

// OrbCollection is a container type for multiple orbs to share formatting
// functions on them.
type OrbCollection struct {
Orbs []Orb `json:"orbs"`
Namespace string `json:"namespace,omitempty"`
}

// String returns a text representation of all Orbs, intended for
// direct human use rather than machine use.
func (orbCollection OrbCollection) String() string {
var result string
for _, o := range orbCollection.Orbs {
result += (o.String())
}
return result
}

// OrbVersion represents a single orb version and its source
type OrbVersion struct {
Version string `json:"version"`
Source string `json:"source"`
}

// Orb is a struct for containing the yaml-unmarshaled contents of an orb
type Orb struct {
Name string
// Avoid "Version" since this is a key in the orb source referring to the orb schema version
HighestVersion string
Commands map[string]struct{}
Jobs map[string]struct{}
Executors map[string]struct{}
Name string `json:"name"`
// Avoid "Version" since there is a "version" key in the orb source referring
// to the orb schema version
HighestVersion string `json:"-"`
Commands map[string]struct{} `json:"-"`
Jobs map[string]struct{} `json:"-"`
Executors map[string]struct{} `json:"-"`
Versions []OrbVersion `json:"versions"`
}

func addOrbElementsToBuffer(buf *bytes.Buffer, name string, elems map[string]struct{}) {
Expand All @@ -128,12 +153,16 @@ func addOrbElementsToBuffer(buf *bytes.Buffer, name string, elems map[string]str
_, err = buf.WriteString(fmt.Sprintf(" - %s\n", key))
}
}
// This should never occur, but the linter made me do it :shrug:
// This will never occur. The docs for bytes.Buffer.WriteString says err
// will always be nil. The linter still expects this error to be checked.
if err != nil {
panic(err)
}
}

// String returns a text representation of the Orb contents, intended for
// direct human use rather than machine use. This function will exclude orb
// source and orbs without any versions in its returned string.
func (orb Orb) String() string {
var buffer bytes.Buffer

Expand Down Expand Up @@ -766,7 +795,7 @@ func OrbSource(ctx context.Context, logger *logger.Logger, namespace string, orb
// ListOrbs queries the API to find all orbs.
// Returns a collection of Orb objects containing their relevant data. Logs
// request and parse errors to the supplied logger.
func ListOrbs(ctx context.Context, logger *logger.Logger, uncertified bool) ([]Orb, error) {
func ListOrbs(ctx context.Context, logger *logger.Logger, uncertified bool) (*OrbCollection, error) {
// 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.
Expand Down Expand Up @@ -810,7 +839,7 @@ query ListOrbs ($after: String!, $certifiedOnly: Boolean!) {
}
`

var orbs []Orb
var orbs OrbCollection

address, err := GraphQLServerAddress(EnvEndpointHost())
if err != nil {
Expand Down Expand Up @@ -842,28 +871,32 @@ query ListOrbs ($after: String!, $certifiedOnly: Boolean!) {

o.Name = edge.Node.Name
o.HighestVersion = v.Version

for _, v := range edge.Node.Versions {
o.Versions = append(o.Versions, OrbVersion(v))
}
err := yaml.Unmarshal([]byte(edge.Node.Versions[0].Source), &o)

if err != nil {
logger.Error(fmt.Sprintf("Corrupt Orb %s %s", edge.Node.Name, v.Version), err)
continue Orbs
}
orbs = append(orbs, o)
orbs.Orbs = append(orbs.Orbs, o)
}
}

if !result.Orbs.PageInfo.HasNextPage {
break
}
}
return orbs, nil
return &orbs, nil
}

// ListNamespaceOrbs queries the API to find all orbs belonging to the given
// namespace.
// Returns a collection of Orb objects containing their relevant data. Logs
// request and parse errors to the supplied logger.
func ListNamespaceOrbs(ctx context.Context, logger *logger.Logger, namespace string) ([]Orb, error) {
func ListNamespaceOrbs(ctx context.Context, logger *logger.Logger, namespace string) (*OrbCollection, error) {
// 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.
Expand Down Expand Up @@ -912,11 +945,11 @@ query namespaceOrbs ($namespace: String, $after: String!) {
}
}
`
var orbs []Orb
var orbs OrbCollection

address, err := GraphQLServerAddress(EnvEndpointHost())
if err != nil {
return orbs, err
return nil, err
}
graphQLclient := client.NewClient(address, logger)

Expand All @@ -927,10 +960,11 @@ query namespaceOrbs ($namespace: String, $after: String!) {
request := client.NewAuthorizedRequest(viper.GetString("token"), query)
request.Var("after", currentCursor)
request.Var("namespace", namespace)
orbs.Namespace = namespace

err := graphQLclient.Run(ctx, request, &result)
if err != nil {
return orbs, errors.Wrap(err, "GraphQL query failed")
return nil, errors.Wrap(err, "GraphQL query failed")
}

NamespaceOrbs:
Expand All @@ -944,12 +978,15 @@ query namespaceOrbs ($namespace: String, $after: String!) {
var o Orb
o.Name = edge.Node.Name
o.HighestVersion = v.Version
for _, v := range edge.Node.Versions {
o.Versions = append(o.Versions, OrbVersion(v))
}
err := yaml.Unmarshal([]byte(edge.Node.Versions[0].Source), &o)
if err != nil {
logger.Error(fmt.Sprintf("Corrupt Orb %s %s", edge.Node.Name, v.Version), err)
continue NamespaceOrbs
}
orbs = append(orbs, o)
orbs.Orbs = append(orbs.Orbs, o)
}
}

Expand All @@ -958,7 +995,7 @@ query namespaceOrbs ($namespace: String, $after: String!) {
}
}

return orbs, nil
return &orbs, nil
}

// IntrospectionQuery makes a query on the API asking for bits of the schema
Expand Down
27 changes: 23 additions & 4 deletions cmd/orb.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"context"
"encoding/json"
"fmt"
"strings"

Expand All @@ -18,6 +19,7 @@ var orbAnnotations = map[string]string{
}

var orbListUncertified bool
var orbListJSON bool

func newOrbCommand() *cobra.Command {

Expand All @@ -30,6 +32,10 @@ func newOrbCommand() *cobra.Command {
}
listCommand.Annotations["NAMESPACE"] = orbAnnotations["NAMESPACE"] + " (Optional)"
listCommand.PersistentFlags().BoolVarP(&orbListUncertified, "uncertified", "u", false, "include uncertified orbs")
listCommand.PersistentFlags().BoolVar(&orbListJSON, "json", false, "print output as json instead of human-readable")
if err := listCommand.PersistentFlags().MarkHidden("json"); err != nil {
panic(err)
}

validateCommand := &cobra.Command{
Use: "validate PATH",
Expand Down Expand Up @@ -153,8 +159,15 @@ func listOrbs(cmd *cobra.Command, args []string) error {
if err != nil {
return errors.Wrapf(err, "Failed to list orbs")
}
for _, o := range orbs {
Logger.Info(o.String())
if orbListJSON {
orbJSON, err := json.MarshalIndent(orbs, "", " ")
if err != nil {
return errors.Wrapf(err, "Failed to convert to convert to JSON")
}
Logger.Info(string(orbJSON))

} else {
Logger.Info(orbs.String())
}
return nil
}
Expand All @@ -165,8 +178,14 @@ func listNamespaceOrbs(namespace string) error {
if err != nil {
return errors.Wrapf(err, "Failed to list orbs in namespace %s", namespace)
}
for _, o := range orbs {
Logger.Info(o.String())
if orbListJSON {
orbJSON, err := json.MarshalIndent(orbs, "", " ")
if err != nil {
return errors.Wrapf(err, "Failed to convert to convert to JSON")
}
Logger.Info(string(orbJSON))
} else {
Logger.Info(orbs.String())
}
return nil
}
Expand Down
Loading

0 comments on commit ae5bb22

Please sign in to comment.