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

Add multi version support #91

Merged
merged 6 commits into from
Sep 20, 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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ lint/fix: ## Fix what can be fixed regarding the linter
clean: __examples/clean brokers/down ## Clean up the project

.PHONY: check
check: clean generate lint examples test ## Check that everything is ready for commit
check: generate lint clean examples test ## Check that everything is ready for commit

.PHONY: __examples/clean
__examples/clean:
Expand All @@ -34,7 +34,7 @@ examples: brokers/up ## Perform examples

.PHONY: test
test: brokers/up ## Perform tests
@go test ./... -timeout=30s
@go test ./... -p 1 -timeout=1m

.PHONY: generate
generate: ## Generate files
Expand Down
160 changes: 126 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Generate Go application and user boilerplate from AsyncAPI specifications.
* [Custom broker](#custom-broker)
* [CLI options](#cli-options)
* [Advanced topics](#advanced-topics)
* [Middlewares](#middlewares)
* [Context](#context)
* [Logging](#logging)
* [Versioning](#versioning)
* [AsyncAPI Extensions](#asyncapi-extensions)
* [Contributing and support](#contributing-and-support)

## Supported functionalities
Expand All @@ -37,6 +42,8 @@ Generate Go application and user boilerplate from AsyncAPI specifications.
* Elastic Common Schema (JSON)
* Text (Humand readable)
* Custom
* Others:
* Versioning support

## Usage

Expand Down Expand Up @@ -172,22 +179,24 @@ code with NATS (you can also find it [here](./examples/helloworld/nats/app/main.
```go
import(
"github.com/lerenn/asyncapi-codegen/pkg/extensions/brokers/nats"
/* ... */
// ...
)

// Create a new application controller
ctrl, _ := NewAppController(nats.NewController("nats://nats:4222"))
defer ctrl.Close(context.Background())

// Subscribe to HelloWorld messages
// Note: it will indefinitely wait for messages as context has no timeout
log.Println("Subscribe to hello world...")
ctrl.SubscribeHello(context.Background(), func(_ context.Context, msg HelloMessage, _ bool) {
log.Println("Received message:", msg.Payload)
})

// Process messages until interruption signal
/* ... */
func main() {
// Create a new application controller
ctrl, _ := NewAppController(nats.NewController("nats://nats:4222"))
defer ctrl.Close(context.Background())

// Subscribe to HelloWorld messages
// Note: it will indefinitely wait for messages as context has no timeout
log.Println("Subscribe to hello world...")
ctrl.SubscribeHello(context.Background(), func(_ context.Context, msg HelloMessage, _ bool) {
log.Println("Received message:", msg.Payload)
})

// Process messages until interruption signal
// ...
}
```

#### User
Expand Down Expand Up @@ -221,16 +230,20 @@ code with NATS (you can also find it [here](./examples/helloworld/nats/app/main.
```go
import(
"github.com/lerenn/asyncapi-codegen/pkg/extensions/brokers/nats"
/* ... */
// ...
)

// Create a new user controller
ctrl, _ := NewUserController(nats.NewController("nats://nats:4222"))
defer ctrl.Close(context.Background())
func main() {
// Create a new user controller
ctrl, _ := NewUserController(nats.NewController("nats://nats:4222"))
defer ctrl.Close(context.Background())

// Send HelloWorld
log.Println("Publishing 'hello world' message")
ctrl.PublishHello(context.Background(), HelloMessage{Payload: "HelloWorld!"})
// Send HelloWorld
log.Println("Publishing 'hello world' message")
ctrl.PublishHello(context.Background(), HelloMessage{Payload: "HelloWorld!"})

// ...
}
```

#### Types
Expand Down Expand Up @@ -288,7 +301,7 @@ func (s Subscriber) Ping(req PingMessage, _ bool) {
}

func main() {
/* ... */
// ...

// Create a new application controller
ctrl, _ := NewAppController(/* Add corresponding broker controller */)
Expand All @@ -299,7 +312,7 @@ func main() {
ctrl.SubscribeAll(context.Background(), sub)

// Process messages until interruption signal
/* ... */
// ...
}
```

Expand Down Expand Up @@ -379,7 +392,7 @@ type BrokerController interface {
Publish(ctx context.Context, channel string, mw extensions.BrokerMessage) error

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

Expand Down Expand Up @@ -435,7 +448,7 @@ If you want to target specific messages, you can use the context passed in argum
```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/extensions"
/* ... */
// ...
)

func myMiddleware(ctx context.Context, _ middleware.Next) context.Context {
Expand All @@ -462,7 +475,7 @@ Here is an example:
```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/extensions"
/* ... */
// ...
)

func surroundingMiddleware(ctx context.Context, next extensions.NextMiddleware) context.Context {
Expand Down Expand Up @@ -511,11 +524,15 @@ to initialize the controller with a logger, with the function `WithLogger()`:
```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/extensions/brokers"
/* ... */
// ...
)

// Create a new app controller with an Elastic Common Schema JSON compatible logger
ctrl, _ := NewAppController(/* Broker of your choice */, WithLogger(log.NewECS()))
func main() {
// Create a new app controller with an Elastic Common Schema JSON compatible logger
ctrl, _ := NewAppController(/* Broker of your choice */, WithLogger(log.NewECS()))

// ...
}
```

You can find all loggers in the directory `pkg/log`.
Expand All @@ -528,12 +545,16 @@ in order to execute it on every published and received messages:
```golang
import(
"github.com/lerenn/asyncapi-codegen/pkg/extensions/brokers"
/* ... */
// ...
)

// Create a new app controller with a middleware for logging incoming/outgoing messages
loggingMiddleware := middleware.Logging(log.NewECS())
ctrl, _ := NewAppController(/* Broker of your choice */, WithMiddlewares(loggingMiddleware))
func main() {
// Create a new app controller with a middleware for logging incoming/outgoing messages
loggingMiddleware := middleware.Logging(log.NewECS())
ctrl, _ := NewAppController(/* Broker of your choice */, WithMiddlewares(loggingMiddleware))

// ...
}
```

#### Custom logging
Expand Down Expand Up @@ -584,7 +605,78 @@ ctrl, _ := NewAppController(
)
```

### Extensions
### Versioning

If you are in need to do a migration or support multiple versions of your
AsyncAPI specifications, you can use the `versioning` package:

```golang

import (
"github.com/lerenn/asyncapi-codegen/pkg/extensions/brokers/nats"
"github.com/lerenn/asyncapi-codegen/pkg/extensions/versioning"
v1 "path/to/asyncapi/spec/version/1"
v2 "path/to/asyncapi/spec/version/2"
)

func main() {
// Create a broker (here from NATS)
broker := nats.NewController("nats://nats:4222"))

// Add a version wrapper to the broker
vw := versioning.NewWrapper(broker)

// Create application for version 1
appV1, _ := v1.NewAppController(vw, /* controller options */)
defer appV1.Close(context.Background())

// Create v2 app
appV2, _ := v2.NewAppController(vw, /* controller options */)
defer appV2.Close(context.Background())

// ...
}
```

Then you can use each application independently:

```golang
err := appV1.SubscribeHello(context.Background(), func(_ context.Context, msg v1.HelloMessage, _ bool) {
// Stuff for version 1
})

err := appV2.SubscribeHello(context.Background(), func(_ context.Context, msg v2.HelloMessage, _ bool) {
// Stuff for version 2
})
```

That way, you can support multiple different versions with the same broker.

#### Version tagging

**Important**: this feature will add an `application-version` header to each
message in order to have the correct version of the application on each of
them.

##### Non-tagged messages

If messages can have no `application-version`, you can use the option `WithDefaultVersion`
to add a default version to non-tagged messages.

```golang
vw := versioning.NewWrapper(broker, versioning.WithDefaultVersion("1.1.4"))
```

##### Change header key for application version

Also, if you don't want to use this header as a recipient to the application version,
you can specify your own header with the option `WithVersionHeaderKey`.

```golang
vw := versioning.NewWrapper(broker, versioning.WithVersionHeaderKey("my-version-key"))
```

### AsyncAPI Extensions

#### Schema Object extensions

Expand Down
5 changes: 3 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '3.5'

services:
kafka:
image: 'bitnami/kafka:latest'
image: 'bitnami/kafka:3.5.1'
ports:
# Listening on a different port to avoid collision with 9092 internal listener
# Prefering to keep 9092 for internal listener as it is the port for examples
Expand All @@ -19,7 +19,8 @@ services:
networks:
- brokers
nats:
image: nats:alpine
image: nats:2.10
command: ["-V"]
ports:
- 4222:4222
networks:
Expand Down
Loading