Skip to content

Commit

Permalink
Merge pull request #690 from CircleCI-Public/create-namespace
Browse files Browse the repository at this point in the history
[EXT-278] Support org id in namespace create command
  • Loading branch information
am-bui authored May 4, 2022
2 parents 341e493 + 3b3107c commit 18585af
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 14 deletions.
4 changes: 2 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ func CreateImportedNamespace(cl *graphql.Client, name string) (*ImportNamespaceR
return &response, nil
}

func createNamespaceWithOwnerID(cl *graphql.Client, name string, ownerID string) (*CreateNamespaceResponse, error) {
func CreateNamespaceWithOwnerID(cl *graphql.Client, name string, ownerID string) (*CreateNamespaceResponse, error) {
var response CreateNamespaceResponse

query := `
Expand Down Expand Up @@ -959,7 +959,7 @@ func CreateNamespace(cl *graphql.Client, name string, organizationName string, o
return nil, errors.Wrap(organizationNotFound(organizationName, organizationVcs), getOrgError.Error())
}

createNSResponse, createNSError := createNamespaceWithOwnerID(cl, name, getOrgResponse.Organization.ID)
createNSResponse, createNSError := CreateNamespaceWithOwnerID(cl, name, getOrgResponse.Organization.ID)

if createNSError != nil {
return nil, createNSError
Expand Down
62 changes: 52 additions & 10 deletions cmd/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/CircleCI-Public/circleci-cli/api/graphql"
"github.com/CircleCI-Public/circleci-cli/prompt"
"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/google/uuid"
"github.com/spf13/cobra"
)

Expand All @@ -18,6 +19,7 @@ type namespaceOptions struct {

// Allows user to skip y/n confirm when creating a namespace
noPrompt bool
orgID *string
// This lets us pass in our own interface for testing
tty createNamespaceUserInterface
// Linked with --integration-testing flag for stubbing UI in gexec tests
Expand Down Expand Up @@ -55,7 +57,7 @@ func newNamespaceCommand(config *settings.Config) *cobra.Command {
}

createCmd := &cobra.Command{
Use: "create <name> <vcs-type> <org-name>",
Use: "create <name> [<vcs-type>] [<org-name>]",
Short: "Create a namespace",
Long: `Create a namespace.
Please note that at this time all namespaces created in the registry are world-readable.`,
Expand All @@ -65,28 +67,31 @@ Please note that at this time all namespaces created in the registry are world-r

return validateToken(opts.cfg)
},
RunE: func(_ *cobra.Command, _ []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
if opts.integrationTesting {
opts.tty = createNamespaceTestUI{
confirm: true,
}
}

return createNamespace(opts)
return createNamespace(cmd, opts)
},
Args: cobra.ExactArgs(3),
Args: cobra.RangeArgs(1, 3),
Annotations: make(map[string]string),
Example: ` circleci namespace create NamespaceName github OrgName
circleci namespace create NamespaceName --org-id "your-org-id-here"`,
}

createCmd.Annotations["<name>"] = "The name to give your new namespace"
createCmd.Annotations["<vcs-type>"] = `Your VCS provider, can be either "github" or "bitbucket"`
createCmd.Annotations["<org-name>"] = `The name used for your organization`
createCmd.Annotations["[<vcs-type>]"] = `Your VCS provider, can be either "github" or "bitbucket". Optional when passing org-id flag.`
createCmd.Annotations["[<org-name>]"] = `The name used for your organization. Optional when passing org-id flag.`

createCmd.Flags().BoolVar(&opts.integrationTesting, "integration-testing", false, "Enable test mode to bypass interactive UI.")
if err := createCmd.Flags().MarkHidden("integration-testing"); err != nil {
panic(err)
}
createCmd.Flags().BoolVar(&opts.noPrompt, "no-prompt", false, "Disable prompt to bypass interactive UI.")
opts.orgID = createCmd.Flags().String("org-id", "", "The id of your organization.")

namespaceCmd.AddCommand(createCmd)

Expand All @@ -103,9 +108,32 @@ func deleteNamespaceAlias(opts namespaceOptions) error {
return nil
}

func createNamespace(opts namespaceOptions) error {
namespaceName := opts.args[0]
func createNamespaceWithOrgId(opts namespaceOptions, namespaceName, orgId string) error {
if !opts.noPrompt {
fmt.Printf(`You are creating a namespace called "%s".
This is the only namespace permitted for your organization with id %s.
To change the namespace, you will have to contact CircleCI customer support.
`, namespaceName, orgId)
}

confirm := fmt.Sprintf("Are you sure you wish to create the namespace: `%s`", namespaceName)
if opts.noPrompt || opts.tty.askUserToConfirm(confirm) {
_, err := api.CreateNamespaceWithOwnerID(opts.cl, namespaceName, orgId)

if err != nil {
return err
}

fmt.Printf("Namespace `%s` created.\n", namespaceName)
fmt.Println("Please note that any orbs you publish in this namespace are open orbs and are world-readable.")
}
return nil
}

func createNamespaceWithVcsTypeAndOrgName(opts namespaceOptions, namespaceName, vcsType, orgName string) error {
if !opts.noPrompt {
fmt.Printf(`You are creating a namespace called "%s".
Expand All @@ -119,18 +147,32 @@ To change the namespace, you will have to contact CircleCI customer support.
confirm := fmt.Sprintf("Are you sure you wish to create the namespace: `%s`", namespaceName)
if opts.noPrompt || opts.tty.askUserToConfirm(confirm) {
_, err := api.CreateNamespace(opts.cl, namespaceName, opts.args[2], strings.ToUpper(opts.args[1]))

if err != nil {
return err
}

fmt.Printf("Namespace `%s` created.\n", namespaceName)
fmt.Println("Please note that any orbs you publish in this namespace are open orbs and are world-readable.")
}

return nil
}

func createNamespace(cmd *cobra.Command,opts namespaceOptions) error {
namespaceName := opts.args[0]
//skip if no orgid provided
if opts.orgID != nil && strings.TrimSpace(*opts.orgID) != ""{
_, err := uuid.Parse(*opts.orgID)
if err == nil {
return createNamespaceWithOrgId(opts, namespaceName, *opts.orgID)
}

//skip if no vcs type and org name provided
} else if len(opts.args) == 3{
return createNamespaceWithVcsTypeAndOrgName(opts, namespaceName, opts.args[1], opts.args[2])
}
return cmd.Help()
}

func renameNamespace(opts namespaceOptions) error {
oldName := opts.args[0]
newName := opts.args[1]
Expand Down
60 changes: 58 additions & 2 deletions cmd/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,62 @@ var _ = Describe("Namespace integration tests", func() {
})

Context("create, with interactive prompts", func() {
Describe("registering a namespace", func() {
Describe("registering a namespace with orgID", func() {
BeforeEach(func() {
command = exec.Command(pathCLI,
"namespace", "create",
"--skip-update-check",
"--token", token,
"--host", tempSettings.TestServer.URL(),
"--integration-testing",
"foo-ns",
"--org-id", `"bb604b45-b6b0-4b81-ad80-796f15eddf87"`,
)
})

It("works with organizationID", func() {
By("setting up a mock server")

gqlOrganizationResponse := `{
"organization": {
"name": "test-org",
"id": "bb604b45-b6b0-4b81-ad80-796f15eddf87"
}
}`

expectedOrganizationRequest := `{
"query": "\n\t\t\tmutation($name: String!, $organizationId: UUID!) {\n\t\t\t\tcreateNamespace(\n\t\t\t\t\tname: $name,\n\t\t\t\t\torganizationId: $organizationId\n\t\t\t\t) {\n\t\t\t\t\tnamespace {\n\t\t\t\t\t\tid\n\t\t\t\t\t}\n\t\t\t\t\terrors {\n\t\t\t\t\t\tmessage\n\t\t\t\t\t\ttype\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}",
"variables": {
"name": "foo-ns",
"organizationId": "\"bb604b45-b6b0-4b81-ad80-796f15eddf87\""
}
}`

tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{
Status: http.StatusOK,
Request: expectedOrganizationRequest,
Response: gqlOrganizationResponse})

By("running the command")
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
Expect(err).ShouldNot(HaveOccurred())
Eventually(session).Should(gexec.Exit(0))

stdout := session.Wait().Out.Contents()

Expect(string(stdout)).To(ContainSubstring(fmt.Sprintf(`You are creating a namespace called "%s".
This is the only namespace permitted for your organization with id "%s".
To change the namespace, you will have to contact CircleCI customer support.
Are you sure you wish to create the namespace: %s
Namespace %s created.
Please note that any orbs you publish in this namespace are open orbs and are world-readable.`, "foo-ns", "bb604b45-b6b0-4b81-ad80-796f15eddf87", "`foo-ns`", "`foo-ns`")))
})
})

Describe("registering a namespace with OrgName and OrgVcs", func() {
BeforeEach(func() {
command = exec.Command(pathCLI,
"namespace", "create",
Expand Down Expand Up @@ -230,7 +285,8 @@ Please note that any orbs you publish in this namespace are open orbs and are wo
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)

Expect(err).ShouldNot(HaveOccurred())
Eventually(session.Err).Should(gbytes.Say("Error: error1\nerror2"))
Eventually(session.Err).Should(gbytes.Say(`Error: error1
error2`))
Eventually(session).ShouldNot(gexec.Exit(0))
})
})
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/gobuffalo/packr/v2 v2.0.0-rc.13
github.com/google/go-github v15.0.0+incompatible // indirect
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
github.com/google/uuid v1.3.0
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mitchellh/mapstructure v1.1.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ github.com/google/go-github v15.0.0+incompatible h1:jlPg2Cpsxb/FyEV/MFiIE9tW/2RA
github.com/google/go-github v15.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
Expand Down

0 comments on commit 18585af

Please sign in to comment.