-
Notifications
You must be signed in to change notification settings - Fork 5
Description
Is your feature request related to a problem? Please describe.
The project is too nested and since it's only a package that's intended to be imported by anyone who wants to interact with RabbitMQ in Go, the core functionality should be in the root of the module.
So when you want to use this module, you simply import github.com/rabbitmq/rabbitmq-amqp-go-client
instead of github.com/rabbitmq/rabbitmq-amqp-go-client/pkg/rabbitmqamqp
.
Describe the solution you'd like
I recommend that you flatten it so that it's more idiomatic and easier to read for anyone who isn't a maintainer.
The examples that currently reside in docs/examples
, should instead be written as examples — as documented in the Go documentation.
Examples are explained here: https://go.dev/blog/examples
The benefit of writing examples as shown by the Go blog article is that they show up as examples in the documentation generated from your code.
So instead of users having to go to the git repository, they can directly see the examples in the generated documentation
So if you flatten the project and name the package like this:
package rabbitmqamqp
Then test files that exclusively test exported functionality as well as example code should be in _test.go
files that have the package name suffix _test
, e.g
Filename example tls_example_test.go
:
package rabbitmqamqp_test
func ExampleTLS() {
// to run the example you can use the certificates from the rabbitmq-amqp-go-client
// inside the directory .ci/certs
caCert, err := os.ReadFile("/path/ca_certificate.pem")
if err != nil {
panic(err) // handling omitted for brevity.
}
// Create a CA certificate pool and add the CA certificate to it
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Load client cert
clientCert, err := tls.LoadX509KeyPair("/path//client_localhost_certificate.pem", "/path//client_localhost_key.pem")
if err != nil {
panic(err) // handling omitted for brevity.
}
// Create a TLS configuration
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: caCertPool,
InsecureSkipVerify: false,
ServerName: "localhost", // the server name should match the name on the certificate
}
env := rmq.NewClusterEnvironment([]rmq.Endpoint{
{
Address: "amqps://localhost:5671",
Options: &rmq.AmqpConnOptions{
SASLType: amqp.SASLTypeAnonymous(),
TLSConfig: tlsConfig,
},
},
})
connection, err := env.NewConnection(context.TODO())
if err != nil {
panic(err) // handling omitted for brevity.
}
// Close the connection
connection.Close(context.TODO())
if err != nil {
panic(err) // handling omitted for brevity.
}
}
This code will become part of the generated documentation. It doesn't have to be a runnable example. Examples that contain the special output comment syntax can be used as tests as well, and become runnable directly on the documentation page and are automatically run by go test
.
Key points:
- Put the primary functionality up front in the root of the module (git repo) and only put extra optional features that are you want to separate from the package in the module root into sub-packages (not nested inside
pkg/
). - Make examples part of the generated documentation that gets published on
go.dev
so that they are next to the actual documentation. - The folder name
pkg
has no meaning and only adds to the noise of the module. It's a bad habit brought by others that come from other languages, into Go projects and it was never part of any official documentation or recommendation. - Hide any form of helpers that you don't want others to import as a sub-package inside
internal/
such asinternal/test-helper
. This makes it so that only this project can use it and you're free to make any breaking changes to it since no other projects can import it. - Helpers used inside tests should accept
t *testing.T
as its first argument andt.Helper()
should be called before any code is executed in said helpers. Helpers can fail the tests if something goes wrong. - Do not use
context.Background()
in examples or tests. Examples should usecontext.TODO()
to indicate that it should be something that the developer has to provide and not the background context. Unit tests that use context should uset.Context()
. - Unit tests should be actual functions that contain 1 or more test cases. Runnable examples are great for simple tests that make assertions as well as show functionality. Otherwise, table driven tests are the recommended approach.
- Do not try to replicate the design patterns and naming conventions used in other languages. I don't bring Go patterns to Java or Python for example. You get the cleanest and most user friendly code if you write code that's tailored to the target audience and ecosystem.
It's worth making these breaking changes when the module is still young and hasn't reached a major release yet.
Describe alternatives you've considered
No response
Additional context
No response