Skip to content

Commit

Permalink
Multiple changes:
Browse files Browse the repository at this point in the history
Introduce Config type to configure startService with variadic arguments.
Add godoc to all public types and functions.
Rename multiple config parsers for better readability.
Add first godoc example.
Add more samples.
Add LICENSE.
Add README.md
Fix various nil pointer reference errors.
  • Loading branch information
fussel178 committed Jan 3, 2024
1 parent 98dcdfc commit 28476e3
Show file tree
Hide file tree
Showing 15 changed files with 437 additions and 81 deletions.
21 changes: 21 additions & 0 deletions backend-go/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 WüSpace e. V.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
71 changes: 71 additions & 0 deletions backend-go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Telestion Service Framework for Go

[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.10407142.svg)](https://doi.org/10.5281/zenodo.10407142)
![GitHub License](https://img.shields.io/github/license/wuespace/telestion)
[![Go Reference](https://pkg.go.dev/badge/github.com/wuespace/telestion/backend.svg)](https://pkg.go.dev/github.com/wuespace/telestion/backend)

This library provides a framework for building Telestion services in Go.

## Installation

Install the library via `go get`:

```shell
go get -u github.com/wuespace/telestion/backend@latest
```

## Basic Usage

```go
package main

import (
"github.com/wuespace/telestion/backend"
"log"
)

type Person struct {
Name string `json:"name"`
Address string `json:"address"`
}

func main() {
// start a new Telestion service
service, err := telestion.StartService()
if err != nil {
log.Fatal(err)
}
log.Println("Service started")

// publish a message on the message bus
service.Nc.Publish("my-topic", []byte("Hello from Go!"))

// subscribe to receive messages from the message bus
// automatically unmarshal JSON message to go struct
_, err = service.NcJson.Subscribe("registered-person-topic", func(person *Person) {
log.Println("Received new personal information:", person)
})
if err != nil {
log.Println(err)
}

// wait for interrupts to prevent immediate shutdown of service
telestion.WaitForInterrupt()

// drain remaining messages and close connection
if err1, err2 := service.Drain(); err1 != nil || err2 != nil {
log.Fatal(err1, err2)
}
}
```

## Behavior Specification

The behavior of this library is specified in
the [Behavior Specification](https://docs.telestion.wuespace.de/Backend%20Development/service-behavior/).
This specification is also used to test the library.
The source code of the tests can be found in the repository under `/backend-features`.

## License

This project is licensed under the terms of the [MIT license](LICENSE).
48 changes: 34 additions & 14 deletions backend-go/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type minimalConfig struct {
DataDir string `mapstructure:"DATA_DIR"`
}

// Checks if the untyped map contains all required config parameters to successfully start the service.
func checkMinimalConfig(mapping map[string]any) error {
mConf := minimalConfig{}

Expand All @@ -42,6 +43,7 @@ func checkMinimalConfig(mapping map[string]any) error {
return nil
}

// Parses an untyped map into a service configuration.
func parseConfig(mapping *map[string]any) (*Config, error) {
config := Config{}

Expand All @@ -66,47 +68,60 @@ func parseConfig(mapping *map[string]any) (*Config, error) {
return &config, nil
}

func assembleConfig() (*Config, error) {
// Loads and parses the service [Config] from different configuration sources in the following order:
//
// 1. `overwriteArgs`
// 2. command line arguments
// 3. environment variables
// 4. default configuration, if `--dev` is passed in the steps from above
// 5. configuration file, if `CONFIG_FILE` parameter is defined, readable and parsable
func assembleConfig(overwriteArgs map[string]string) (*Config, error) {
config := &map[string]any{}
mergeMap(config, consoleArgs())
//log.Printf("After console args: %+v", config)
mergeMap(config, envs())
//log.Printf("After env: %+v", config)

// add config params from passed service options
addMissing(config, &overwriteArgs)
// add config params from command line arguments
addMissing(config, cliConfig())
// add config params from environment variables
addMissing(config, envConfig())

// add default config if "dev" configuration is defined
if dev, ok := (*config)["DEV"].(bool); ok && dev {
dc, err := defaultConfig()
if err != nil {
return nil, err
}
mergeMap(config, dc)
addMissing(config, dc)
}
//log.Printf("After default config: %+v", config)

// add config file parameters if "CONFIG_FILE" is defined and readable
if configPath, ok := (*config)["CONFIG_FILE"].(string); ok && len(configPath) != 0 {
fc, err := fileConfig(configPath)
if err != nil {
return nil, err
}
mergeMap(config, fc)
addMissing(config, fc)
}
//log.Printf("After file config: %+v", config)

// verify if configuration is valid
if err := checkMinimalConfig(*config); err != nil {
return nil, err
}

return parseConfig(config)
}

func mergeMap[V any | string](config *map[string]any, updates *map[string]V) {
// Adds entries from updates to mapping that don't exist in mapping.
func addMissing[V any | string](mapping *map[string]any, updates *map[string]V) {
for k, v := range *updates {
if _, contained := (*config)[k]; !contained {
(*config)[k] = v
if _, contained := (*mapping)[k]; !contained {
(*mapping)[k] = v
}
}
}

func consoleArgs() *map[string]any {
// Parses the console arguments and returns a map that holds the configuration parameters.
func cliConfig() *map[string]any {
// setup flags
var (
devFlag bool
Expand Down Expand Up @@ -182,7 +197,8 @@ func isFlagPassed(name string) bool {
return passed
}

func envs() *map[string]string {
// Read the environment variables and provides them as map ready to be included in the service config.
func envConfig() *map[string]string {
result := make(map[string]string, len(os.Environ()))
for _, entry := range os.Environ() {
if key, value, ok := strings.Cut(entry, "="); ok {
Expand All @@ -193,6 +209,8 @@ func envs() *map[string]string {
return &result
}

// Tries to read the configuration file and returns the content as untyped map.
// Fails, if the config file is not readable or if the content is not JSON parsable.
func fileConfig(configPath string) (*map[string]any, error) {
// Note that the file config is supposed to be a json config
jsonConfig := map[string]any{}
Expand All @@ -211,6 +229,8 @@ func fileConfig(configPath string) (*map[string]any, error) {
return &jsonConfig, nil
}

// Returns the default configuration for development purposes.
// Fails, if the process is not allowed to determine the current working directory.
func defaultConfig() (*map[string]string, error) {
dataDir, err := filepath.Abs("data")
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions backend-go/example_lib_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package telestion_test
24 changes: 24 additions & 0 deletions backend-go/example_util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package telestion_test

import (
"github.com/nats-io/nats.go"
"github.com/wuespace/telestion/backend"
)

// The WaitForInterrupt function waits for an external interrupt
// to prevent immediate shutdown of the service.
func ExampleWaitForInterrupt() {
// start a new Telestion service
service, _ := telestion.StartService()

// subscribe to a subject on the message bus
_, _ = service.Nc.Subscribe("subject", func(msg *nats.Msg) {
//...
})

// wait for external interrupt to prevent immediate shutdown of service
telestion.WaitForInterrupt()

// drain NATS connections
_, _ = service.Drain()
}
2 changes: 1 addition & 1 deletion backend-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ module github.com/wuespace/telestion/backend
go 1.21.4

require (
github.com/cucumber/godog v0.13.0
github.com/mitchellh/mapstructure v1.5.0
github.com/nats-io/nats.go v1.31.0
)

require (
github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect
github.com/cucumber/godog v0.13.0 // indirect
github.com/cucumber/messages/go/v21 v21.0.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
Expand Down
13 changes: 5 additions & 8 deletions backend-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM
github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s=
github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
Expand All @@ -18,14 +19,13 @@ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c=
github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
Expand All @@ -35,12 +35,11 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
github.com/nats-io/nkeys v0.4.5 h1:Zdz2BUlFm4fJlierwvGK+yl20IAKUm7eV6AAZXEhkPk=
github.com/nats-io/nkeys v0.4.5/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
Expand All @@ -52,16 +51,14 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 28476e3

Please sign in to comment.