feat(telemetry): improved instrumentation for observability (#11159)

# Which Problems Are Solved

Zitadel did not provide easy correlation between errors, logs, traces
and metrics. The configuration for those instrumentations was also not
consistent, with some supporting different exporters then others.
Implementation and parsing of config was also spaghettified over
multiple packages, with awkward parsing and inconsistent naming of
options.

# How the Problems Are Solved

All telemetry is now merged under the name "instrumentation". Why?
1. We thought it was a good idea in the past to call the milestone
exporter `Telemtry` in the runtime config. Calling this `TelemetryV2`
looks weird.
2. Not everything is a meter and not everything is sent (tele...). 
3. It's also
[defined](https://opentelemetry.io/docs/concepts/instrumentation/) as
such by the OTEL documentation.

## New features

- Adds structured, context based logging with trace-ID awareness
- Static log fields are added to the context, such as service and
request path
- Static log fields are injected in each logline emitted by the
application
- Structured logs can also be send to an otel exporter
- Structured logs can be printed to StdErr in text and JSON format
- Error sinks make sure every error is logged at the correct level:
- Warnings for client side errors (HTTP 400 range, Invalid request etc)
  - Error for server side errors (Internal server errors)
- Metrics can now also be send to a OTEL collector. (previously they
could only be scraped from `/debug/metrics` with prometheus)

## Exporters

This change adds all the exporters supported by OTEL upstream and some
google specific exporters for our cloud deployment.

- StdOut / StdErr: all instrumentations
- OTEL gRPC / HTTP: all instrumentations
- Google: all instrumentations except logging
- Prometheus (pull-based): only metrics

The exception is profiling, which only supports the google exporting due
to lack of support by OTEL upstream.

## Configuration and structure 

- All instrumentation is moved into the new `backend/v3/instrumentation`
package. It reuses configuration types, so both code and runtime
configuration are easier to understand.
- The `internal/telemetry` packages are removed.
- Instrumentation is started with a single function and a proper
shutdown function is now provided.
- Legacy configuration is still parsed from the runtime config, as long
as the new configuration is disabled. This allows backporting this
feature to v4 without breaking existing configurations.

# Additional Changes

- Devcontainer: set `$PATH` variable so installed go binaries can be run
individually, without NX.
- NX: install GCI tool to fix imports

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/8408
- Closes https://github.com/zitadel/zitadel/issues/6664
- Backport to v4
This commit is contained in:
Tim Möhlmann
2026-01-12 05:51:39 +00:00
committed by GitHub
parent d7a9e9d48b
commit eb22b58756
160 changed files with 4320 additions and 2135 deletions
+5 -33
View File
@@ -2,43 +2,15 @@ package zerrors
import "fmt"
var (
_ AlreadyExists = (*AlreadyExistsError)(nil)
_ Error = (*AlreadyExistsError)(nil)
)
type AlreadyExists interface {
error
IsAlreadyExists()
}
type AlreadyExistsError struct {
*ZitadelError
}
func ThrowAlreadyExists(parent error, id, message string) error {
return &AlreadyExistsError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindAlreadyExists, parent, id, message)
}
func ThrowAlreadyExistsf(parent error, id, format string, a ...interface{}) error {
return &AlreadyExistsError{CreateZitadelError(parent, id, fmt.Sprintf(format, a...))}
}
func (err *AlreadyExistsError) IsAlreadyExists() {}
func (err *AlreadyExistsError) Is(target error) bool {
t, ok := target.(*AlreadyExistsError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
func ThrowAlreadyExistsf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindAlreadyExists, parent, id, fmt.Sprintf(format, a...))
}
func IsErrorAlreadyExists(err error) bool {
_, ok := err.(AlreadyExists)
return ok
}
func (err *AlreadyExistsError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindAlreadyExists
}
+51 -18
View File
@@ -9,25 +9,58 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestAlreadyExistsError(t *testing.T) {
var alreadyExistsError interface{} = new(zerrors.AlreadyExistsError)
_, ok := alreadyExistsError.(zerrors.AlreadyExists)
assert.True(t, ok)
}
func TestAlreadyExists(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowAlreadyExistsf(t *testing.T) {
err := zerrors.ThrowAlreadyExistsf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.AlreadyExistsError)
assert.True(t, ok)
}
t.Run("ThrowAlreadyExists", func(t *testing.T) {
err := zerrors.ThrowAlreadyExists(parentErr, id, message)
assert.NotNil(t, err)
func TestIsErrorAlreadyExists(t *testing.T) {
err := zerrors.ThrowAlreadyExists(nil, "id", "msg")
ok := zerrors.IsErrorAlreadyExists(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindAlreadyExists, zitadelErr.Kind)
err = errors.New("Already Exists!")
ok = zerrors.IsErrorAlreadyExists(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowAlreadyExistsf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowAlreadyExistsf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindAlreadyExists, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsErrorAlreadyExists", func(t *testing.T) {
err := zerrors.ThrowAlreadyExists(parentErr, id, message)
isAlreadyExists := zerrors.IsErrorAlreadyExists(err)
assert.True(t, isAlreadyExists)
otherErr := errors.New("some other error")
isAlreadyExists = zerrors.IsErrorAlreadyExists(otherErr)
assert.False(t, isAlreadyExists)
})
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ DeadlineExceeded = (*DeadlineExceededError)(nil)
_ Error = (*DeadlineExceededError)(nil)
)
type DeadlineExceeded interface {
error
IsDeadlineExceeded()
}
type DeadlineExceededError struct {
*ZitadelError
}
import "fmt"
func ThrowDeadlineExceeded(parent error, id, message string) error {
return &DeadlineExceededError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindDeadlineExceeded, parent, id, message)
}
func ThrowDeadlineExceededf(parent error, id, format string, a ...interface{}) error {
return ThrowDeadlineExceeded(parent, id, fmt.Sprintf(format, a...))
func ThrowDeadlineExceededf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindDeadlineExceeded, parent, id, fmt.Sprintf(format, a...))
}
func (err *DeadlineExceededError) IsDeadlineExceeded() {}
func IsDeadlineExceeded(err error) bool {
_, ok := err.(DeadlineExceeded)
return ok
}
func (err *DeadlineExceededError) Is(target error) bool {
t, ok := target.(*DeadlineExceededError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *DeadlineExceededError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindDeadlineExceeded
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestDeadlineExceededError(t *testing.T) {
var err interface{} = new(zerrors.DeadlineExceededError)
_, ok := err.(zerrors.DeadlineExceeded)
assert.True(t, ok)
}
func TestDeadlineExceeded(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowDeadlineExceededf(t *testing.T) {
err := zerrors.ThrowDeadlineExceededf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.DeadlineExceededError)
assert.True(t, ok)
}
t.Run("ThrowDeadlineExceeded", func(t *testing.T) {
err := zerrors.ThrowDeadlineExceeded(parentErr, id, message)
assert.NotNil(t, err)
func TestIsDeadlineExceeded(t *testing.T) {
err := zerrors.ThrowDeadlineExceeded(nil, "id", "msg")
ok := zerrors.IsDeadlineExceeded(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindDeadlineExceeded, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsDeadlineExceeded(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowDeadlineExceededf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowDeadlineExceededf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindDeadlineExceeded, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsDeadlineExceeded", func(t *testing.T) {
err := zerrors.ThrowDeadlineExceeded(parentErr, id, message)
isDeadlineExceeded := zerrors.IsDeadlineExceeded(err)
assert.True(t, isDeadlineExceeded)
})
}
-19
View File
@@ -1,19 +0,0 @@
package zerrors
import (
"strings"
)
// Error is a stdlib error extension.
// It contains parameters to identify errors through all application layers
type Error interface {
GetParent() error
GetMessage() string
SetMessage(string)
GetID() string
}
// Contains compares the error message with needle
func Contains(err error, needle string) bool {
return err != nil && strings.Contains(err.Error(), needle)
}
-19
View File
@@ -1,19 +0,0 @@
package zerrors_test
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestContains(t *testing.T) {
err := errors.New("hello world")
world := zerrors.Contains(err, "hello")
assert.True(t, world)
mars := zerrors.Contains(err, "mars")
assert.False(t, mars)
}
-34
View File
@@ -1,34 +0,0 @@
package errors
import (
"fmt"
)
var (
_ {{.ErrorName}} = (*{{.ErrorName}}Error)(nil)
_ Error = (*{{.ErrorName}}Error)(nil)
)
type {{.ErrorName}} interface {
error
Is{{.ErrorName}}()
}
type {{.ErrorName}}Error struct {
*CaosError
}
func Throw{{.ErrorName}}(parent error, id, message string) error {
return &{{.ErrorName}}Error{CreateCaosError(parent, id, message)}
}
func Throw{{.ErrorName}}f(parent error, id, format string, a ...interface{}) error {
return Throw{{.ErrorName}}(parent, id, fmt.Sprintf(format, a...))
}
func (err *{{.ErrorName}}Error) Is{{.ErrorName}}() {}
func Is{{.ErrorName}}(err error) bool {
_, ok := err.({{.ErrorName}})
return ok
}
@@ -1,75 +0,0 @@
// go generate
package main
import (
"flag"
"fmt"
"html/template"
"log"
"os"
"strings"
)
func main() {
errorName := readErrorName()
errorName = validateErrorName(errorName)
data := &Data{
ErrorName: errorName,
}
errorFile := data.createFile("error.go.tmpl")
data.createTemplate("error.go.tmpl", errorFile)
if err := errorFile.Close(); err != nil {
log.Fatal(err)
}
testFile := data.createFile("error_test.go.tmpl")
data.createTemplate("error_test.go.tmpl", testFile)
if err := testFile.Close(); err != nil {
log.Fatal(err)
}
fmt.Print(`
!!!!!
Add status mapping in internal/api/grpc/zerrors.go
!!!!!`)
}
type Data struct {
ErrorName string
}
func (data *Data) createFile(tmplName string) *os.File {
filename := strings.Replace(tmplName, "error", strings.ToLower(data.ErrorName), 1)
filename = filename[:len(filename)-5]
filePath := fmt.Sprintf("../%s", filename)
file, err := os.Create(filePath)
if err != nil {
log.Fatalf("unable to create file (%s): %v", filePath, err)
}
return file
}
func (data *Data) createTemplate(templateName string, file *os.File) {
tmpl := template.Must(template.New(templateName).ParseFiles(templateName))
if err := tmpl.Execute(file, data); err != nil {
log.Fatal("unable to execute tmpl: ", err)
}
}
func readErrorName() (errorName string) {
flag.StringVar(&errorName, "Name", "", "KeyType of the error (e.g. Internal)")
flag.Parse()
return errorName
}
func validateErrorName(errorName string) string {
if errorName == "" {
log.Fatal("pass argument name")
}
if strings.Contains(errorName, " ") || strings.Contains(errorName, ".") {
log.Fatal("name cannot contain spaces or points")
}
return strings.Title(errorName)
}
@@ -1,5 +0,0 @@
type {{.ErrorName}} interface {
error
Is{{.ErrorName}}()
}
@@ -1,33 +0,0 @@
package errors_test
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/zerrors"
)
func Test{{.ErrorName}}Error(t *testing.T) {
var err interface{}
err = new(zerrors.{{.ErrorName}}Error)
_, ok := err.(*zerrors.{{.ErrorName}})
assert.True(t, ok)
}
func TestThrow{{.ErrorName}}f(t *testing.T) {
err := zerrors.Throw{{.ErrorName}}f(nil, "id", "msg")
_, ok := err.(*zerrors.{{.ErrorName}}Error)
assert.True(t, ok)
}
func TestIs{{.ErrorName}}(t *testing.T) {
err := zerrors.Throw{{.ErrorName}}(nil, "id", "msg")
ok := zerrors.Is{{.ErrorName}}(err)
assert.True(t, ok)
err = errors.New("I am found!")
ok = zerrors.Is{{.ErrorName}}(err)
assert.False(t, ok)
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ Internal = (*InternalError)(nil)
_ Error = (*InternalError)(nil)
)
type Internal interface {
error
IsInternal()
}
type InternalError struct {
*ZitadelError
}
import "fmt"
func ThrowInternal(parent error, id, message string) error {
return &InternalError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindInternal, parent, id, message)
}
func ThrowInternalf(parent error, id, format string, a ...interface{}) error {
return ThrowInternal(parent, id, fmt.Sprintf(format, a...))
func ThrowInternalf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindInternal, parent, id, fmt.Sprintf(format, a...))
}
func (err *InternalError) IsInternal() {}
func IsInternal(err error) bool {
_, ok := err.(Internal)
return ok
}
func (err *InternalError) Is(target error) bool {
t, ok := target.(*InternalError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *InternalError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindInternal
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestInternalError(t *testing.T) {
var err interface{} = new(zerrors.InternalError)
_, ok := err.(zerrors.Internal)
assert.True(t, ok)
}
func TestInternal(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowInternalf(t *testing.T) {
err := zerrors.ThrowInternalf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.InternalError)
assert.True(t, ok)
}
t.Run("ThrowInternal", func(t *testing.T) {
err := zerrors.ThrowInternal(parentErr, id, message)
assert.NotNil(t, err)
func TestIsInternal(t *testing.T) {
err := zerrors.ThrowInternal(nil, "id", "msg")
ok := zerrors.IsInternal(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindInternal, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsInternal(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowInternalf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowInternalf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindInternal, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsInternal", func(t *testing.T) {
err := zerrors.ThrowInternal(parentErr, id, message)
isInternal := zerrors.IsInternal(err)
assert.True(t, isInternal)
})
}
+6 -45
View File
@@ -1,55 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ InvalidArgument = (*InvalidArgumentError)(nil)
_ Error = (*InvalidArgumentError)(nil)
)
type InvalidArgument interface {
error
IsInvalidArgument()
}
type InvalidArgumentError struct {
*ZitadelError
}
import "fmt"
func ThrowInvalidArgument(parent error, id, message string) error {
return &InvalidArgumentError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindInvalidArgument, parent, id, message)
}
func ThrowInvalidArgumentf(parent error, id, format string, a ...interface{}) error {
return ThrowInvalidArgument(parent, id, fmt.Sprintf(format, a...))
func ThrowInvalidArgumentf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindInvalidArgument, parent, id, fmt.Sprintf(format, a...))
}
func (err *InvalidArgumentError) IsInvalidArgument() {}
func IsErrorInvalidArgument(err error) bool {
_, ok := err.(InvalidArgument)
return ok
}
func (err *InvalidArgumentError) Is(target error) bool {
t, ok := target.(*InvalidArgumentError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *InvalidArgumentError) As(target any) bool {
targetErr, ok := target.(*InvalidArgumentError)
if !ok {
return false
}
*targetErr = *err
return true
}
func (err *InvalidArgumentError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindInvalidArgument
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestInvalidArgumentError(t *testing.T) {
var invalidArgumentError interface{} = new(zerrors.InvalidArgumentError)
_, ok := invalidArgumentError.(zerrors.InvalidArgument)
assert.True(t, ok)
}
func TestInvalidArgument(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowInvalidArgumentf(t *testing.T) {
err := zerrors.ThrowInvalidArgumentf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.InvalidArgumentError)
assert.True(t, ok)
}
t.Run("ThrowInvalidArgument", func(t *testing.T) {
err := zerrors.ThrowInvalidArgument(parentErr, id, message)
assert.NotNil(t, err)
func TestIsErrorInvalidArgument(t *testing.T) {
err := zerrors.ThrowInvalidArgument(nil, "id", "msg")
ok := zerrors.IsErrorInvalidArgument(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindInvalidArgument, zitadelErr.Kind)
err = errors.New("I am invalid!")
ok = zerrors.IsErrorInvalidArgument(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowInvalidArgumentf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowInvalidArgumentf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindInvalidArgument, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsInvalidArgument", func(t *testing.T) {
err := zerrors.ThrowInvalidArgument(parentErr, id, message)
isInvalidArgument := zerrors.IsErrorInvalidArgument(err)
assert.True(t, isInvalidArgument)
})
}
+153
View File
@@ -0,0 +1,153 @@
// Code generated by "enumer -type=Kind -trimprefix=Kind -json"; DO NOT EDIT.
package zerrors
import (
"encoding/json"
"fmt"
"strings"
)
const _KindName = "CanceledUnknownInvalidArgumentDeadlineExceededNotFoundAlreadyExistsPermissionDeniedResourceExhaustedPreconditionFailedAbortedOutOfRangeUnimplementedInternalUnavailableDataLossUnauthenticated"
var _KindIndex = [...]uint8{0, 8, 15, 30, 46, 54, 67, 83, 100, 118, 125, 135, 148, 156, 167, 175, 190}
const _KindLowerName = "canceledunknowninvalidargumentdeadlineexceedednotfoundalreadyexistspermissiondeniedresourceexhaustedpreconditionfailedabortedoutofrangeunimplementedinternalunavailabledatalossunauthenticated"
func (i Kind) String() string {
i -= 1
if i < 0 || i >= Kind(len(_KindIndex)-1) {
return fmt.Sprintf("Kind(%d)", i+1)
}
return _KindName[_KindIndex[i]:_KindIndex[i+1]]
}
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
func _KindNoOp() {
var x [1]struct{}
_ = x[KindCanceled-(1)]
_ = x[KindUnknown-(2)]
_ = x[KindInvalidArgument-(3)]
_ = x[KindDeadlineExceeded-(4)]
_ = x[KindNotFound-(5)]
_ = x[KindAlreadyExists-(6)]
_ = x[KindPermissionDenied-(7)]
_ = x[KindResourceExhausted-(8)]
_ = x[KindPreconditionFailed-(9)]
_ = x[KindAborted-(10)]
_ = x[KindOutOfRange-(11)]
_ = x[KindUnimplemented-(12)]
_ = x[KindInternal-(13)]
_ = x[KindUnavailable-(14)]
_ = x[KindDataLoss-(15)]
_ = x[KindUnauthenticated-(16)]
}
var _KindValues = []Kind{KindCanceled, KindUnknown, KindInvalidArgument, KindDeadlineExceeded, KindNotFound, KindAlreadyExists, KindPermissionDenied, KindResourceExhausted, KindPreconditionFailed, KindAborted, KindOutOfRange, KindUnimplemented, KindInternal, KindUnavailable, KindDataLoss, KindUnauthenticated}
var _KindNameToValueMap = map[string]Kind{
_KindName[0:8]: KindCanceled,
_KindLowerName[0:8]: KindCanceled,
_KindName[8:15]: KindUnknown,
_KindLowerName[8:15]: KindUnknown,
_KindName[15:30]: KindInvalidArgument,
_KindLowerName[15:30]: KindInvalidArgument,
_KindName[30:46]: KindDeadlineExceeded,
_KindLowerName[30:46]: KindDeadlineExceeded,
_KindName[46:54]: KindNotFound,
_KindLowerName[46:54]: KindNotFound,
_KindName[54:67]: KindAlreadyExists,
_KindLowerName[54:67]: KindAlreadyExists,
_KindName[67:83]: KindPermissionDenied,
_KindLowerName[67:83]: KindPermissionDenied,
_KindName[83:100]: KindResourceExhausted,
_KindLowerName[83:100]: KindResourceExhausted,
_KindName[100:118]: KindPreconditionFailed,
_KindLowerName[100:118]: KindPreconditionFailed,
_KindName[118:125]: KindAborted,
_KindLowerName[118:125]: KindAborted,
_KindName[125:135]: KindOutOfRange,
_KindLowerName[125:135]: KindOutOfRange,
_KindName[135:148]: KindUnimplemented,
_KindLowerName[135:148]: KindUnimplemented,
_KindName[148:156]: KindInternal,
_KindLowerName[148:156]: KindInternal,
_KindName[156:167]: KindUnavailable,
_KindLowerName[156:167]: KindUnavailable,
_KindName[167:175]: KindDataLoss,
_KindLowerName[167:175]: KindDataLoss,
_KindName[175:190]: KindUnauthenticated,
_KindLowerName[175:190]: KindUnauthenticated,
}
var _KindNames = []string{
_KindName[0:8],
_KindName[8:15],
_KindName[15:30],
_KindName[30:46],
_KindName[46:54],
_KindName[54:67],
_KindName[67:83],
_KindName[83:100],
_KindName[100:118],
_KindName[118:125],
_KindName[125:135],
_KindName[135:148],
_KindName[148:156],
_KindName[156:167],
_KindName[167:175],
_KindName[175:190],
}
// KindString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func KindString(s string) (Kind, error) {
if val, ok := _KindNameToValueMap[s]; ok {
return val, nil
}
if val, ok := _KindNameToValueMap[strings.ToLower(s)]; ok {
return val, nil
}
return 0, fmt.Errorf("%s does not belong to Kind values", s)
}
// KindValues returns all values of the enum
func KindValues() []Kind {
return _KindValues
}
// KindStrings returns a slice of all String values of the enum
func KindStrings() []string {
strs := make([]string, len(_KindNames))
copy(strs, _KindNames)
return strs
}
// IsAKind returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Kind) IsAKind() bool {
for _, v := range _KindValues {
if i == v {
return true
}
}
return false
}
// MarshalJSON implements the json.Marshaler interface for Kind
func (i Kind) MarshalJSON() ([]byte, error) {
return json.Marshal(i.String())
}
// UnmarshalJSON implements the json.Unmarshaler interface for Kind
func (i *Kind) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("Kind should be a string, got %s", data)
}
var err error
*i, err = KindString(s)
return err
}
+5 -28
View File
@@ -2,38 +2,15 @@ package zerrors
import "fmt"
type NotFound interface {
error
IsNotFound()
}
type NotFoundError struct {
*ZitadelError
}
func ThrowNotFound(parent error, id, message string) error {
return &NotFoundError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindNotFound, parent, id, message)
}
func ThrowNotFoundf(parent error, id, format string, a ...interface{}) error {
return ThrowNotFound(parent, id, fmt.Sprintf(format, a...))
func ThrowNotFoundf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindNotFound, parent, id, fmt.Sprintf(format, a...))
}
func (err *NotFoundError) IsNotFound() {}
func IsNotFound(err error) bool {
_, ok := err.(NotFound)
return ok
}
func (err *NotFoundError) Is(target error) bool {
t, ok := target.(*NotFoundError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *NotFoundError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindNotFound
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestNotFoundError(t *testing.T) {
var notFoundError interface{} = new(zerrors.NotFoundError)
_, ok := notFoundError.(zerrors.NotFound)
assert.True(t, ok)
}
func TestNotFound(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowNotFoundf(t *testing.T) {
err := zerrors.ThrowNotFoundf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.NotFoundError)
assert.True(t, ok)
}
t.Run("ThrowNotFound", func(t *testing.T) {
err := zerrors.ThrowNotFound(parentErr, id, message)
assert.NotNil(t, err)
func TestIsNotFound(t *testing.T) {
err := zerrors.ThrowNotFound(nil, "id", "msg")
ok := zerrors.IsNotFound(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindNotFound, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsNotFound(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowNotFoundf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowNotFoundf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindNotFound, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsNotFound", func(t *testing.T) {
err := zerrors.ThrowNotFound(parentErr, id, message)
isNotFound := zerrors.IsNotFound(err)
assert.True(t, isNotFound)
})
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ PermissionDenied = (*PermissionDeniedError)(nil)
_ Error = (*PermissionDeniedError)(nil)
)
type PermissionDenied interface {
error
IsPermissionDenied()
}
type PermissionDeniedError struct {
*ZitadelError
}
import "fmt"
func ThrowPermissionDenied(parent error, id, message string) error {
return &PermissionDeniedError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindPermissionDenied, parent, id, message)
}
func ThrowPermissionDeniedf(parent error, id, format string, a ...interface{}) error {
return ThrowPermissionDenied(parent, id, fmt.Sprintf(format, a...))
func ThrowPermissionDeniedf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindPermissionDenied, parent, id, fmt.Sprintf(format, a...))
}
func (err *PermissionDeniedError) IsPermissionDenied() {}
func IsPermissionDenied(err error) bool {
_, ok := err.(PermissionDenied)
return ok
}
func (err *PermissionDeniedError) Is(target error) bool {
t, ok := target.(*PermissionDeniedError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *PermissionDeniedError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindPermissionDenied
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestPermissionDeniedError(t *testing.T) {
var err interface{} = new(zerrors.PermissionDeniedError)
_, ok := err.(zerrors.PermissionDenied)
assert.True(t, ok)
}
func TestPermissionDenied(t *testing.T) {
parentErr := errors.New("parent error")
id := "PERMISSION_DENIED"
message := "you do not have permission"
func TestThrowPermissionDeniedf(t *testing.T) {
err := zerrors.ThrowPermissionDeniedf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.PermissionDeniedError)
assert.True(t, ok)
}
t.Run("ThrowPermissionDenied", func(t *testing.T) {
err := zerrors.ThrowPermissionDenied(parentErr, id, message)
assert.NotNil(t, err)
func TestIsPermissionDenied(t *testing.T) {
err := zerrors.ThrowPermissionDenied(nil, "id", "msg")
ok := zerrors.IsPermissionDenied(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindPermissionDenied, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsPermissionDenied(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowPermissionDeniedf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowPermissionDeniedf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindPermissionDenied, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsPermissionDenied", func(t *testing.T) {
err := zerrors.ThrowPermissionDenied(parentErr, id, message)
isPermissionDenied := zerrors.IsPermissionDenied(err)
assert.True(t, isPermissionDenied)
})
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ PreconditionFailed = (*PreconditionFailedError)(nil)
_ Error = (*PreconditionFailedError)(nil)
)
type PreconditionFailed interface {
error
IsPreconditionFailed()
}
type PreconditionFailedError struct {
*ZitadelError
}
import "fmt"
func ThrowPreconditionFailed(parent error, id, message string) error {
return &PreconditionFailedError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindPreconditionFailed, parent, id, message)
}
func ThrowPreconditionFailedf(parent error, id, format string, a ...interface{}) error {
return ThrowPreconditionFailed(parent, id, fmt.Sprintf(format, a...))
func ThrowPreconditionFailedf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindPreconditionFailed, parent, id, fmt.Sprintf(format, a...))
}
func (err *PreconditionFailedError) IsPreconditionFailed() {}
func IsPreconditionFailed(err error) bool {
_, ok := err.(PreconditionFailed)
return ok
}
func (err *PreconditionFailedError) Is(target error) bool {
t, ok := target.(*PreconditionFailedError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *PreconditionFailedError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindPreconditionFailed
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestPreconditionFailedError(t *testing.T) {
var err interface{} = new(zerrors.PreconditionFailedError)
_, ok := err.(zerrors.PreconditionFailed)
assert.True(t, ok)
}
func TestPreconditionFailed(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowPreconditionFailedf(t *testing.T) {
err := zerrors.ThrowPreconditionFailedf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.PreconditionFailedError)
assert.True(t, ok)
}
t.Run("ThrowPreconditionFailed", func(t *testing.T) {
err := zerrors.ThrowPreconditionFailed(parentErr, id, message)
assert.NotNil(t, err)
func TestIsPreconditionFailed(t *testing.T) {
err := zerrors.ThrowPreconditionFailed(nil, "id", "msg")
ok := zerrors.IsPreconditionFailed(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindPreconditionFailed, zitadelErr.Kind)
err = errors.New("Precondition failed!")
ok = zerrors.IsPreconditionFailed(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowPreconditionFailedf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowPreconditionFailedf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindPreconditionFailed, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsPreconditionFailed", func(t *testing.T) {
err := zerrors.ThrowPreconditionFailed(parentErr, id, message)
isPreconditionFailed := zerrors.IsPreconditionFailed(err)
assert.True(t, isPreconditionFailed)
})
}
+6 -38
View File
@@ -1,48 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ ResourceExhausted = (*ResourceExhaustedError)(nil)
_ Error = (*ResourceExhaustedError)(nil)
)
type ResourceExhausted interface {
error
IsResourceExhausted()
}
type ResourceExhaustedError struct {
*ZitadelError
}
import "fmt"
func ThrowResourceExhausted(parent error, id, message string) error {
return &ResourceExhaustedError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindResourceExhausted, parent, id, message)
}
func ThrowResourceExhaustedf(parent error, id, format string, a ...interface{}) error {
return ThrowResourceExhausted(parent, id, fmt.Sprintf(format, a...))
func ThrowResourceExhaustedf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindResourceExhausted, parent, id, fmt.Sprintf(format, a...))
}
func (err *ResourceExhaustedError) IsResourceExhausted() {}
func IsResourceExhausted(err error) bool {
//nolint:errorlint
_, ok := err.(ResourceExhausted)
return ok
}
func (err *ResourceExhaustedError) Is(target error) bool {
//nolint:errorlint
t, ok := target.(*ResourceExhaustedError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *ResourceExhaustedError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindResourceExhausted
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestResourceExhaustedError(t *testing.T) {
var err interface{} = new(zerrors.ResourceExhaustedError)
_, ok := err.(zerrors.ResourceExhausted)
assert.True(t, ok)
}
func TestResourceExhausted(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowResourceExhaustedf(t *testing.T) {
err := zerrors.ThrowResourceExhaustedf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.ResourceExhaustedError)
assert.True(t, ok)
}
t.Run("ThrowResourceExhausted", func(t *testing.T) {
err := zerrors.ThrowResourceExhausted(parentErr, id, message)
assert.NotNil(t, err)
func TestIsResourceExhausted(t *testing.T) {
err := zerrors.ThrowResourceExhausted(nil, "id", "msg")
ok := zerrors.IsResourceExhausted(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindResourceExhausted, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsResourceExhausted(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowResourceExhaustedf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowResourceExhaustedf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindResourceExhausted, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsResourceExhausted", func(t *testing.T) {
err := zerrors.ThrowResourceExhausted(parentErr, id, message)
isResourceExhausted := zerrors.IsResourceExhausted(err)
assert.True(t, isResourceExhausted)
})
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ Unauthenticated = (*UnauthenticatedError)(nil)
_ Error = (*UnauthenticatedError)(nil)
)
type Unauthenticated interface {
error
IsUnauthenticated()
}
type UnauthenticatedError struct {
*ZitadelError
}
import "fmt"
func ThrowUnauthenticated(parent error, id, message string) error {
return &UnauthenticatedError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindUnauthenticated, parent, id, message)
}
func ThrowUnauthenticatedf(parent error, id, format string, a ...interface{}) error {
return ThrowUnauthenticated(parent, id, fmt.Sprintf(format, a...))
func ThrowUnauthenticatedf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindUnauthenticated, parent, id, fmt.Sprintf(format, a...))
}
func (err *UnauthenticatedError) IsUnauthenticated() {}
func IsUnauthenticated(err error) bool {
_, ok := err.(Unauthenticated)
return ok
}
func (err *UnauthenticatedError) Is(target error) bool {
t, ok := target.(*UnauthenticatedError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *UnauthenticatedError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindUnauthenticated
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestUnauthenticatedError(t *testing.T) {
var err interface{} = new(zerrors.UnauthenticatedError)
_, ok := err.(zerrors.Unauthenticated)
assert.True(t, ok)
}
func TestUnauthenticated(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowUnauthenticatedf(t *testing.T) {
err := zerrors.ThrowUnauthenticatedf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.UnauthenticatedError)
assert.True(t, ok)
}
t.Run("ThrowUnauthenticated", func(t *testing.T) {
err := zerrors.ThrowUnauthenticated(parentErr, id, message)
assert.NotNil(t, err)
func TestIsUnauthenticated(t *testing.T) {
err := zerrors.ThrowUnauthenticated(nil, "id", "msg")
ok := zerrors.IsUnauthenticated(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnauthenticated, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsUnauthenticated(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowUnauthenticatedf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowUnauthenticatedf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnauthenticated, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsUnauthenticated", func(t *testing.T) {
err := zerrors.ThrowUnauthenticated(parentErr, id, message)
isUnauthenticated := zerrors.IsUnauthenticated(err)
assert.True(t, isUnauthenticated)
})
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ Unavailable = (*UnavailableError)(nil)
_ Error = (*UnavailableError)(nil)
)
type Unavailable interface {
error
IsUnavailable()
}
type UnavailableError struct {
*ZitadelError
}
import "fmt"
func ThrowUnavailable(parent error, id, message string) error {
return &UnavailableError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindUnavailable, parent, id, message)
}
func ThrowUnavailablef(parent error, id, format string, a ...interface{}) error {
return ThrowUnavailable(parent, id, fmt.Sprintf(format, a...))
func ThrowUnavailablef(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindUnavailable, parent, id, fmt.Sprintf(format, a...))
}
func (err *UnavailableError) IsUnavailable() {}
func IsUnavailable(err error) bool {
_, ok := err.(Unavailable)
return ok
}
func (err *UnavailableError) Is(target error) bool {
t, ok := target.(*UnavailableError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *UnavailableError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindUnavailable
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestUnavailableError(t *testing.T) {
var err interface{} = new(zerrors.UnavailableError)
_, ok := err.(zerrors.Unavailable)
assert.True(t, ok)
}
func TestUnavailable(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowUnavailablef(t *testing.T) {
err := zerrors.ThrowUnavailablef(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.UnavailableError)
assert.True(t, ok)
}
t.Run("ThrowUnavailable", func(t *testing.T) {
err := zerrors.ThrowUnavailable(parentErr, id, message)
assert.NotNil(t, err)
func TestIsUnavailable(t *testing.T) {
err := zerrors.ThrowUnavailable(nil, "id", "msg")
ok := zerrors.IsUnavailable(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnavailable, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsUnavailable(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowUnavailablef", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowUnavailablef(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnavailable, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsUnavailable", func(t *testing.T) {
err := zerrors.ThrowUnavailable(parentErr, id, message)
isUnavailable := zerrors.IsUnavailable(err)
assert.True(t, isUnavailable)
})
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ Unimplemented = (*UnimplementedError)(nil)
_ Error = (*UnimplementedError)(nil)
)
type Unimplemented interface {
error
IsUnimplemented()
}
type UnimplementedError struct {
*ZitadelError
}
import "fmt"
func ThrowUnimplemented(parent error, id, message string) error {
return &UnimplementedError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindUnimplemented, parent, id, message)
}
func ThrowUnimplementedf(parent error, id, format string, a ...interface{}) error {
return ThrowUnimplemented(parent, id, fmt.Sprintf(format, a...))
func ThrowUnimplementedf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindUnimplemented, parent, id, fmt.Sprintf(format, a...))
}
func (err *UnimplementedError) IsUnimplemented() {}
func IsUnimplemented(err error) bool {
_, ok := err.(Unimplemented)
return ok
}
func (err *UnimplementedError) Is(target error) bool {
t, ok := target.(*UnimplementedError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *UnimplementedError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindUnimplemented
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestUnimplementedError(t *testing.T) {
var unimplementedError interface{} = new(zerrors.UnimplementedError)
_, ok := unimplementedError.(zerrors.Unimplemented)
assert.True(t, ok)
}
func TestUnimplemented(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowUnimplementedf(t *testing.T) {
err := zerrors.ThrowUnimplementedf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.UnimplementedError)
assert.True(t, ok)
}
t.Run("ThrowUnimplemented", func(t *testing.T) {
err := zerrors.ThrowUnimplemented(parentErr, id, message)
assert.NotNil(t, err)
func TestIsUnimplemented(t *testing.T) {
err := zerrors.ThrowUnimplemented(nil, "id", "msg")
ok := zerrors.IsUnimplemented(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnimplemented, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsUnimplemented(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowUnimplementedf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowUnimplementedf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnimplemented, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsUnimplemented", func(t *testing.T) {
err := zerrors.ThrowUnimplemented(parentErr, id, message)
isUnimplemented := zerrors.IsUnimplemented(err)
assert.True(t, isUnimplemented)
})
}
+6 -36
View File
@@ -1,46 +1,16 @@
package zerrors
import (
"fmt"
)
var (
_ Unknown = (*UnknownError)(nil)
_ Error = (*UnknownError)(nil)
)
type Unknown interface {
error
IsUnknown()
}
type UnknownError struct {
*ZitadelError
}
import "fmt"
func ThrowUnknown(parent error, id, message string) error {
return &UnknownError{CreateZitadelError(parent, id, message)}
return CreateZitadelError(KindUnknown, parent, id, message)
}
func ThrowUnknownf(parent error, id, format string, a ...interface{}) error {
return ThrowUnknown(parent, id, fmt.Sprintf(format, a...))
func ThrowUnknownf(parent error, id, format string, a ...any) error {
return CreateZitadelError(KindUnknown, parent, id, fmt.Sprintf(format, a...))
}
func (err *UnknownError) IsUnknown() {}
func IsUnknown(err error) bool {
_, ok := err.(Unknown)
return ok
}
func (err *UnknownError) Is(target error) bool {
t, ok := target.(*UnknownError)
if !ok {
return false
}
return err.ZitadelError.Is(t.ZitadelError)
}
func (err *UnknownError) Unwrap() error {
return err.ZitadelError
zitadelErr, ok := AsZitadelError(err)
return ok && zitadelErr.Kind == KindUnknown
}
+47 -18
View File
@@ -9,25 +9,54 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestUnknownError(t *testing.T) {
var err interface{} = new(zerrors.UnknownError)
_, ok := err.(zerrors.Unknown)
assert.True(t, ok)
}
func TestUnknown(t *testing.T) {
parentErr := errors.New("parent error")
id := "test_id"
message := "test message"
func TestThrowUnknownf(t *testing.T) {
err := zerrors.ThrowUnknownf(nil, "id", "msg")
//nolint:errorlint
_, ok := err.(*zerrors.UnknownError)
assert.True(t, ok)
}
t.Run("ThrowUnknown", func(t *testing.T) {
err := zerrors.ThrowUnknown(parentErr, id, message)
assert.NotNil(t, err)
func TestIsUnknown(t *testing.T) {
err := zerrors.ThrowUnknown(nil, "id", "msg")
ok := zerrors.IsUnknown(err)
assert.True(t, ok)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnknown, zitadelErr.Kind)
err = errors.New("I am found!")
ok = zerrors.IsUnknown(err)
assert.False(t, ok)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, message, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("ThrowUnknownf", func(t *testing.T) {
format := "formatted %s"
arg := "message"
expectedMessage := "formatted message"
err := zerrors.ThrowUnknownf(parentErr, id, format, arg)
assert.NotNil(t, err)
zitadelErr, ok := zerrors.AsZitadelError(err)
assert.True(t, ok)
assert.Equal(t, zerrors.KindUnknown, zitadelErr.Kind)
zitadelError := new(zerrors.ZitadelError)
if errors.As(err, &zitadelError) {
assert.Equal(t, parentErr, zitadelError.Unwrap())
assert.Equal(t, id, zitadelError.ID)
assert.Equal(t, expectedMessage, zitadelError.Message)
} else {
t.Errorf("error is not of type ZitadelError")
}
})
t.Run("IsUnknown", func(t *testing.T) {
err := zerrors.ThrowUnknown(parentErr, id, message)
isUnknown := zerrors.IsUnknown(err)
assert.True(t, isUnknown)
})
}
+94 -13
View File
@@ -3,23 +3,93 @@ package zerrors
import (
"errors"
"fmt"
"reflect"
"log/slog"
)
var _ Error = (*ZitadelError)(nil)
//go:generate enumer -type=Kind -trimprefix=Kind -json
type Kind int
const (
// KindCanceled indicates that the operation was canceled, typically by the
// caller.
KindCanceled Kind = 1
// KindUnknown indicates that the operation failed for an unknown reason.
KindUnknown Kind = 2
// KindInvalidArgument indicates that client supplied an invalid argument.
KindInvalidArgument Kind = 3
// KindDeadlineExceeded indicates that deadline expired before the operation
// could complete.
KindDeadlineExceeded Kind = 4
// KindNotFound indicates that some requested entity (for example, a file or
// directory) was not found.
KindNotFound Kind = 5
// KindAlreadyExists indicates that client attempted to create an entity (for
// example, a file or directory) that already exists.
KindAlreadyExists Kind = 6
// KindPermissionDenied indicates that the caller doesn't have permission to
// execute the specified operation.
KindPermissionDenied Kind = 7
// KindResourceExhausted indicates that some resource has been exhausted. For
// example, a per-user quota may be exhausted or the entire file system may
// be full.
KindResourceExhausted Kind = 8
// KindPreconditionFailed indicates that the system is not in a state
// required for the operation's execution.
KindPreconditionFailed Kind = 9
// KindAborted indicates that operation was aborted by the system, usually
// because of a concurrency issue such as a sequencer check failure or
// transaction abort.
KindAborted Kind = 10
// KindOutOfRange indicates that the operation was attempted past the valid
// range (for example, seeking past end-of-file).
KindOutOfRange Kind = 11
// KindUnimplemented indicates that the operation isn't implemented,
// supported, or enabled in this service.
KindUnimplemented Kind = 12
// KindInternal indicates that some invariants expected by the underlying
// system have been broken. This Kind is reserved for serious errors.
KindInternal Kind = 13
// KindUnavailable indicates that the service is currently unavailable. This
// is usually temporary, so clients can back off and retry idempotent
// operations.
KindUnavailable Kind = 14
// KindDataLoss indicates that the operation has resulted in unrecoverable
// data loss or corruption.
KindDataLoss Kind = 15
// KindUnauthenticated indicates that the request does not have valid
// authentication credentials for the operation.
KindUnauthenticated Kind = 16
)
type ZitadelError struct {
Kind Kind
Parent error
Message string
ID string
}
func ThrowError(parent error, id, message string) error {
return CreateZitadelError(parent, id, message)
return CreateZitadelError(KindUnknown, parent, id, message)
}
func CreateZitadelError(parent error, id, message string) *ZitadelError {
func CreateZitadelError(kind Kind, parent error, id, message string) *ZitadelError {
return &ZitadelError{
Kind: kind,
Parent: parent,
ID: id,
Message: message,
@@ -58,6 +128,9 @@ func (err *ZitadelError) Is(target error) bool {
if !ok {
return false
}
if t.Kind != err.Kind {
return false
}
if t.ID != "" && t.ID != err.ID {
return false
}
@@ -71,16 +144,24 @@ func (err *ZitadelError) Is(target error) bool {
return true
}
func (err *ZitadelError) As(target interface{}) bool {
_, ok := target.(**ZitadelError)
if !ok {
return false
}
reflect.Indirect(reflect.ValueOf(target)).Set(reflect.ValueOf(err))
return true
}
func IsZitadelError(err error) bool {
zitadelErr := new(ZitadelError)
return errors.As(err, &zitadelErr)
}
func AsZitadelError(err error) (*ZitadelError, bool) {
zitadelErr := new(ZitadelError)
ok := errors.As(err, &zitadelErr)
return zitadelErr, ok
}
var _ slog.LogValuer = (*ZitadelError)(nil)
func (err *ZitadelError) LogValue() slog.Value {
return slog.GroupValue(
slog.String("kind", err.Kind.String()),
slog.String("message", err.Message),
slog.String("id", err.ID),
slog.Any("parent", err.Parent),
)
}
+31 -8
View File
@@ -19,26 +19,49 @@ func TestErrorMethod(t *testing.T) {
assert.Equal(t, subExptected, err.Error())
}
func TestIsZitadelError(t *testing.T) {
func TestZitadelError_Is(t *testing.T) {
parent := errors.New("parent error")
target := zerrors.CreateZitadelError(zerrors.KindAborted, parent, "id", "message")
tests := []struct {
name string
name string // description of this test case
err error
want bool
}{
{
name: "zitadel error",
err: zerrors.ThrowInvalidArgument(nil, "id", "msg"),
want: true,
name: "wrong type",
err: errors.New("some other error"),
want: false,
},
{
name: "other error",
err: errors.New("just a random error"),
name: "different kind",
err: zerrors.CreateZitadelError(zerrors.KindNotFound, parent, "id", "message"),
want: false,
},
{
name: "different id",
err: zerrors.CreateZitadelError(zerrors.KindAborted, parent, "otherID", "message"),
want: false,
},
{
name: "different message",
err: zerrors.CreateZitadelError(zerrors.KindAborted, parent, "id", "other message"),
want: false,
},
{
name: "different parent",
err: zerrors.CreateZitadelError(zerrors.KindAborted, errors.New("other parent"), "id", "message"),
want: false,
},
{
name: "same error",
err: zerrors.CreateZitadelError(zerrors.KindAborted, parent, "id", "message"),
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, zerrors.IsZitadelError(tt.err), "IsZitadelError(%v)", tt.err)
got := errors.Is(tt.err, target)
assert.Equal(t, tt.want, got)
})
}
}