Skip to content

Commit

Permalink
Merge pull request #873 from CircleCI-Public/revert-865-PIPE-2315/rem…
Browse files Browse the repository at this point in the history
…ove-api-service-from-config-compilation

Revert "PIPE-2315 - Removes API-Service from config compilation"
  • Loading branch information
elliotforbes authored Mar 20, 2023
2 parents 88383c6 + 092ba1a commit 299ecec
Show file tree
Hide file tree
Showing 11 changed files with 438 additions and 578 deletions.
119 changes: 119 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"

"github.com/CircleCI-Public/circleci-cli/api/graphql"
"github.com/CircleCI-Public/circleci-cli/pipeline"
"github.com/CircleCI-Public/circleci-cli/references"
"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/Masterminds/semver"
Expand Down Expand Up @@ -512,6 +513,124 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) {
return &response, nil
}

// ConfigQueryLegacy calls the GQL API to validate and process config with the legacy orgSlug
func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) {
var response BuildConfigResponse
var query string
config, err := loadYaml(configPath)
if err != nil {
return nil, err
}
// GraphQL isn't forwards-compatible, so we are unusually selective here about
// passing only non-empty fields on to the API, to minimize user impact if the
// backend is out of date.
var fieldAddendums string
if orgSlug != "" {
fieldAddendums += ", orgSlug: $orgSlug"
}
if len(params) > 0 {
fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson"
}
query = fmt.Sprintf(
`query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgSlug: String) {
buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) {
valid,
errors { message },
sourceYaml,
outputYaml
}
}`,
fieldAddendums)

request := graphql.NewRequest(query)
request.SetToken(cl.Token)
request.Var("config", config)

if values != nil {
request.Var("pipelineValues", pipeline.PrepareForGraphQL(values))
}
if params != nil {
pipelineParameters, err := json.Marshal(params)
if err != nil {
return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error())
}
request.Var("pipelineParametersJson", string(pipelineParameters))
}

if orgSlug != "" {
request.Var("orgSlug", orgSlug)
}

err = cl.Run(request, &response)
if err != nil {
return nil, errors.Wrap(err, "Unable to validate config")
}
if len(response.BuildConfig.ConfigResponse.Errors) > 0 {
return nil, &response.BuildConfig.ConfigResponse.Errors
}

return &response.BuildConfig.ConfigResponse, nil
}

// ConfigQuery calls the GQL API to validate and process config with the org id
func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pipeline.Parameters, values pipeline.Values) (*ConfigResponse, error) {
var response BuildConfigResponse
var query string
config, err := loadYaml(configPath)
if err != nil {
return nil, err
}
// GraphQL isn't forwards-compatible, so we are unusually selective here about
// passing only non-empty fields on to the API, to minimize user impact if the
// backend is out of date.
var fieldAddendums string
if orgId != "" {
fieldAddendums += ", orgId: $orgId"
}
if len(params) > 0 {
fieldAddendums += ", pipelineParametersJson: $pipelineParametersJson"
}
query = fmt.Sprintf(
`query ValidateConfig ($config: String!, $pipelineParametersJson: String, $pipelineValues: [StringKeyVal!], $orgId: UUID!) {
buildConfig(configYaml: $config, pipelineValues: $pipelineValues%s) {
valid,
errors { message },
sourceYaml,
outputYaml
}
}`,
fieldAddendums)

request := graphql.NewRequest(query)
request.SetToken(cl.Token)
request.Var("config", config)

if values != nil {
request.Var("pipelineValues", pipeline.PrepareForGraphQL(values))
}
if params != nil {
pipelineParameters, err := json.Marshal(params)
if err != nil {
return nil, fmt.Errorf("unable to serialize pipeline values: %s", err.Error())
}
request.Var("pipelineParametersJson", string(pipelineParameters))
}

if orgId != "" {
request.Var("orgId", orgId)
}

err = cl.Run(request, &response)
if err != nil {
return nil, errors.Wrap(err, "Unable to validate config")
}
if len(response.BuildConfig.ConfigResponse.Errors) > 0 {
return nil, &response.BuildConfig.ConfigResponse.Errors
}

return &response.BuildConfig.ConfigResponse, nil
}

// OrbQuery validated and processes an orb.
func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) {
var response OrbConfigResponse
Expand Down
44 changes: 4 additions & 40 deletions api/rest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@ import (
)

type Client struct {
baseURL *url.URL
// The config api host differs for both cloud and server setups.
// For cloud, the base domain will be https://api.circleci.com
// for server, this should match the host as we don't have the same
// api subdomain setup
apiURL *url.URL
baseURL *url.URL
circleToken string
client *http.Client
}
Expand All @@ -34,15 +29,13 @@ func New(host string, config *settings.Config) *Client {
endpoint += "/"
}

baseURL, _ := url.Parse(host)
apiURL, _ := url.Parse(config.ConfigAPIHost)
u, _ := url.Parse(host)

client := config.HTTPClient
client.Timeout = 10 * time.Second

return &Client{
apiURL: apiURL.ResolveReference(&url.URL{Path: endpoint}),
baseURL: baseURL.ResolveReference(&url.URL{Path: endpoint}),
baseURL: u.ResolveReference(&url.URL{Path: endpoint}),
circleToken: config.Token,
client: client,
}
Expand All @@ -64,34 +57,6 @@ func (c *Client) NewRequest(method string, u *url.URL, payload interface{}) (req
return nil, err
}

c.enrichRequestHeaders(req, payload)
return req, nil
}

// NewAPIRequest - similar to NewRequest except it uses the apiURL as the base URL.
func (c *Client) NewAPIRequest(method string, u *url.URL, payload interface{}) (req *http.Request, err error) {
var r io.Reader
if payload != nil {
buf := &bytes.Buffer{}
r = buf
err = json.NewEncoder(buf).Encode(payload)
if err != nil {
fmt.Printf("failed to encode payload as json: %s\n", err.Error())
return nil, err
}
}

req, err = http.NewRequest(method, c.apiURL.ResolveReference(u).String(), r)
if err != nil {
fmt.Printf("failed to create new http request: %s\n", err.Error())
return nil, err
}

c.enrichRequestHeaders(req, payload)
return req, nil
}

func (c *Client) enrichRequestHeaders(req *http.Request, payload interface{}) {
req.Header.Set("Circle-Token", c.circleToken)
req.Header.Set("Accept", "application/json")
req.Header.Set("User-Agent", version.UserAgent())
Expand All @@ -102,12 +67,12 @@ func (c *Client) enrichRequestHeaders(req *http.Request, payload interface{}) {
if payload != nil {
req.Header.Set("Content-Type", "application/json")
}
return req, nil
}

func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int, err error) {
httpResp, err := c.client.Do(req)
if err != nil {
fmt.Printf("failed to make http request: %s\n", err.Error())
return 0, err
}
defer httpResp.Body.Close()
Expand All @@ -118,7 +83,6 @@ func (c *Client) DoRequest(req *http.Request, resp interface{}) (statusCode int,
}{}
err = json.NewDecoder(httpResp.Body).Decode(&httpError)
if err != nil {
fmt.Printf("failed to decode body: %s", err.Error())
return httpResp.StatusCode, err
}
return httpResp.StatusCode, &HTTPError{Code: httpResp.StatusCode, Message: httpError.Message}
Expand Down
37 changes: 0 additions & 37 deletions api/rest/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,43 +118,6 @@ func TestClient_DoRequest(t *testing.T) {
})
}

func TestAPIRequest(t *testing.T) {
fix := &fixture{}
c, cleanup := fix.Run(http.StatusCreated, `{"key": "value"}`)
defer cleanup()

t.Run("test new api request sets the default headers", func(t *testing.T) {
req, err := c.NewAPIRequest("GET", &url.URL{}, struct{}{})
assert.NilError(t, err)

assert.Equal(t, req.Header.Get("User-Agent"), "circleci-cli/0.0.0-dev+dirty-local-tree (source)")
assert.Equal(t, req.Header.Get("Circle-Token"), c.circleToken)
assert.Equal(t, req.Header.Get("Accept"), "application/json")
})

type testPayload struct {
Message string
}

t.Run("test new api request sets the default headers", func(t *testing.T) {
req, err := c.NewAPIRequest("GET", &url.URL{}, testPayload{Message: "hello"})
assert.NilError(t, err)

assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "")
assert.Equal(t, req.Header.Get("Content-Type"), "application/json")
})

t.Run("test new api request doesn't set content-type with empty payload", func(t *testing.T) {
req, err := c.NewAPIRequest("GET", &url.URL{}, nil)
assert.NilError(t, err)

assert.Equal(t, req.Header.Get("Circleci-Cli-Command"), "")
if req.Header.Get("Content-Type") != "" {
t.Fail()
}
})
}

type fixture struct {
mu sync.Mutex
url url.URL
Expand Down
Loading

0 comments on commit 299ecec

Please sign in to comment.