Add Dispatcher implementation for CQRS pattern
Some checks failed
Go CI/CD / go-ci (push) Has been cancelled
Some checks failed
Go CI/CD / go-ci (push) Has been cancelled
Introduced a Dispatcher to manage command, query, and event handlers in a thread-safe manner utilizing read-write mutexes. This includes handler registration and dispatching logic, error handling for unregistered handlers, and support for concurrent operations. Added comprehensive tests for handler registration, dispatching, and error scenarios.
This commit is contained in:
parent
1cbda29b36
commit
3d2e5ec027
50
.gitea/workflows/ci-basic.yml
Normal file
50
.gitea/workflows/ci-basic.yml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: Go CI/CD
|
||||||
|
run-name: ${{ github.actor }} is running CI/CD basic
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- main
|
||||||
|
- release/**
|
||||||
|
- develop
|
||||||
|
pull_request:
|
||||||
|
branches-ignore:
|
||||||
|
- main
|
||||||
|
- release/**
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
go-ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.24'
|
||||||
|
|
||||||
|
- name: Download dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go mod tidy -x
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go test -json > test-report.out
|
||||||
|
go test -coverprofile=coverage.out
|
||||||
|
|
||||||
|
- name: SonarQube Analysis
|
||||||
|
uses: SonarSource/sonarqube-scan-action@v5
|
||||||
|
env:
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
||||||
|
|
||||||
|
- name: Build binary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go build ./...
|
50
.gitea/workflows/ci-protected.yml
Normal file
50
.gitea/workflows/ci-protected.yml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: Go CI/CD
|
||||||
|
run-name: ${{ github.actor }} is running CI/CD protected
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- release/**
|
||||||
|
- develop
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- release/**
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
go-ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.24'
|
||||||
|
|
||||||
|
- name: Download dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go mod tidy -x
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go test -json > test-report.out
|
||||||
|
go test -coverprofile=coverage.out
|
||||||
|
|
||||||
|
- name: SonarQube Analysis
|
||||||
|
uses: SonarSource/sonarqube-scan-action@v5
|
||||||
|
env:
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
||||||
|
|
||||||
|
- name: Build binary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go build ./...
|
43
.gitea/workflows/ci-tag.yml
Normal file
43
.gitea/workflows/ci-tag.yml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: Go CI/CD
|
||||||
|
run-name: ${{ github.actor }} is running CI/CD Tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
go-ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.24'
|
||||||
|
|
||||||
|
- name: Download dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go mod tidy -x
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go test -json > test-report.out
|
||||||
|
go test -coverprofile=coverage.out
|
||||||
|
|
||||||
|
- name: SonarQube Analysis
|
||||||
|
uses: SonarSource/sonarqube-scan-action@v5
|
||||||
|
env:
|
||||||
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
||||||
|
|
||||||
|
- name: Build binary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
go build ./...
|
64
.gitignore
vendored
Normal file
64
.gitignore
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
bin
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the 'go tool cover' command
|
||||||
|
*.out
|
||||||
|
coverage.xml
|
||||||
|
test-report.xml
|
||||||
|
|
||||||
|
# Directory for Go modules
|
||||||
|
/vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
||||||
|
# Editor configs
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.log
|
||||||
|
*.viminfo
|
||||||
|
*.un~
|
||||||
|
Session.vim
|
||||||
|
|
||||||
|
# JetBrains Rider specific
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/shelf/
|
||||||
|
|
||||||
|
# Sublime Text specific
|
||||||
|
*.sublime-workspace
|
||||||
|
*.sublime-project
|
||||||
|
|
||||||
|
# VSCode specific
|
||||||
|
.vscode/
|
||||||
|
.vscode/settings.json
|
||||||
|
.vscode/tasks.json
|
||||||
|
.vscode/launch.json
|
||||||
|
|
||||||
|
# Emacs specific
|
||||||
|
*~
|
||||||
|
\#*\#
|
||||||
|
.#*
|
||||||
|
|
||||||
|
# MacOS specific
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Node modules (in case of tools/scripts)
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Python virtual environments (for dev tools/scripts)
|
||||||
|
venv/
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
206
dispatcher.go
Normal file
206
dispatcher.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package stonecqrs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitstormr.dev/stone-utils/stoneerror"
|
||||||
|
)
|
||||||
|
|
||||||
|
// errCommandHandlerNotFound indicates a missing command handler error.
|
||||||
|
// errQueryHandlerNotFound indicates a missing query handler error.
|
||||||
|
// errEventHandlerNotFound indicates a missing event handler error.
|
||||||
|
var (
|
||||||
|
errCommandHandlerNotFound = stoneerror.New(
|
||||||
|
1001, "command handler not found",
|
||||||
|
)
|
||||||
|
errQueryHandlerNotFound = stoneerror.New(1002, "query handler not found")
|
||||||
|
errEventHandlerNotFound = stoneerror.New(1003, "event handler not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command represents an action or operation to be handled by a dispatcher.
|
||||||
|
type Command interface{}
|
||||||
|
|
||||||
|
// Query represents a marker interface used to identify query types.
|
||||||
|
type Query interface{}
|
||||||
|
|
||||||
|
// Event represents a domain event within the application architecture.
|
||||||
|
type Event interface{}
|
||||||
|
|
||||||
|
// CommandHandler defines an interface for handling commands in a CQRS pattern.
|
||||||
|
// Implementations should process commands and return resulting events or errors.
|
||||||
|
// Handle processes the given command and returns any resulting events or error.
|
||||||
|
type CommandHandler interface {
|
||||||
|
Handle(ctx context.Context, cmd Command) ([]Event, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryHandler defines an interface for handling queries within a context.
|
||||||
|
type QueryHandler interface {
|
||||||
|
Handle(ctx context.Context, query Query) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventHandler is an interface for handling specific types of events.
|
||||||
|
// It defines a single method Handle to process an Event with a context.
|
||||||
|
// Handle returns an error if the event handling fails.
|
||||||
|
type EventHandler interface {
|
||||||
|
Handle(ctx context.Context, event Event) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatcher is responsible for managing and dispatching handlers.
|
||||||
|
// It supports command, query, and event handlers concurrently.
|
||||||
|
// Commands, queries, and events are identified by their type names.
|
||||||
|
// Uses a read-write mutex to ensure thread-safe operations.
|
||||||
|
type Dispatcher struct {
|
||||||
|
commandHandlers map[string]CommandHandler
|
||||||
|
queryHandlers map[string]QueryHandler
|
||||||
|
eventHandlers map[string][]EventHandler
|
||||||
|
mutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDispatcher creates and returns a new instance of Dispatcher.
|
||||||
|
func NewDispatcher() *Dispatcher {
|
||||||
|
return &Dispatcher{
|
||||||
|
commandHandlers: make(map[string]CommandHandler),
|
||||||
|
queryHandlers: make(map[string]QueryHandler),
|
||||||
|
eventHandlers: make(map[string][]EventHandler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCommandHandler registers a handler for a specific command type.
|
||||||
|
func (d *Dispatcher) RegisterCommandHandler(
|
||||||
|
cmd Command, handler CommandHandler,
|
||||||
|
) {
|
||||||
|
d.mutex.Lock()
|
||||||
|
defer d.mutex.Unlock()
|
||||||
|
d.commandHandlers[typeName(cmd)] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterQueryHandler registers a handler for the specified query type.
|
||||||
|
func (d *Dispatcher) RegisterQueryHandler(query Query, handler QueryHandler) {
|
||||||
|
d.mutex.Lock()
|
||||||
|
defer d.mutex.Unlock()
|
||||||
|
d.queryHandlers[typeName(query)] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterEventHandler registers a handler for a specific type of event.
|
||||||
|
func (d *Dispatcher) RegisterEventHandler(event Event, handler EventHandler) {
|
||||||
|
d.mutex.Lock()
|
||||||
|
defer d.mutex.Unlock()
|
||||||
|
name := typeName(event)
|
||||||
|
d.eventHandlers[name] = append(d.eventHandlers[name], handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchCommand processes a Command and dispatches the resulting Events.
|
||||||
|
// Returns the generated Events or an error if command handling fails.
|
||||||
|
func (d *Dispatcher) DispatchCommand(ctx context.Context, cmd Command) (
|
||||||
|
[]Event, error,
|
||||||
|
) {
|
||||||
|
handler, err := d.getCommandHandler(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
events, err := handler.Handle(ctx, cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(events) > 0 {
|
||||||
|
if err = d.DispatchEvents(ctx, events...); err != nil {
|
||||||
|
return events, stoneerror.Wrap(
|
||||||
|
err, 1004, "failed to dispatch generated events",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchQuery dispatches a query to its corresponding handler.
|
||||||
|
// It retrieves the query handler and invokes its Handle method.
|
||||||
|
// Returns the result of the query handler or an error if not found.
|
||||||
|
func (d *Dispatcher) DispatchQuery(
|
||||||
|
ctx context.Context, query Query,
|
||||||
|
) (interface{}, error) {
|
||||||
|
handler, err := d.getQueryHandler(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return handler.Handle(ctx, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchEvents dispatches multiple events to their registered handlers.
|
||||||
|
// It processes each event sequentially and stops on the first error.
|
||||||
|
// Returns an error if any event fails to dispatch.
|
||||||
|
func (d *Dispatcher) DispatchEvents(
|
||||||
|
ctx context.Context, events ...Event,
|
||||||
|
) error {
|
||||||
|
for _, event := range events {
|
||||||
|
if err := d.DispatchEvent(ctx, event); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchEvent dispatches an event to all registered event handlers.
|
||||||
|
// It returns an error if any handler fails to process the event.
|
||||||
|
func (d *Dispatcher) DispatchEvent(ctx context.Context, event Event) error {
|
||||||
|
handlers, err := d.getEventHandlers(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, handler := range handlers {
|
||||||
|
if err = handler.Handle(ctx, event); err != nil {
|
||||||
|
return stoneerror.Wrap(err, 1005, "event handling failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCommandHandler retrieves the CommandHandler for the given Command.
|
||||||
|
// Returns an error if no handler is found.
|
||||||
|
func (d *Dispatcher) getCommandHandler(cmd Command) (CommandHandler, error) {
|
||||||
|
d.mutex.RLock()
|
||||||
|
defer d.mutex.RUnlock()
|
||||||
|
|
||||||
|
handler, exists := d.commandHandlers[typeName(cmd)]
|
||||||
|
if !exists {
|
||||||
|
return nil, errCommandHandlerNotFound
|
||||||
|
}
|
||||||
|
return handler, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getQueryHandler retrieves the handler for a specific query type.
|
||||||
|
// It returns an error if the handler is not found.
|
||||||
|
func (d *Dispatcher) getQueryHandler(query Query) (QueryHandler, error) {
|
||||||
|
d.mutex.RLock()
|
||||||
|
defer d.mutex.RUnlock()
|
||||||
|
|
||||||
|
handler, exists := d.queryHandlers[typeName(query)]
|
||||||
|
if !exists {
|
||||||
|
return nil, errQueryHandlerNotFound
|
||||||
|
}
|
||||||
|
return handler, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEventHandlers retrieves all handlers for a specific event type.
|
||||||
|
// Returns an error if no handlers are registered for the event type.
|
||||||
|
func (d *Dispatcher) getEventHandlers(event Event) ([]EventHandler, error) {
|
||||||
|
d.mutex.RLock()
|
||||||
|
defer d.mutex.RUnlock()
|
||||||
|
|
||||||
|
handlers, exists := d.eventHandlers[typeName(event)]
|
||||||
|
if !exists {
|
||||||
|
return nil, errEventHandlerNotFound
|
||||||
|
}
|
||||||
|
return handlers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeName returns the name of the type of the given interface value.
|
||||||
|
func typeName(v interface{}) string {
|
||||||
|
return fmt.Sprintf("%T", v)
|
||||||
|
}
|
112
dispatcher_test.go
Normal file
112
dispatcher_test.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package stonecqrs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testCommand struct {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type testQuery struct {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type testEvent struct {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type unknownEvent struct{}
|
||||||
|
|
||||||
|
type testCommandHandler struct{}
|
||||||
|
|
||||||
|
func (handler *testCommandHandler) Handle(
|
||||||
|
ctx context.Context, cmd Command,
|
||||||
|
) ([]Event, error) {
|
||||||
|
fmt.Println(cmd)
|
||||||
|
return []Event{
|
||||||
|
testEvent{message: "test"},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type testQueryHandler struct{}
|
||||||
|
|
||||||
|
func (handler *testQueryHandler) Handle(
|
||||||
|
ctx context.Context, query Query,
|
||||||
|
) (interface{}, error) {
|
||||||
|
fmt.Println(query)
|
||||||
|
return "test", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type testEventHandler struct{}
|
||||||
|
|
||||||
|
func (handler *testEventHandler) Handle(
|
||||||
|
ctx context.Context, event Event,
|
||||||
|
) error {
|
||||||
|
fmt.Println(event)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_NewDispatcher(t *testing.T) {
|
||||||
|
d := NewDispatcher()
|
||||||
|
if d == nil {
|
||||||
|
t.Fatal("expected non-nil dispatcher")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Register(t *testing.T) {
|
||||||
|
d := NewDispatcher()
|
||||||
|
d.RegisterCommandHandler(testCommand{}, &testCommandHandler{})
|
||||||
|
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
||||||
|
d.RegisterEventHandler(testEvent{}, &testEventHandler{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_DispatchCommandWithEvents(t *testing.T) {
|
||||||
|
d := NewDispatcher()
|
||||||
|
d.RegisterCommandHandler(testCommand{}, &testCommandHandler{})
|
||||||
|
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
||||||
|
d.RegisterEventHandler(testEvent{}, &testEventHandler{})
|
||||||
|
|
||||||
|
_, err := d.DispatchCommand(context.Background(), testCommand{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_DispatchQuery(t *testing.T) {
|
||||||
|
d := NewDispatcher()
|
||||||
|
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
||||||
|
|
||||||
|
_, err := d.DispatchQuery(context.Background(), testQuery{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_UnknownEvent(t *testing.T) {
|
||||||
|
d := NewDispatcher()
|
||||||
|
d.RegisterCommandHandler(testCommand{}, &testCommandHandler{})
|
||||||
|
d.RegisterQueryHandler(testQuery{}, &testQueryHandler{})
|
||||||
|
d.RegisterEventHandler(testEvent{}, &testEventHandler{})
|
||||||
|
|
||||||
|
_, err := d.DispatchCommand(context.Background(), unknownEvent{})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.DispatchEvent(context.Background(), unknownEvent{})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.DispatchEvent(context.Background(), unknownEvent{})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module gitstormr.dev/stone-utils/stonecqrs
|
||||||
|
|
||||||
|
go 1.24
|
||||||
|
|
||||||
|
require gitstormr.dev/stone-utils/stoneerror v1.0.0
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
gitstormr.dev/stone-utils/stoneerror v1.0.0 h1:EJpn4MZBeYifWlCoQBEGmGdEtNABjOrUzJmQSqcXqY0=
|
||||||
|
gitstormr.dev/stone-utils/stoneerror v1.0.0/go.mod h1:Rs34Oz14ILsbkZ++Ov9PObTz7mRvyyvcCcML9AeyIyk=
|
10
sonar-project.properties
Normal file
10
sonar-project.properties
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
sonar.projectKey=f33dd35c-308c-4f08-ace2-6449efc238e9
|
||||||
|
sonar.projectName=stone-utils/stonecqrs
|
||||||
|
sonar.language=go
|
||||||
|
sonar.sources=.
|
||||||
|
sonar.exclusions=**/*_test.go
|
||||||
|
sonar.tests=.
|
||||||
|
sonar.test.inclusions=**/*_test.go
|
||||||
|
sonar.go.tests.reportPaths=test-report.out
|
||||||
|
sonar.go.coverage.reportPaths=coverage.out
|
||||||
|
sonar.qualitygate.wait=true
|
Loading…
x
Reference in New Issue
Block a user