Skip to content

Commit

Permalink
Merge pull request #101 from CircleCI-Public/CIRCLE-13450-bugfix-orb-…
Browse files Browse the repository at this point in the history
…list-namespace

CIRCLE-13450 Fix "orb list namespace"
  • Loading branch information
Zachary Scott authored Sep 10, 2018
2 parents 50bc25c + e164858 commit f6dc8ad
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 98 deletions.
119 changes: 111 additions & 8 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,12 @@ type CreateOrbResponse struct {

// Orb is a struct for containing the yaml-unmarshaled contents of an orb
type Orb struct {
Commands map[string]struct{}
Jobs map[string]struct{}
Executors map[string]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{}
}

func addOrbElementsToBuffer(buf *bytes.Buffer, name string, elems map[string]struct{}) {
Expand All @@ -125,6 +128,11 @@ func addOrbElementsToBuffer(buf *bytes.Buffer, name string, elems map[string]str
func (orb Orb) String() string {
var buffer bytes.Buffer

_, err := buffer.WriteString(fmt.Sprintln(orb.Name, "("+orb.HighestVersion+")"))
if err != nil {
// The WriteString docstring says that it will never return an error
panic(err)
}
addOrbElementsToBuffer(&buffer, "Commands", orb.Commands)
addOrbElementsToBuffer(&buffer, "Jobs", orb.Jobs)
addOrbElementsToBuffer(&buffer, "Executors", orb.Executors)
Expand Down Expand Up @@ -730,9 +738,105 @@ func OrbSource(ctx context.Context, logger *logger.Logger, namespace string, orb
return response.Orb.Versions[0].Source, nil
}

// 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) ([]Orb, 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.
type orbList struct {
Orbs struct {
TotalCount int
Edges []struct {
Cursor string
Node struct {
Name string
Versions []struct {
Version string
Source string
}
}
}
PageInfo struct {
HasNextPage bool
}
}
}

query := `
query ListOrbs ($after: String!) {
orbs(first: 20, after: $after) {
totalCount,
edges {
cursor
node {
name
versions(count: 1) {
version,
source
}
}
}
pageInfo {
hasNextPage
}
}
}
`

var orbs []Orb

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

var result orbList
currentCursor := ""

for {
request := client.NewAuthorizedRequest(viper.GetString("token"), query)
request.Var("after", currentCursor)

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

Orbs:
for i := range result.Orbs.Edges {
edge := result.Orbs.Edges[i]
currentCursor = edge.Cursor
if len(edge.Node.Versions) > 0 {
v := edge.Node.Versions[0]

var o Orb

o.Name = edge.Node.Name
o.HighestVersion = v.Version
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)
}
}

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

// ListNamespaceOrbs queries the API to find all orbs belonging to the given
// namespace. Prints the orbs and their jobs and commands to the supplied
// logger.
// 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) {
// Define a structure that matches the result of the GQL
// query, so that we can use mapstructure to convert from
Expand Down Expand Up @@ -810,11 +914,10 @@ query namespaceOrbs ($namespace: String, $after: String!) {
if len(edge.Node.Versions) > 0 {
v := edge.Node.Versions[0]

// Print the orb name and first version returned by the API
logger.Infof("%s (%s)", edge.Node.Name, v.Version)

// Parse the orb source to print its commands, executors and jobs
var o Orb
o.Name = edge.Node.Name
o.HighestVersion = v.Version
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)
Expand Down
94 changes: 4 additions & 90 deletions cmd/orb.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ import (
"strings"

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

"github.com/circleci/graphql"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
)

var orbAnnotations = map[string]string{
Expand Down Expand Up @@ -149,95 +146,12 @@ func listOrbs(cmd *cobra.Command, args []string) error {
}

ctx := context.Background()

// 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 orbList struct {
Orbs struct {
TotalCount int
Edges []struct {
Cursor string
Node struct {
Name string
Versions []struct {
Version string
Source string
}
}
}
PageInfo struct {
HasNextPage bool
}
}
}

request := graphql.NewRequest(`
query ListOrbs ($after: String!) {
orbs(first: 20, after: $after) {
totalCount,
edges {
cursor
node {
name
versions(count: 1) {
version,
source
}
}
}
pageInfo {
hasNextPage
}
}
}
`)

address, err := api.GraphQLServerAddress(api.EnvEndpointHost())
orbs, err := api.ListOrbs(ctx, Logger)
if err != nil {
return err
return errors.Wrapf(err, "Failed to list orbs")
}
client := client.NewClient(address, Logger)

var result orbList
currentCursor := ""

for {
request.Var("after", currentCursor)
err := client.Run(ctx, request, &result)

if err != nil {
return errors.Wrap(err, "GraphQL query failed")
}

// Debug logging of result fields.
// Logger.Prettyify(result)
Orbs:
for i := range result.Orbs.Edges {
edge := result.Orbs.Edges[i]
currentCursor = edge.Cursor
if len(edge.Node.Versions) > 0 {
v := edge.Node.Versions[0]

Logger.Infof("%s (%s)", edge.Node.Name, v.Version)

var o api.Orb

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
}

Logger.Info(o.String())

}
}

if !result.Orbs.PageInfo.HasNextPage {
break
}
for _, o := range orbs {
Logger.Info(o.String())
}
return nil
}
Expand Down
5 changes: 5 additions & 0 deletions cmd/orb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1139,7 +1139,12 @@ var _ = Describe("Orb integration tests", func() {
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expect(err).ShouldNot(HaveOccurred())
Eventually(session.Out).Should(gbytes.Say("circleci/gradle"))
Eventually(session.Out).Should(gbytes.Say("Jobs"))
Eventually(session.Out).Should(gbytes.Say("- test"))
Eventually(session.Out).Should(gbytes.Say("circleci/rollbar"))
Eventually(session.Out).Should(gbytes.Say("Commands"))
Eventually(session.Out).Should(gbytes.Say("- notify_deploy"))
Eventually(session).Should(gexec.Exit(0))
Expect(testServer.ReceivedRequests()).Should(HaveLen(2))
})
Expand Down

0 comments on commit f6dc8ad

Please sign in to comment.