Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DEVEX-1216] context commands can now be used with --org-id parameter. #1046

Merged
merged 1 commit into from
Feb 28, 2024

Conversation

JulesFaucherre
Copy link
Contributor

Jira ticket

Changes

  • Refactor of the context API so that it can use either orgID or vcsType / orgName to reference an org. The information is passed upon creation of the context API client.
  • Allow all commands of circleci context to use the --org-id.
  • Commands circleci context store-secret, circleci context remove-secret, circleci context create and circleci context delete show a text line upon success to inform user.

Rationale

Having these commands use the --org-id parameter was necessary to support standalone projects.

Considerations

The context API had to be adapted to handle using orgID instead of vcsType / orgName. A first approach was already implemented for CreateContext. The solution taken here was to add a method CreateContextWithOrgID.
After some reflexion, we thought that duplicating every call of the interface would not be very clear and would pass the decision of whether to use orgID or vcsType / orgName to the caller. Thus we decided to unite the interface into one that would be instantiated with the information about its org.

Screenshots

N/A.

Copy link
Contributor

@loderunner loderunner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, the design seems great to me.

However I notice a pattern of using pointers a lot. Is there a justification for this that I'm missing? If it's to mark presence or absence of the variable, most of the time, the zero value works just as well, especially with string, where empty string often works just as well as nil (except in cases where empty string makes semantic sense).

If there's a justification, sorry for all the comments, let's discuss so I can understand better.

api/api.go Outdated Show resolved Hide resolved
md_docs/md_docs.go Outdated Show resolved Hide resolved
@@ -129,6 +129,10 @@ func (c *Client) DoRequest(req *http.Request, resp interface{}) (int, error) {
return httpResp.StatusCode, nil
}

func (c *Client) Do(req *http.Request) (*http.Response, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's not your fault that rest.Client is already a leaky abstraction, but it seems that wherever you call Do, you're doing things very similarly to DoRequest.
Is there a way to re-use, maybe change, the existing DoRequest method, rather than creating a new, leakier method?

api/api.go Outdated Show resolved Hide resolved
api/context/gql.go Outdated Show resolved Hide resolved
api/context/rest.go Outdated Show resolved Hide resolved
api/context/rest.go Outdated Show resolved Hide resolved
api/context/rest.go Outdated Show resolved Hide resolved
}

// createListContextsParams is a helper to create ListContextsParams from the content of the ContextRestClient
func (c *restClient) createListContextsParams() (*ListContextsWithRestParams, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why return a pointer, here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, as a Go practice I like to return a unusable value in case of error, returning a pointer here allow to return nil in case of error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what the zero value is for. Returning nil is just returning the zero value of a pointer type.

For example, here, in the Go tutorial, the func return "", errors.New(). Other examples here and here from the Go sources.

Comment on lines 189 to 148
type ListEnvVarsWithRestParams struct {
ContextID string
PageToken *string
}

type listEnvVarsResponse struct {
Items []EnvironmentVariable `json:"items"`
NextPageToken *string `json:"next_page_token"`
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think even your page tokens could be defined as string. In many cases, checking against "" works just as well as checking against nil, with less complexity.

Copy link
Contributor

@loderunner loderunner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Started a discussion that I think we could take offline. Resolve it and you can merge.

NextPageToken string `json:"next_page_token"`
}

func ListEnvVarsWithRest(client *rest.Client, params ListEnvVarsWithRestParams) (*listEnvVarsResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could generalize for all return values. No need to return a pointer if you don't have anything hard to copy in the struct (like a mutex, a file or a network connection), or if you know the copying is a performance bottleneck. In many cases, not making it a pointer even lets the compiler do some cool optimizations.

Also, I think the return type should be exported, since it's returned from an exported func, and has exported fields.

Suggested change
func ListEnvVarsWithRest(client *rest.Client, params ListEnvVarsWithRestParams) (*listEnvVarsResponse, error) {
func ListEnvVarsWithRest(client *rest.Client, params ListEnvVarsWithRestParams) (ListEnvVarsResponse, error) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment has sent me down a rabbit hole of when to:

  • use pointers as receivers
  • pass pointers as arguments
  • return pointers from functions

Opinions vary, and many people inject different semantics into the simple * in Go. Optionals, mutability, pass-by-reference... Very different from Rust and its explicit mut, & references and std::option, or C++ and its many types of "smart pointers". We should probably take this offline and establish our own guide of "Pointer usage in Go". Hell, we could probably make a blog post out of it.

Do as you like for pointer types. You can resolve this, let's chat later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I don't know but maybe someone in CCI wrote some go practices for the company.

Refactored the context api abstraction. You can now access function to
request contexts directly or use an abstraction that handles server
without v2 API.
@JulesFaucherre JulesFaucherre merged commit 583e145 into develop Feb 28, 2024
1 check passed
@JulesFaucherre JulesFaucherre deleted the DEVEX-1216 branch February 28, 2024 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants