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

Stop generating broker code #66

Merged
merged 4 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 116 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Generate Go client and server boilerplate from AsyncAPI specifications.
## Contents

* [Supported functionalities](#supported-functionalities)
* [Usage](#usage)
* [Concepts](#concepts)
* [Examples](#examples):
* [Basic example](#basic-example)
Expand All @@ -31,6 +32,31 @@ Generate Go client and server boilerplate from AsyncAPI specifications.
* Logging:
* JSON (ECS compatible)

## Usage

In order to use this library in your code, please execute the following lines:

```shell
# Install the tool
go install github.com/lerenn/asyncapi-codegen/cmd/asyncapi-codegen@latest

# Generate the code from the asyncapi file
asyncapi-codegen -i ./asyncapi.yaml -p <your-package> -o ./asyncapi.gen.go

# Install dependencies needed by the generated code
go get -u github.com/lerenn/asyncapi-codegen/pkg/broker
go get -u github.com/lerenn/asyncapi-codegen/pkg/context
go get -u github.com/lerenn/asyncapi-codegen/pkg/log
go get -u github.com/lerenn/asyncapi-codegen/pkg/middleware
```

You can also specify the generation part by adding a `go generate` instruction
at the beginning of your file:

```golang
//go:generate go run github.com/lerenn/asyncapi-codegen/cmd/asyncapi-codegen@<version> -i ./asyncapi.yaml -p <your-package> -o ./asyncapi.gen.go
```

## Concepts

![basic schema](assets/basic-schema.svg)
Expand All @@ -55,9 +81,9 @@ the client, the broker, and the application.
* <span style="color:red">Red parts</span>: you will need to fill these parts
between client, broker and application. These will allow message production and
reception with the generated code.
* <span style="color:orange">Orange parts</span>: these will be also generated
automatically if you use an implemented message broker. You can also use the
`none` type in order to implement it yourself.
* <span style="color:orange">Orange parts</span>: these parts will be available
in this repository if you use an already supported broker. However, you can also
use the implement it yourself if the broker is not supported yet.

## Examples

Expand Down Expand Up @@ -141,11 +167,16 @@ And here is an example of the application that could be written to use this gene
code with NATS (you can also find it [here](./examples/helloworld/app/main.go)):

```go
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

// Connect to NATS
nc, _ := nats.Connect("nats://nats:4222")

// Create a new application controller
ctrl, _ := generated.NewAppController(generated.NewNATSController(nc))
ctrl, _ := generated.NewAppController(controllers.NewNATS(nc))
defer ctrl.Close(context.Background())

// Subscribe to HelloWorld messages
Expand Down Expand Up @@ -188,11 +219,16 @@ And here is an example of the client that could be written to use this generated
code with NATS (you can also find it [here](./examples/helloworld/app/main.go)):

```go
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

// Connect to NATS
nc, _ := nats.Connect("nats://nats:4222")

// Create a new application controller
ctrl, _ := generated.NewClientController(generated.NewNATSController(nc))
ctrl, _ := generated.NewClientController(controllers.NewNATS(nc))
defer ctrl.Close(context.Background())

// Send HelloWorld
Expand All @@ -214,26 +250,6 @@ type HelloMessage struct {
}
```

#### Broker

In order to connect your application and your client to your broker, we need to
provide an adapter to it. Here is the interface that you need to satisfy:

```go
type BrokerController interface {
// Publish will be called under the hood by any PublishXXX function
Publish(ctx context.Context, channel string, mw UniversalMessage) error
// Subscribe will be called under the hood by any SubscribeXXX function
Subscribe(ctx context.Context, channel string) (msgs chan UniversalMessage, stop chan interface{}, err error)
}
```

You can find that there is an `UniversalMessage` structure that is provided and
that aims to abstract the event broker technology.

You can either generate an already existing adapter, or write your own if it doesn't
exists or if it doesn't suit your needs.

### Request/Response example

This example will use a `ping` example that you can find
Expand All @@ -259,6 +275,11 @@ use `ping.gen.go`.
#### Application (or server in this case)

```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

type ServerSubscriber struct {
Controller *generated.AppController
}
Expand All @@ -278,7 +299,7 @@ func main() {
/* ... */

// Create a new server controller
ctrl, _ := generated.NewAppController(generated.NewNATSController(nc))
ctrl, _ := generated.NewAppController(controllers.NewNATS(nc))
defer ctrl.Close(context.Background())

// Subscribe to all (we could also have just listened on the ping request channel)
Expand Down Expand Up @@ -319,8 +340,8 @@ resp, _ := ctrl.WaitForPong(context.Background(), req, publicationFunc)
## CLI options

The default options for oapi-codegen will generate everything; client, application,
broker, type definitions, and broker implementations but you can generate subsets
of those via the -generate flag. It defaults to client,application,broker,types
and type definitions but you can generate subsets of those via the -generate
flag. It defaults to client,application,types
but you can specify any combination of those.

Here are the universal parts that you can generate:
Expand All @@ -329,16 +350,10 @@ Here are the universal parts that you can generate:
the types in the same package to compile.
* `client`: generate the client boilerplate. It, too, requires the types to be
present in its package.
* `broker`: generate the broker controller that you have to fill either with an
existing implementation (more below), or by implementing your own.
* `types`: all type definitions for all types in the AsyncAPI spec.
This will be everything under `#components`, as well as request parameter,
request body, and response type objects.

You can also specify some specific implementation for the broker of your choice:

* `nats`: generate the NATS message broker boilerplate.

## Advanced topics

### Middlewares
Expand All @@ -348,8 +363,13 @@ messages. You can add one or multiple middlewares using the following function
on a controller:

```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

// Create a new app controller with a NATS controller for example
ctrl, _ := generated.NewAppController(generated.NewNATSController(nc))
ctrl, _ := generated.NewAppController(controllers.NewNATS(nc))

// Add middleware
ctrl.AddMiddlewares(myMiddleware1, myMiddleware2 /*, ... */)
Expand All @@ -371,6 +391,7 @@ If you want to target specific messages, you can use the context passed in argum
```golang
import(
apiContext "github.com/lerenn/asyncapi-codegen/pkg/context"
/* ... */
)

func myMiddleware(ctx context.Context, _ middleware.Next) context.Context {
Expand Down Expand Up @@ -439,8 +460,13 @@ To log internal operation of the controller, the only thing you have to do is
to set a logger to your controller with the function `SetLogger()`:

```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

// Create a new app controller with a NATS controller for example
ctrl, _ := generated.NewAppController(generated.NewNATSController(nc))
ctrl, _ := generated.NewAppController(controllers.NewNATS(nc))

// Attach a logger (optional)
// You can find loggers in `github.com/lerenn/asyncapi-codegen/pkg/log` or create your own
Expand All @@ -456,8 +482,13 @@ To log published and received messages, you'll have to pass a logger as a middle
in order to execute it on every published and received messages:

```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

// Create a new app controller with a NATS controller for example
ctrl, _ := generated.NewAppController(generated.NewNATSController(nc))
ctrl, _ := generated.NewAppController(controllers.NewNATS(nc))

// Add middleware
ctrl.AddMiddlewares(middleware.Logging(log.NewECS()))
Expand Down Expand Up @@ -503,8 +534,13 @@ func (logger SimpleLogger) Error(ctx log.Context, msg string, info ...log.Additi
You can then create a controller with a logger using similar lines:

```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

// Create a new app controller with a NATS controller for example
ctrl, _ := generated.NewAppController(generated.NewNATSController(nc))
ctrl, _ := generated.NewAppController(controllers.NewNATS(nc))

// Set a logger
ctrl.SetLogger(SimpleLogger{})
Expand All @@ -521,9 +557,50 @@ applications which uses code generated by `asyncapi-codegen` but on different
queues:

```golang
broker.SetQueueName("my-custom-queue-name")
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker/controllers"
/* ... */
)

// Generate a new NATS controller
ctrl := controllers.NewNATS(nc)

// Set queue name on the NATS controller
ctrl.SetQueueName("my-custom-queue-name")
```

### Implementing your own broker controller

In order to connect your application and your client to your broker, we need to
provide an adapter to it. Here is the interface that you need to satisfy:

```go
import(
"github.com/lerenn/asyncapi-codegen/pkg/broker"
"github.com/lerenn/asyncapi-codegen/pkg/log"
)

type BrokerController interface {
// SetLogger set a logger that will log operations on broker controller
SetLogger(logger log.Interface)

// Publish a message to the broker
Publish(ctx context.Context, channel string, mw broker.Message) error

// Subscribe to messages from the broker
Subscribe(ctx context.Context, channel string) (msgs chan broker.Message, stop chan interface{}, err error)

// SetQueueName sets the name of the queue that will be used by the broker
SetQueueName(name string)
}
```

You can find that there is an `broker.Message` structure that is provided and
that aims to abstract the event broker technology.

By writing your own by satisfying this interface, you will be able to connect
your broker to the generated code.

## Contributing and support

If you find any bug or lacking a feature, please raise an issue on the Github repository!
Expand Down
8 changes: 1 addition & 7 deletions cmd/asyncapi-codegen/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func ProcessFlags() Flags {
flag.StringVar(&f.InputPath, "i", "asyncapi.yaml", "AsyncAPI specification file to use")
flag.StringVar(&f.OutputPath, "o", "asyncapi.gen.go", "Destination file")
flag.StringVar(&f.PackageName, "p", "asyncapi", "Golang package name")
flag.StringVar(&f.Generate, "g", "client,application,broker,types", "Generation options")
flag.StringVar(&f.Generate, "g", "client,application,types", "Generation options")
flag.BoolVar(&f.DisableFormatting, "disable-formatting", false, "Disables the code generation formatting")

flag.Parse()
Expand All @@ -64,18 +64,12 @@ func (f Flags) ToCodegenOptions() (codegen.Options, error) {
gens := strings.Split(f.Generate, ",")
for _, v := range gens {
switch v {
// Generic code
case "application":
opt.Generate.Application = true
case "broker":
opt.Generate.Broker = true
case "client":
opt.Generate.Client = true
case "types":
opt.Generate.Types = true
// Broker implementations
case "nats":
opt.Generate.NATS = true
default:
return opt, fmt.Errorf("%w: %q", ErrInvalidGenerate, v)
}
Expand Down
19 changes: 10 additions & 9 deletions examples/helloworld/app/generated/app.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading