Skip to main content
added 103 characters in body; edited tags
Source Link
200_success
  • 144.2k
  • 22
  • 188
  • 473

The project can be found in this repository on Githubrepository on Github.

This makes it really hard to figure out which flags belong to which command and it's easy to re-initialize the same variable. See https://github.com/michaellihs/golab/blob/master/cmd/group.go#L31group.go, line 31 for the full example.

The project can be found in this repository on Github.

This makes it really hard to figure out which flags belong to which command and it's easy to re-initialize the same variable. See https://github.com/michaellihs/golab/blob/master/cmd/group.go#L31 for the full example.

The project can be found in this repository on Github.

This makes it really hard to figure out which flags belong to which command and it's easy to re-initialize the same variable. See group.go, line 31 for the full example.

Source Link

Breakable Toy: Go CLI for Gitlab

I started to learn programming in Go and came up with a Breakable Toy, which is a CLI for Gitlab. To get something up and running fast, I used some Go libraries:

  • Ginkgo for testing
  • Cobra for basic CLI stuff
  • Sling for HTTP requests and JSON mapping

The project can be found in this repository on Github.

I am looking for any feedback / code review that could help me to better understand the concepts and conventions of Go. I come from the Java world and have the impression that I try to stick too much on the object oriented world. So any feedback concerning code style and best-practices is highly appreciated.

Besides general feedback, here are some specific questions:

1. Error Handling in Go

Being used to the try catch mechanism in Java, the _, err := mechanism in Go seems a little unhandy to me. To me it looks as if I have to implement the "bubble up" of exceptions all by myself. Example:

// HTTP client
func (client *GitlabClient) Do(req *http.Request, value interface{}) (*http.Response, error) {
    resp, err := client.sling.Do(req, value, nil)
    return resp, err
}

// service
func (service *ProjectsService) List() (*[]model.Project, error) {
    // ...
    _, err = service.Client.Do(req, projects)
    if err != nil {
        return nil, err
    }
    return projects, nil
}

// CLI command 
func(cmd *cobra.Command, args []string) error {
    projects, err := gitlabClient.Projects.List()
    if err != nil {
        return err
    }
    err = OutputJson(projects)
    return err
}

so there are really many LOCs only for error handling - is that the "way to go"?

2. Global Variables

The Cobra library seems to store values for CLI flags in global variables:

var id string

// ...

projectGetCmd.PersistentFlags().StringVarP(&id, "id", "i", "", "(required) Either ID of project or 'namespace/project-name'")
viper.BindPFlag("id", projectGetCmd.PersistentFlags().Lookup("id"))

This makes it really hard to figure out which flags belong to which command and it's easy to re-initialize the same variable. See https://github.com/michaellihs/golab/blob/master/cmd/group.go#L31 for the full example.

Is there any rule of thumb / best practice of how to namespace variables in Go besides structs?

3. Mocking in Unit Tests

I wrote some integration tests with Ginkgo that also simulate an HTTP server. This works quite nicely. But now I wanted to test, whether my CLI commands are calling the expected service methods and therefore I wanted to mock the services classes in the Unit tests for the commands. I couldn't find a convincing way for how to mock objects in Go. Can you give me any hint / resources for that?

Disclaimer

I am not expecting a full code review of my application. But if you have any suggestions for improvement, I'd be glad the get them.