Skip to content

Commit

Permalink
Add command for create namespace
Browse files Browse the repository at this point in the history
Takes a positional 'name' argument and two flags:
'org-name' and (org)'vcs'

Modify appendPostHandler to accept a struct of information
  • Loading branch information
hannahhenderson committed Jul 24, 2018
1 parent 6d28439 commit a9d0184
Show file tree
Hide file tree
Showing 6 changed files with 339 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .gometalinter.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"Enable": [
"deadcode",
"errcheck",
"gas",
"gas", // 'gas' is updating to 'gosec'
"goconst",
"gocyclo",
"gofmt",
Expand Down
100 changes: 100 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"io/ioutil"
"strings"

"fmt"

"github.com/CircleCI-Public/circleci-cli/client"
"github.com/CircleCI-Public/circleci-cli/logger"
"github.com/pkg/errors"
Expand Down Expand Up @@ -41,6 +43,17 @@ type PublishOrbResponse struct {
GQLResponseErrors
}

// CreateNamespaceResponse type matches the data shape of the GQL response for
// creating a namespace
type CreateNamespaceResponse struct {
Namespace struct {
CreatedAt string
ID string
}

GQLResponseErrors
}

// ToError returns all GraphQL errors for a single response concatenated, or
// nil.
func (response GQLResponseErrors) ToError() error {
Expand Down Expand Up @@ -164,3 +177,90 @@ func OrbPublish(ctx context.Context, logger *logger.Logger,
}
return &response.PublishOrb.PublishOrbResponse, err
}

func createNamespaceByID(ctx context.Context, logger *logger.Logger, name string, ownerID string) (*CreateNamespaceResponse, error) {
var response struct {
CreateNamespace struct {
CreateNamespaceResponse
}
}

query := `
mutation($name: String!, $organizationId: UUID!) {
createNamespace(
name: $name,
organizationId: $organizationId
) {
namespace {
createdAt
id
}
errors {
message
type
}
}
}`

request := client.NewAuthorizedRequest(viper.GetString("token"), query)
request.Var("name", name)
request.Var("organizationId", ownerID)

graphQLclient := client.NewClient(viper.GetString("endpoint"), logger)

err := graphQLclient.Run(ctx, request, &response)

if err != nil {
err = errors.Wrap(err, fmt.Sprintf("Unable to create namespace %s for ownerId %s", name, ownerID))
}

return &response.CreateNamespace.CreateNamespaceResponse, err
}

func getOrganization(ctx context.Context, logger *logger.Logger, organizationName string, organizationVcs string) (string, error) {
var response struct {
Organization struct {
ID string
}
}

query := `
query($organizationName: String!, $organizationVcs: VCSType!) {
organization(
name: $organizationName
vcsType: $organizationVcs
) {
id
}
}`

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

graphQLclient := client.NewClient(viper.GetString("endpoint"), logger)

err := graphQLclient.Run(ctx, request, &response)

if err != nil || response.Organization.ID == "" {
err = errors.Wrap(err, fmt.Sprintf("Unable to find organization %s of vcs-type %s", organizationName, organizationVcs))
}

return response.Organization.ID, err
}

// CreateNamespace creates (reserves) a namespace for an organization
func CreateNamespace(ctx context.Context, logger *logger.Logger, name string, organizationName string, organizationVcs string) (*CreateNamespaceResponse, error) {
organizationID, err := getOrganization(ctx, logger, organizationName, organizationVcs)
if err != nil {
return nil, err
}

namespace, err := createNamespaceByID(ctx, logger, name, organizationID)

if err != nil {
return nil, err
}

return namespace, err
}
48 changes: 28 additions & 20 deletions cmd/cmd_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,36 @@ func TestCmd(t *testing.T) {
RunSpecs(t, "Cmd Suite")
}

type MockRequestResponse struct {
Request string
Status int
Response string
}

// Test helpers

func appendPostHandler(server *ghttp.Server, authToken string, statusCode int, expectedRequestJson string, responseBody string) {
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/"),
ghttp.VerifyHeader(http.Header{
"Authorization": []string{authToken},
}),
ghttp.VerifyContentType("application/json; charset=utf-8"),
// From Gomegas ghttp.VerifyJson to avoid the
// VerifyContentType("application/json") check
// that fails with "application/json; charset=utf-8"
func(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
req.Body.Close()
Expect(err).ShouldNot(HaveOccurred())
Expect(body).Should(MatchJSON(expectedRequestJson), "JSON Mismatch")
},
ghttp.RespondWith(statusCode, `{ "data": `+responseBody+`}`),
),
)
func appendPostHandler(server *ghttp.Server, authToken string, combineHandlers ...MockRequestResponse) {
for _, handler := range combineHandlers {
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/"),
ghttp.VerifyHeader(http.Header{
"Authorization": []string{authToken},
}),
ghttp.VerifyContentType("application/json; charset=utf-8"),
// From Gomegas ghttp.VerifyJson to avoid the
// VerifyContentType("application/json") check
// that fails with "application/json; charset=utf-8"
func(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
req.Body.Close()
Expect(err).ShouldNot(HaveOccurred())
Expect(body).Should(MatchJSON(handler.Request), "JSON Mismatch")
},
ghttp.RespondWith(handler.Status, `{ "data": `+handler.Response+`}`),
),
)
}
}

type tmpFile struct {
Expand Down
24 changes: 20 additions & 4 deletions cmd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ var _ = Describe("Config", func() {
}
}`

appendPostHandler(testServer, token, http.StatusOK, expectedRequestJson, gqlResponse)
appendPostHandler(testServer, token, MockRequestResponse{
Status: http.StatusOK,
Request: expectedRequestJson,
Response: gqlResponse,
})

session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expand Down Expand Up @@ -97,7 +101,11 @@ var _ = Describe("Config", func() {
}
}`

appendPostHandler(testServer, token, http.StatusOK, expectedRequestJson, gqlResponse)
appendPostHandler(testServer, token, MockRequestResponse{
Status: http.StatusOK,
Request: expectedRequestJson,
Response: gqlResponse,
})

session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expand Down Expand Up @@ -143,7 +151,11 @@ var _ = Describe("Config", func() {
}
}`

appendPostHandler(testServer, token, http.StatusOK, expectedRequestJson, gqlResponse)
appendPostHandler(testServer, token, MockRequestResponse{
Status: http.StatusOK,
Request: expectedRequestJson,
Response: gqlResponse,
})

session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expand Down Expand Up @@ -174,7 +186,11 @@ var _ = Describe("Config", func() {
}
}`

appendPostHandler(testServer, token, http.StatusOK, expectedRequestJson, gqlResponse)
appendPostHandler(testServer, token, MockRequestResponse{
Status: http.StatusOK,
Request: expectedRequestJson,
Response: gqlResponse,
})

session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expand Down
36 changes: 36 additions & 0 deletions cmd/orb.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
var orbPath string
var orbVersion string
var orbID string
var organizationName string
var organizationVcs string

func newOrbCommand() *cobra.Command {

Expand Down Expand Up @@ -48,6 +50,17 @@ func newOrbCommand() *cobra.Command {
orbPublishCommand.PersistentFlags().StringVarP(&orbVersion, "orb-version", "o", "", "version of orb to publish")
orbPublishCommand.PersistentFlags().StringVarP(&orbID, "orb-id", "i", "", "id of orb to publish")

orbCreateNamespace := &cobra.Command{
Use: "create",
Short: "create an orb namespace",
RunE: createOrbNamespace,
Args: cobra.ExactArgs(1),
}

namespaceCommand := &cobra.Command{
Use: "ns",
}

orbCommand := &cobra.Command{
Use: "orb",
Short: "Operate on orbs",
Expand All @@ -63,6 +76,11 @@ func newOrbCommand() *cobra.Command {

orbCommand.AddCommand(orbPublishCommand)

orbCreateNamespace.PersistentFlags().StringVar(&organizationName, "org-name", "", "organization name")
orbCreateNamespace.PersistentFlags().StringVar(&organizationVcs, "vcs", "GITHUB", "organization vcs")
namespaceCommand.AddCommand(orbCreateNamespace)
orbCommand.AddCommand(namespaceCommand)

return orbCommand
}

Expand Down Expand Up @@ -229,3 +247,21 @@ func publishOrb(cmd *cobra.Command, args []string) error {
Logger.Info("Orb published")
return nil
}

func createOrbNamespace(cmd *cobra.Command, args []string) error {
var err error
ctx := context.Background()

response, err := api.CreateNamespace(ctx, Logger, args[0], organizationName, organizationVcs)

if err != nil {
return err
}

if len(response.Errors) > 0 {
return response.ToError()
}

Logger.Info("Namespace created")
return nil
}
Loading

0 comments on commit a9d0184

Please sign in to comment.