Skip to content

Commit

Permalink
Merge pull request #697 from CircleCI-Public/EXT-281-context-update
Browse files Browse the repository at this point in the history
Ext 281 context update
  • Loading branch information
rykimcircle authored May 19, 2022
2 parents af626c3 + e68e80c commit 5e6ca98
Show file tree
Hide file tree
Showing 10 changed files with 421 additions and 39 deletions.
12 changes: 6 additions & 6 deletions api/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import (
// An EnvironmentVariable has a Variable, a ContextID (its owner), and a
// CreatedAt date.
type EnvironmentVariable struct {
Variable string
Variable string
ContextID string
CreatedAt time.Time
}

// A Context is the owner of EnvironmentVariables.
type Context struct{
type Context struct {
CreatedAt time.Time `json:"created_at"`
ID string `json:"id"`
Name string `json:"name"`
ID string `json:"id"`
Name string `json:"name"`
}

// ContextInterface is the interface to interact with contexts and environment
Expand All @@ -25,8 +25,8 @@ type ContextInterface interface {
Contexts(vcs, org string) (*[]Context, error)
ContextByName(vcs, org, name string) (*Context, error)
DeleteContext(contextID string) error
CreateContext(vcs, org, name string) (error)

CreateContext(vcs, org, name string) error
CreateContextWithOrgID(orgID *string, name string) error
EnvironmentVariables(contextID string) (*[]EnvironmentVariable, error)
CreateEnvironmentVariable(contextID, variable, value string) error
DeleteEnvironmentVariable(contextID, variable string) error
Expand Down
16 changes: 13 additions & 3 deletions api/context_graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,22 @@ func (c *GraphQLContextClient) CreateContext(vcsType, orgName, contextName strin
cl := c.Client

org, err := getOrganization(cl, orgName, vcsType)
if err != nil {
return err
}

err = c.CreateContextWithOrgID(&org.Organization.ID, contextName)
if err != nil {
return err
}

return nil
}

// CreateContextWithOrgID creates a new Context in the supplied organization.
func (c *GraphQLContextClient) CreateContextWithOrgID(orgID *string, contextName string) error {
cl := c.Client

query := `
mutation CreateContext($input: CreateContextInput!) {
createContext(input: $input) {
Expand All @@ -78,7 +89,7 @@ func (c *GraphQLContextClient) CreateContext(vcsType, orgName, contextName strin
ContextName string `json:"contextName"`
}

input.OwnerId = org.Organization.ID
input.OwnerId = *orgID
input.OwnerType = "ORGANIZATION"
input.ContextName = contextName

Expand All @@ -94,14 +105,13 @@ func (c *GraphQLContextClient) CreateContext(vcsType, orgName, contextName strin
}
}

if err = cl.Run(request, &response); err != nil {
if err := cl.Run(request, &response); err != nil {
return improveVcsTypeError(err)
}

if response.CreateContext.Error.Type != "" {
return fmt.Errorf("Error creating context: %s", response.CreateContext.Error.Type)
}

return nil
}

Expand Down
72 changes: 72 additions & 0 deletions api/context_rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,37 @@ func (c *ContextRestClient) DeleteEnvironmentVariable(contextID, variable string
return nil
}

func (c *ContextRestClient) CreateContextWithOrgID(orgID *string, name string) error {
req, err := c.newCreateContextRequestWithOrgID(orgID, name)
if err != nil {
return err
}

resp, err := c.client.Do(req)

if err != nil {
return err
}

bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return err
}
if resp.StatusCode != 200 {
var dest errorResponse
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return err
}
return errors.New(*dest.Message)
}
var dest Context
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return err
}
return nil
}

// CreateContext creates a new context in the supplied organization.
func (c *ContextRestClient) CreateContext(vcs, org, name string) error {
req, err := c.newCreateContextRequest(vcs, org, name)
Expand Down Expand Up @@ -329,6 +360,7 @@ func (c *ContextRestClient) listContexts(params *listContextsParams) (*listConte
return &dest, nil
}

//newCreateContextRequest posts a new context creation with orgname and vcs type using a slug
func (c *ContextRestClient) newCreateContextRequest(vcs, org, name string) (*http.Request, error) {
var err error
queryURL, err := url.Parse(c.server)
Expand All @@ -352,6 +384,7 @@ func (c *ContextRestClient) newCreateContextRequest(vcs, org, name string) (*htt
Owner: struct {
Slug *string `json:"slug,omitempty"`
}{

Slug: toSlug(vcs, org),
},
}
Expand All @@ -366,6 +399,45 @@ func (c *ContextRestClient) newCreateContextRequest(vcs, org, name string) (*htt
return c.newHTTPRequest("POST", queryURL.String(), bodyReader)
}

//newCreateContextRequestWithOrgID posts a new context creation with an orgID
func (c *ContextRestClient) newCreateContextRequestWithOrgID(orgID *string, name string) (*http.Request, error) {
var err error
queryURL, err := url.Parse(c.server)
if err != nil {
return nil, err
}
queryURL, err = queryURL.Parse("context")
if err != nil {
return nil, err
}

var bodyReader io.Reader

var body = struct {
Name string `json:"name"`
Owner struct {
ID *string `json:"id,omitempty"`
} `json:"owner"`
}{
Name: name,
Owner: struct {
ID *string `json:"id,omitempty"`
}{

ID: orgID,
},
}
buf, err := json.Marshal(body)

if err != nil {
return nil, err
}

bodyReader = bytes.NewReader(buf)

return c.newHTTPRequest("POST", queryURL.String(), bodyReader)
}

func (c *ContextRestClient) newCreateEnvironmentVariableRequest(contextID, variable, value string) (*http.Request, error) {
var err error
queryURL, err := url.Parse(c.server)
Expand Down
98 changes: 98 additions & 0 deletions api/context_rest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package api

import (
"fmt"
"io/ioutil"
"net/http"

"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/ghttp"
)

type MockRequestResponse struct {
Request string
Status int
Response string
ErrorResponse string
}

// Uses Ginkgo http handler to mock out http requests and make assertions off the results.
// If ErrorResponse is defined in the passed handler it will override the Response.
func appendRESTPostHandler(server *ghttp.Server, combineHandlers ...MockRequestResponse) {
for _, handler := range combineHandlers {
responseBody := handler.Response
if handler.ErrorResponse != "" {
responseBody = handler.ErrorResponse
}

server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v2/context"),
ghttp.VerifyContentType("application/json"),
func(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
Expect(err).ShouldNot(HaveOccurred())
err = req.Body.Close()
Expect(err).ShouldNot(HaveOccurred())
Expect(handler.Request).Should(MatchJSON(body), "JSON Mismatch")
},
ghttp.RespondWith(handler.Status, responseBody),
),
)
}
}

func getContextRestClient(server *ghttp.Server) (*ContextRestClient, error) {
client := &http.Client{}

return NewContextRestClient(settings.Config{
RestEndpoint: "api/v2",
Host: server.URL(),
HTTPClient: client,
Token: "token",
})
}

var _ = ginkgo.Describe("Context Rest Tests", func() {
ginkgo.It("Should handle a successful request with createContextWithOrgID", func() {
server := ghttp.NewServer()

defer server.Close()

name := "name"
orgID := "497f6eca-6276-4993-bfeb-53cbbbba6f08"
client, err := getContextRestClient(server)
Expect(err).To(BeNil())

appendRESTPostHandler(server, MockRequestResponse{
Status: http.StatusOK,
Request: fmt.Sprintf(`{"name": "%s","owner":{"id":"%s"}}`, name, orgID),
Response: fmt.Sprintf(`{"id": "%s", "name": "%s", "created_at": "2015-09-21T17:29:21.042Z" }`, orgID, name),
})

err = client.CreateContextWithOrgID(&orgID, name)
Expect(err).To(BeNil())
})

ginkgo.It("Should handle an error request with createContextWithOrgID", func() {
server := ghttp.NewServer()

defer server.Close()

name := "name"
orgID := "497f6eca-6276-4993-bfeb-53cbbbba6f08"
client, err := getContextRestClient(server)
Expect(err).To(BeNil())

appendRESTPostHandler(server, MockRequestResponse{
Status: http.StatusInternalServerError,
Request: fmt.Sprintf(`{"name": "%s","owner":{"id":"%s"}}`, name, orgID),
ErrorResponse: `{"message": "🍎"}`,
})

err = client.CreateContextWithOrgID(&orgID, name)
Expect(err).ToNot(BeNil())
})
})
49 changes: 49 additions & 0 deletions api/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func createSingleUseGraphQLServer(result interface{}, requestAssertions func(req
}

var _ = ginkgo.Describe("API", func() {
orgID := "bb604b45-b6b0-4b81-ad80-796f15eddf87"

ginkgo.Describe("FooBar", func() {
ginkgo.It("improveVcsTypeError", func() {

Expand Down Expand Up @@ -94,6 +96,30 @@ var _ = ginkgo.Describe("API", func() {

})

ginkgo.It("can handles failure creating contexts", func() {

var result struct {
CreateContext struct {
Error struct {
Type string
}
}
}

result.CreateContext.Error.Type = "force-this-error"

server, client := createSingleUseGraphQLServer(result, func(count uint64, req *graphQLRequest) {
switch count {
case 1:
Expect(req.Variables["input"].(map[string]interface{})["ownerId"]).To(Equal(orgID))
}
})
defer server.Close()
err := client.CreateContextWithOrgID(&orgID, "foo-bar")
Expect(err).To(MatchError("Error creating context: force-this-error"))

})

})

ginkgo.It("can handles success creating contexts", func() {
Expand Down Expand Up @@ -126,6 +152,29 @@ var _ = ginkgo.Describe("API", func() {

})

ginkgo.It("can handles success creating contexts with create context with orgID", func() {

var result struct {
CreateContext struct {
Error struct {
Type string
}
}
}

result.CreateContext.Error.Type = ""

server, client := createSingleUseGraphQLServer(result, func(count uint64, req *graphQLRequest) {
switch count {
case 1:
Expect(req.Variables["input"].(map[string]interface{})["ownerId"]).To(Equal(orgID))
}
})
defer server.Close()
Expect(client.CreateContextWithOrgID(&orgID, "foo-bar")).To(Succeed())

})

ginkgo.Describe("List Contexts", func() {

ginkgo.It("can list contexts", func() {
Expand Down
24 changes: 24 additions & 0 deletions clitest/clitest.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,30 @@ type MockRequestResponse struct {
ErrorResponse string
}

func (tempSettings *TempSettings) AppendRESTPostHandler(combineHandlers ...MockRequestResponse) {
for _, handler := range combineHandlers {
responseBody := handler.Response
if handler.ErrorResponse != "" {
responseBody = handler.ErrorResponse
}

tempSettings.TestServer.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("POST", "/api/v2/context"),
ghttp.VerifyContentType("application/json"),
func(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
err = req.Body.Close()
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
gomega.Expect(handler.Request).Should(gomega.MatchJSON(body), "JSON Mismatch")
},
ghttp.RespondWith(handler.Status, responseBody),
),
)
}
}

// AppendPostHandler stubs out the provided MockRequestResponse.
// When authToken is an empty string no token validation is performed.
func (tempSettings *TempSettings) AppendPostHandler(authToken string, combineHandlers ...MockRequestResponse) {
Expand Down
Loading

0 comments on commit 5e6ca98

Please sign in to comment.