From 1d5a0501afe63f26d474a4f4e5bc10f48803601d Mon Sep 17 00:00:00 2001 From: JulesFaucherre Date: Mon, 4 Mar 2024 11:16:35 +0100 Subject: [PATCH] changed the `context list` table (#1047) --- api/api.go | 6 +-- cmd/context.go | 59 +++++++++++++++++++---------- cmd/context_test.go | 90 ++++++++++++++++++++++++++------------------- 3 files changed, 94 insertions(+), 61 deletions(-) diff --git a/api/api.go b/api/api.go index 6ef521bf5..ff3df2238 100644 --- a/api/api.go +++ b/api/api.go @@ -770,7 +770,7 @@ func GetOrganization(cl *graphql.Client, params GetOrganizationParams) (*GetOrga var request *graphql.Request if params.OrgID != "" { - request = graphql.NewRequest(`query($orgId: UUID!) { + request = graphql.NewRequest(`query($orgId: ID!) { organization(id: $orgId) { id name @@ -787,14 +787,14 @@ func GetOrganization(cl *graphql.Client, params GetOrganizationParams) (*GetOrga } }`) request.Var("orgName", params.OrgName) - request.Var("vcsType", params.VCSType) + request.Var("vcsType", strings.ToUpper(params.VCSType)) } var response GetOrganizationResponse request.SetToken(cl.Token) err := cl.Run(request, &response) if err != nil { - return nil, err + return nil, errors.Wrap(err, "fetching organization") } return &response, nil } diff --git a/cmd/context.go b/cmd/context.go index 703ffb8d1..f359885ee 100644 --- a/cmd/context.go +++ b/cmd/context.go @@ -8,7 +8,9 @@ import ( "strings" "time" + "github.com/CircleCI-Public/circleci-cli/api" "github.com/CircleCI-Public/circleci-cli/api/context" + "github.com/CircleCI-Public/circleci-cli/api/graphql" "github.com/CircleCI-Public/circleci-cli/prompt" "github.com/olekukonko/tablewriter" "github.com/pkg/errors" @@ -78,7 +80,21 @@ are injected at runtime.`, Use: "list --org-id ", PreRunE: initClient, RunE: func(cmd *cobra.Command, args []string) error { - return listContexts(contextClient) + gqlClient := graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, false) + params := api.GetOrganizationParams{ + OrgID: orgID, + VCSType: vcsType, + OrgName: orgName, + } + params.OrgID = orgID + params.VCSType = vcsType + params.OrgName = orgName + org, err := api.GetOrganization(gqlClient, params) + if err != nil { + return err + } + + return listContexts(contextClient, org.Organization.Name, org.Organization.ID) }, Args: MultiExactArgs(0, 2), Example: `circleci context list --org-id 00000000-0000-0000-0000-000000000000 @@ -158,7 +174,20 @@ are injected at runtime.`, Use: "delete --org-id ", PreRunE: initClient, RunE: func(cmd *cobra.Command, args []string) error { - return deleteContext(contextClient, force, initiatedArgs[0]) + gqlClient := graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, false) + params := api.GetOrganizationParams{ + OrgID: orgID, + VCSType: vcsType, + OrgName: orgName, + } + params.OrgID = orgID + params.VCSType = vcsType + params.OrgName = orgName + org, err := api.GetOrganization(gqlClient, params) + if err != nil { + return err + } + return deleteContext(contextClient, org.Organization.Name, force, initiatedArgs[0]) }, Args: MultiExactArgs(1, 3), Example: `circleci context delete --org-id 00000000-0000-0000-0000-000000000000 contextName @@ -177,17 +206,18 @@ are injected at runtime.`, return command } -func listContexts(contextClient context.ContextInterface) error { +func listContexts(contextClient context.ContextInterface, orgName string, orgId string) error { contexts, err := contextClient.Contexts() if err != nil { return err } table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Id", "Name", "Created At"}) + table.SetHeader([]string{"Organization", "Org ID", "Name", "Created At"}) for _, context := range contexts { table.Append([]string{ - context.ID, + orgName, + orgId, context.Name, context.CreatedAt.Format(time.RFC3339), }) @@ -282,27 +312,16 @@ func storeEnvVar(client context.ContextInterface, prompt storeEnvVarPrompt, cont return err } -func askForConfirmation(message string) bool { - fmt.Println(message) - var response string - if _, err := fmt.Scanln(&response); err != nil { - return false - } - return strings.HasPrefix(strings.ToLower(response), "y") -} - -func deleteContext(client context.ContextInterface, force bool, contextName string) error { +func deleteContext(client context.ContextInterface, orgName string, force bool, contextName string) error { context, err := client.ContextByName(contextName) if err != nil { return err } - message := fmt.Sprintf("Are you sure that you want to delete this context: %s (y/n)?", context.Name) - - shouldDelete := force || askForConfirmation(message) - + shouldDelete := force || prompt.AskUserToConfirm(fmt.Sprintf("Are you sure that you want to delete this context: %s %s (y/n)?", orgName, context.Name)) if !shouldDelete { - return errors.New("OK, cancelling") + fmt.Printf("Cancelling context deletion") + return nil } err = client.DeleteContext(context.ID) diff --git a/cmd/context_test.go b/cmd/context_test.go index 5ab8207e2..d58558e0e 100644 --- a/cmd/context_test.go +++ b/cmd/context_test.go @@ -20,20 +20,15 @@ import ( var ( contentTypeHeader http.Header = map[string][]string{"Content-Type": {"application/json"}} -) - -func mockServerForREST(tempSettings *clitest.TempSettings) { - tempSettings.TestServer.AppendHandlers( - ghttp.CombineHandlers( - ghttp.VerifyRequest("GET", "/api/v2/openapi.json"), - ghttp.RespondWith( - http.StatusOK, - `{"paths":{"/context":{}}}`, - contentTypeHeader, - ), + openAPIHandler = ghttp.CombineHandlers( + ghttp.VerifyRequest("GET", "/api/v2/openapi.json"), + ghttp.RespondWith( + http.StatusOK, + `{"paths":{"/context":{}}}`, + contentTypeHeader, ), ) -} +) var _ = Describe("Context integration tests", func() { var ( @@ -45,6 +40,19 @@ var _ = Describe("Context integration tests", func() { vcsType string = "bitbucket" orgName string = "test-org" orgSlug string = fmt.Sprintf("%s/%s", vcsType, orgName) + + gqlGetOrgHandler = ghttp.CombineHandlers( + ghttp.VerifyRequest("POST", "/graphql-unstable"), + ghttp.RespondWith(http.StatusOK, fmt.Sprintf(`{ + "data": { + "organization": { + "id": "%s", + "name": "%s", + "vcsType": "%s" + } + } +}`, orgID, orgName, vcsType)), + ) ) BeforeEach(func() { @@ -73,20 +81,21 @@ https://circleci.com/account/api`)) }) It("should handle errors", func() { - mockServerForREST(tempSettings) + contextName := "context-name" tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( - ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-id=%s", orgID)), + ghttp.VerifyRequest("POST", "/api/v2/context"), ghttp.RespondWith( - http.StatusBadRequest, - `{"message":"no context found"}`, + http.StatusUnauthorized, + `{"message":"permission issue"}`, contentTypeHeader, ), ), ) command = commandWithHome(pathCLI, tempSettings.Home, - "context", "list", "--org-id", orgID, + "context", "create", "--org-id", orgID, contextName, "--skip-update-check", "--token", token, "--host", tempSettings.TestServer.URL(), @@ -97,14 +106,15 @@ https://circleci.com/account/api`)) Eventually(session).Should(gexec.Exit()) // Exit codes are different between Unix and Windows so we're only checking that it does not equal 0 Expect(session.ExitCode()).ToNot(Equal(0)) - Expect(string(session.Err.Contents())).To(Equal("Error: no context found\n")) + Expect(string(session.Err.Contents())).To(Equal("Error: permission issue\n")) }) }) Describe("list", func() { - It("should list context with VCS / org name", func() { - mockServerForREST(tempSettings) + It("should list contexts with the right columns", func() { tempSettings.TestServer.AppendHandlers( + openAPIHandler, + gqlGetOrgHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-slug=%s", orgSlug)), ghttp.RespondWith( @@ -125,10 +135,10 @@ https://circleci.com/account/api`)) Expect(err).ShouldNot(HaveOccurred()) Eventually(session).Should(gexec.Exit(0)) - Expect(string(session.Out.Contents())).To(Equal(`+----+------+------------+ -| ID | NAME | CREATED AT | -+----+------+------------+ -+----+------+------------+ + Expect(string(session.Out.Contents())).To(Equal(`+--------------+--------+------+------------+ +| ORGANIZATION | ORG ID | NAME | CREATED AT | ++--------------+--------+------+------------+ ++--------------+--------+------+------------+ `)) }) @@ -139,8 +149,9 @@ https://circleci.com/account/api`)) } body, err := json.Marshal(struct{ Items []context.Context }{contexts}) Expect(err).ShouldNot(HaveOccurred()) - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, + gqlGetOrgHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-slug=%s", orgSlug)), ghttp.RespondWith( @@ -162,7 +173,7 @@ https://circleci.com/account/api`)) Expect(err).ShouldNot(HaveOccurred()) Eventually(session).Should(gexec.Exit(0)) lines := strings.Split(string(session.Out.Contents()), "\n") - Expect(lines[1]).To(MatchRegexp("|\\w+ID\\w+|\\w+NAME\\w+|\\w+CREATED AT\\w+|")) + Expect(lines[1]).To(MatchRegexp("|\\w+ORGANIZATION\\w+|\\w+ORG ID\\w+|\\w+NAME\\w+|\\w+CREATED AT\\w+|")) Expect(lines).To(HaveLen(7)) }) @@ -173,8 +184,9 @@ https://circleci.com/account/api`)) } body, err := json.Marshal(struct{ Items []context.Context }{contexts}) Expect(err).ShouldNot(HaveOccurred()) - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, + gqlGetOrgHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-id=%s", orgID)), ghttp.RespondWith( @@ -216,8 +228,8 @@ https://circleci.com/account/api`)) ) It("should show context with vcs type / org name", func() { - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-slug=%s", orgSlug)), ghttp.RespondWith( @@ -252,8 +264,8 @@ https://circleci.com/account/api`)) }) It("should show context with org id", func() { - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-id=%s", orgID)), ghttp.RespondWith( @@ -306,8 +318,8 @@ https://circleci.com/account/api`)) It("should store value when giving vcs type / org name", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-slug=%s", orgSlug)), ghttp.RespondWith( @@ -342,8 +354,8 @@ https://circleci.com/account/api`)) It("should store value when giving vcs type / org name", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-id=%s", orgID)), ghttp.RespondWith( @@ -389,8 +401,8 @@ https://circleci.com/account/api`)) It("should remove environment variable with vcs type / org name", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-slug=%s", orgSlug)), ghttp.RespondWith( @@ -424,8 +436,8 @@ https://circleci.com/account/api`)) It("should remove environment variable with org id", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-id=%s", orgID)), ghttp.RespondWith( @@ -469,8 +481,9 @@ https://circleci.com/account/api`)) It("should delete context with vcs type / org name", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, + gqlGetOrgHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-slug=%s", orgSlug)), ghttp.RespondWith( @@ -504,8 +517,9 @@ https://circleci.com/account/api`)) It("should delete context with org id", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, + gqlGetOrgHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("GET", "/api/v2/context", fmt.Sprintf("owner-id=%s", orgID)), ghttp.RespondWith( @@ -550,8 +564,8 @@ https://circleci.com/account/api`)) It("should create new context using an org id", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("POST", "/api/v2/context"), ghttp.VerifyContentType("application/json"), @@ -575,8 +589,8 @@ https://circleci.com/account/api`)) It("should create new context using vcs type / org name", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("POST", "/api/v2/context"), ghttp.VerifyContentType("application/json"), @@ -604,8 +618,8 @@ https://circleci.com/account/api`)) It("handles errors", func() { By("setting up a mock server") - mockServerForREST(tempSettings) tempSettings.TestServer.AppendHandlers( + openAPIHandler, ghttp.CombineHandlers( ghttp.VerifyRequest("POST", "/api/v2/context"), ghttp.VerifyContentType("application/json"),