support json schema
This commit is contained in:
parent
f0a699029a
commit
3e319b24fd
2
go.mod
2
go.mod
@ -7,6 +7,7 @@ require (
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/swaggest/jsonschema-go v0.3.73
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
@ -39,6 +40,7 @@ require (
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/swaggest/refl v1.3.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -75,6 +75,10 @@ github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggest/jsonschema-go v0.3.73 h1:gU1pBzF3pkZ1GDD3dRMdQoCjrA0sldJ+QcM7aSSPgvc=
|
||||
github.com/swaggest/jsonschema-go v0.3.73/go.mod h1:qp+Ym2DIXHlHzch3HKz50gPf2wJhKOrAB/VYqLS2oJU=
|
||||
github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I=
|
||||
github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
|
40
pkg/app/schema.go
Normal file
40
pkg/app/schema.go
Normal file
@ -0,0 +1,40 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
js "github.com/swaggest/jsonschema-go"
|
||||
)
|
||||
|
||||
// Generates json schema for app's config.AppConfig
|
||||
func (app *App) Schema() ([]byte, error) {
|
||||
r := js.Reflector{}
|
||||
|
||||
s, err := r.Reflect(*app.cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.MarshalIndent(s, "", " ")
|
||||
}
|
||||
|
||||
// Generates json schema for custom config
|
||||
// which embeds *config.AppConfig into it
|
||||
// Panics if no *config.AppConfig is embedded into custom
|
||||
// config type
|
||||
//
|
||||
// See swaggest/jsonschema-go for struct tag docs
|
||||
func CustomSchema[T any](target T) ([]byte, error) {
|
||||
if err := HasAppConfig(target); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
r := js.Reflector{}
|
||||
|
||||
s, err := r.Reflect(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.MarshalIndent(s, "", " ")
|
||||
}
|
@ -19,7 +19,7 @@ import (
|
||||
// are up to the caller.
|
||||
func MustLoadConfigInto[T any](ctx context.Context, into T) (context.Context, T) {
|
||||
// Step 1: Check our custom type for required *config.AppConfig
|
||||
if err := hasAppConfig(into); err != nil {
|
||||
if err := HasAppConfig(into); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ func setAppConfig[T any](target T, appConfig *config.AppConfig) error {
|
||||
}
|
||||
|
||||
// Replace *config.AppConfig
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
for i := range v.NumField() {
|
||||
field := v.Field(i)
|
||||
if field.Type() == reflect.TypeOf((*config.AppConfig)(nil)) {
|
||||
if !field.CanSet() {
|
||||
@ -80,7 +80,7 @@ func setAppConfig[T any](target T, appConfig *config.AppConfig) error {
|
||||
return errors.New("no *config.AppConfig field found in target struct")
|
||||
}
|
||||
|
||||
func hasAppConfig[T any](target T) error {
|
||||
func HasAppConfig[T any](target T) error {
|
||||
v := reflect.ValueOf(target)
|
||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||
return errors.New("target must be a non-nil pointer to a struct")
|
||||
@ -92,7 +92,7 @@ func hasAppConfig[T any](target T) error {
|
||||
}
|
||||
|
||||
hasAppConfig := false
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
for i := range v.NumField() {
|
||||
field := v.Type().Field(i)
|
||||
if field.Type == reflect.TypeOf((*config.AppConfig)(nil)) {
|
||||
hasAppConfig = true
|
||||
|
@ -11,16 +11,16 @@ var DefaultConfig = &AppConfig{
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Name string `yaml:"name,omitempty" env:"APP_NAME"`
|
||||
Environment string `yaml:"environment,omitempty" env:"APP_ENVIRONMENT"`
|
||||
Name string `yaml:"name,omitempty" env:"APP_NAME" json:"name,omitempty"`
|
||||
Environment string `yaml:"environment,omitempty" env:"APP_ENVIRONMENT" json:"environment,omitempty"`
|
||||
// This should either be set by ldflags, such as with
|
||||
// go build -ldflags "-X gitea.libretechconsulting.com/rmcguire/go-app/pkg/config.Version=$(VERSION)"
|
||||
// or allow this to use build meta. Will default to (devel)
|
||||
Version string `yaml:"version,omitempty" env:"APP_VERSION"`
|
||||
Logging *LogConfig `yaml:"logging,omitempty"`
|
||||
HTTP *HTTPConfig `yaml:"http,omitempty"`
|
||||
OTEL *OTELConfig `yaml:"otel,omitempty"`
|
||||
GRPC *GRPCConfig `yaml:"grpc,omitempty"`
|
||||
Version string `yaml:"version,omitempty" env:"APP_VERSION" json:"version,omitempty"`
|
||||
Logging *LogConfig `yaml:"logging,omitempty" json:"logging,omitempty"`
|
||||
HTTP *HTTPConfig `yaml:"http,omitempty" json:"http,omitempty"`
|
||||
OTEL *OTELConfig `yaml:"otel,omitempty" json:"otel,omitempty"`
|
||||
GRPC *GRPCConfig `yaml:"grpc,omitempty" json:"grpc,omitempty"`
|
||||
}
|
||||
|
||||
func (ac *AppConfig) HTTPEnabled() bool {
|
||||
|
@ -10,9 +10,9 @@ var defaultGRPCConfig = &GRPCConfig{
|
||||
}
|
||||
|
||||
type GRPCConfig struct {
|
||||
Enabled bool `yaml:"enabled" env:"APP_GRPC_ENABLED"`
|
||||
Listen string `yaml:"listen" env:"APP_GRPC_LISTEN"`
|
||||
LogRequests bool `yaml:"logRequests" env:"APP_GRPC_LOG_REQUESTS"`
|
||||
EnableReflection bool `yaml:"enableReflection" env:"APP_GRPC_ENABLE_REFLECTION"`
|
||||
EnableInstrumentation bool `yaml:"enableInstrumentation" env:"APP_GRPC_ENABLE_INSTRUMENTATION"` // requires OTEL
|
||||
Enabled bool `yaml:"enabled" env:"APP_GRPC_ENABLED" json:"enabled,omitempty"`
|
||||
Listen string `yaml:"listen" env:"APP_GRPC_LISTEN" json:"listen,omitempty"`
|
||||
LogRequests bool `yaml:"logRequests" env:"APP_GRPC_LOG_REQUESTS" json:"logRequests,omitempty"`
|
||||
EnableReflection bool `yaml:"enableReflection" env:"APP_GRPC_ENABLE_REFLECTION" json:"enableReflection,omitempty"`
|
||||
EnableInstrumentation bool `yaml:"enableInstrumentation" env:"APP_GRPC_ENABLE_INSTRUMENTATION" json:"enableInstrumentation,omitempty"` // requires OTEL
|
||||
}
|
||||
|
@ -14,13 +14,13 @@ var defaultHTTPConfig = &HTTPConfig{
|
||||
|
||||
// HTTP Configuration
|
||||
type HTTPConfig struct {
|
||||
Enabled bool `yaml:"enabled" env:"APP_HTTP_ENABLED"`
|
||||
Listen string `yaml:"listen,omitempty" env:"APP_HTTP_LISTEN"`
|
||||
LogRequests bool `yaml:"logRequests" env:"APP_HTTP_LOG_REQUESTS"`
|
||||
ReadTimeout string `yaml:"readTimeout" env:"APP_HTTP_READ_TIMEOUT"` // Go duration (e.g. 10s)
|
||||
WriteTimeout string `yaml:"writeTimeout" env:"APP_HTTP_WRITE_TIMEOUT"` // Go duration (e.g. 10s)
|
||||
IdleTimeout string `yaml:"idleTimeout" env:"APP_HTTP_IDLE_TIMEOUT"` // Go duration (e.g. 10s)
|
||||
rT *time.Duration `yaml:"rT" env:"rT"`
|
||||
wT *time.Duration `yaml:"wT" env:"wT"`
|
||||
iT *time.Duration `yaml:"iT" env:"iT"`
|
||||
Enabled bool `yaml:"enabled" env:"APP_HTTP_ENABLED" json:"enabled,omitempty"`
|
||||
Listen string `yaml:"listen,omitempty" env:"APP_HTTP_LISTEN" json:"listen,omitempty"`
|
||||
LogRequests bool `yaml:"logRequests" env:"APP_HTTP_LOG_REQUESTS" json:"logRequests,omitempty"`
|
||||
ReadTimeout string `yaml:"readTimeout" env:"APP_HTTP_READ_TIMEOUT" json:"readTimeout,omitempty"` // Go duration (e.g. 10s)
|
||||
WriteTimeout string `yaml:"writeTimeout" env:"APP_HTTP_WRITE_TIMEOUT" json:"writeTimeout,omitempty"` // Go duration (e.g. 10s)
|
||||
IdleTimeout string `yaml:"idleTimeout" env:"APP_HTTP_IDLE_TIMEOUT" json:"idleTimeout,omitempty"` // Go duration (e.g. 10s)
|
||||
rT *time.Duration
|
||||
wT *time.Duration
|
||||
iT *time.Duration
|
||||
}
|
||||
|
@ -10,11 +10,11 @@ var defaultLoggingConfig = &LogConfig{
|
||||
|
||||
// Logging Configuration
|
||||
type LogConfig struct {
|
||||
Enabled bool `yaml:"enabled,omitempty" env:"APP_LOG_ENABLED"`
|
||||
Level string `yaml:"level,omitempty" env:"APP_LOG_LEVEL"`
|
||||
Format LogFormat `yaml:"format,omitempty" env:"APP_LOG_FORMAT"`
|
||||
Output LogOutput `yaml:"output,omitempty" env:"APP_LOG_OUTPUT"`
|
||||
TimeFormat TimeFormat `yaml:"timeFormat,omitempty" env:"APP_LOG_TIME_FORMAT"`
|
||||
Enabled bool `yaml:"enabled,omitempty" env:"APP_LOG_ENABLED" json:"enabled,omitempty"`
|
||||
Level string `yaml:"level,omitempty" env:"APP_LOG_LEVEL" json:"level,omitempty"`
|
||||
Format LogFormat `yaml:"format,omitempty" env:"APP_LOG_FORMAT" json:"format,omitempty"`
|
||||
Output LogOutput `yaml:"output,omitempty" env:"APP_LOG_OUTPUT" json:"output,omitempty"`
|
||||
TimeFormat TimeFormat `yaml:"timeFormat,omitempty" env:"APP_LOG_TIME_FORMAT" json:"timeFormat,omitempty"`
|
||||
}
|
||||
|
||||
type LogFormat string
|
||||
|
@ -10,9 +10,9 @@ var defaultOTELConfig = &OTELConfig{
|
||||
|
||||
// OTEL Configuration
|
||||
type OTELConfig struct {
|
||||
Enabled bool `yaml:"enabled,omitempty" env:"APP_OTEL_ENABLED"`
|
||||
PrometheusEnabled bool `yaml:"prometheusEnabled,omitempty" env:"APP_OTEL_PROMETHEUS_ENABLED"`
|
||||
PrometheusPath string `yaml:"prometheusPath,omitempty" env:"APP_OTEL_PROMETHEUS_PATH"`
|
||||
StdoutEnabled bool `yaml:"stdoutEnabled,omitempty" env:"APP_OTEL_STDOUT_ENABLED"`
|
||||
MetricIntervalSecs int `yaml:"metricIntervalSecs,omitempty" env:"APP_OTEL_METRIC_INTERVAL_SECS"`
|
||||
Enabled bool `yaml:"enabled,omitempty" env:"APP_OTEL_ENABLED" json:"enabled,omitempty"`
|
||||
PrometheusEnabled bool `yaml:"prometheusEnabled,omitempty" env:"APP_OTEL_PROMETHEUS_ENABLED" json:"prometheusEnabled,omitempty"`
|
||||
PrometheusPath string `yaml:"prometheusPath,omitempty" env:"APP_OTEL_PROMETHEUS_PATH" json:"prometheusPath,omitempty"`
|
||||
StdoutEnabled bool `yaml:"stdoutEnabled,omitempty" env:"APP_OTEL_STDOUT_ENABLED" json:"stdoutEnabled,omitempty"`
|
||||
MetricIntervalSecs int `yaml:"metricIntervalSecs,omitempty" env:"APP_OTEL_METRIC_INTERVAL_SECS" json:"metricIntervalSecs,omitempty"`
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func configureLogger(cfg *config.LogConfig) (*zerolog.Logger, error) {
|
||||
TimeFormat: zerolog.TimeFieldFormat,
|
||||
}
|
||||
if cfg.TimeFormat == config.TimeFormatOff {
|
||||
consoleWriter.FormatTimestamp = func(_ interface{}) string {
|
||||
consoleWriter.FormatTimestamp = func(_ any) string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user