diff --git a/docs/07-config.mdx b/docs/07-config.mdx new file mode 100644 index 0000000..659e59f --- /dev/null +++ b/docs/07-config.mdx @@ -0,0 +1,35 @@ +--- +id: config +title: Config +sidebar_label: Config +sidebar_position: 7 +--- + +ogen could be configurated via `.ogen.yml` file: + +```yaml [title=".ogen.yml"] +# sets generator options. +generator: + # sets generator features. + features: + enable: + # Enables paths client generation + - "paths/client" + # Enables paths server generation + - "paths/server" + # Enables validation of client requests + - "client/request/validation" + # Enables validation of server responses + - "server/response/validation" + # Enables OpenTelemetry integration + - "ogen/otel" + disable_all: true +``` + +See full config example [here](https://github.com/ogen-go/ogen/blob/main/examples/_config/example_all.yml). + +### Features + +ogen provides a opt-in/opt-out mechanism for some features like OpenTelemetry integration. + +You can find a [complete feature list](https://github.com/ogen-go/ogen/blob/main/gen/features.go) in ogen repository. diff --git a/docs/07-faq.mdx b/docs/08-faq.mdx similarity index 59% rename from docs/07-faq.mdx rename to docs/08-faq.mdx index c621fd8..ddf5fb1 100644 --- a/docs/07-faq.mdx +++ b/docs/08-faq.mdx @@ -2,7 +2,7 @@ id: faq title: Frequently Asked Questions (FAQ) sidebar_label: FAQ -sidebar_position: 7 +sidebar_position: 8 --- ### How to set `404 Not Found` handler? @@ -116,75 +116,102 @@ func newServer() *http.Server { See [Static router](concepts/static_router.mdx#using-nethttp-middlewares) section for more details. -### How to use prometheus? +### How to use Prometheus? -To instrument server or client with OpenTelemetry and prometheus exporter, pass `oas.Option`: +See [OpenTelemetry Go SDK examples](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/c3c853808383d1bce596cbb910546c305c9187e8/examples/prometheus/main.go#L33-L38). + +Also, see [our `autometer` helper](https://pkg.go.dev/github.com/go-faster/sdk/autometer). + +Example setup: ```go package main import ( - "github.com/go-faster/errors" - promClient "github.com/prometheus/client_golang/prometheus" - "go.opentelemetry.io/otel/exporters/prometheus" - "go.opentelemetry.io/otel/sdk/export/metric/aggregation" - "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" - controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - selector "go.opentelemetry.io/otel/sdk/metric/selector/simple" - "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "context" + + "example/internal/oas" - "application/oas" + "github.com/prometheus/client_golang/prometheus" + otelprometheus "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" ) -// Resource returns new resource for application. -func Resource(name string) *resource.Resource { - r, _ := resource.Merge( +const appName = "my-ogen-service" + +// setupMeterProvider setups Prometheus instrumentation. +func setupMeterProvider(reg prometheus.Registerer) (metric.MeterProvider, ShutdownFunc, error) { + // Create an OpenTelemetry Resource description. + // + // See https://opentelemetry.io/docs/languages/go/resources/ + appResource, err := resource.Merge( resource.Default(), resource.NewWithAttributes( semconv.SchemaURL, - semconv.ServiceNameKey.String(name), + semconv.ServiceNameKey.String(appName), ), ) - return r -} + if err != nil { + return nil, nil, err + } -func newPrometheus(config prometheus.Config, options ...controller.Option) (*prometheus.Exporter, error) { - c := controller.New( - processor.NewFactory( - selector.NewWithHistogramDistribution( - histogram.WithExplicitBoundaries(config.DefaultHistogramBoundaries), - ), - aggregation.CumulativeTemporalitySelector(), - processor.WithMemory(true), - ), - options..., + // Create Prometheus exporter and given registerer. + exp, err := otelprometheus.New( + otelprometheus.WithRegisterer(reg), ) - return prometheus.New(config, c) -} + if err != nil { + return nil, nil, err + } -// newMeterProviderOption returns oas.Option for prometheus instrumentation. -func newMeterProviderOption() (oas.Option, error) { - registry := promClient.NewPedanticRegistry() - res := Resource("app") - promExporter, err := newPrometheus(prometheus.Config{ - DefaultHistogramBoundaries: promClient.DefBuckets, - - Registry: registry, - Gatherer: registry, - Registerer: registry, - }, - controller.WithCollectPeriod(0), - controller.WithResource(res), + mp := sdkmetric.NewMeterProvider( + sdkmetric.WithResource(appResource), + sdkmetric.WithReader(exp), ) + return mp, func(ctx context.Context) error { + // Flush collected metrics. + _ = mp.ForceFlush(ctx) + return mp.Shutdown(ctx) + }, nil +} + +type ShutdownFunc = func(ctx context.Context) error + +func createAPIServer(h oas.Handler) (_ *oas.Server, _ ShutdownFunc, rerr error) { + meterProvider, shutdown, err := setupMeterProvider(prometheus.DefaultRegisterer) + if err != nil { + return nil, nil, err + } + defer func() { + // Shutdown MeterProvider and release resources in case of error. + if rerr != nil { + shutdown(context.TODO()) + } + }() + + server, err := oas.NewServer(h, oas.WithMeterProvider(meterProvider)) if err != nil { - return nil, errors.Wrap(err, "prometheus") + return nil, nil, err } - return oas.WithMeterProvider(promExporter.MeterProvider()), nil + + // Returned `shutdown` function would stop metric collection. + return server, shutdown, nil } ``` ### How to add some tags to generated structures? [Use `x-oapi-codegen-extra-tags` extension.](spec/extensions.md#extra-struct-field-tags) + +### How to disable OpenTelemetry integration? + +Disable `ogen/otel` feature via [config](config): + +```yaml [title=".ogen.yml"] +generator: + features: + disable: + - "ogen/otel" +``` diff --git a/docs/concepts/convenient_errors.md b/docs/concepts/convenient_errors.md index 729301c..93bc883 100644 --- a/docs/concepts/convenient_errors.md +++ b/docs/concepts/convenient_errors.md @@ -12,6 +12,7 @@ If spec meets all following requirements: then `ogen` generates special handler for errors returned by `Handler` implementation and does not generate default response variant. For example: + ```yaml openapi: 3.0.3 info: @@ -87,9 +88,12 @@ type Handler interface { ### Force or Disable `Convenient errors` -Use `--convenient-errors` option - -- `auto` (default) generates `NewError` if possible -- `on` tells generator to fail if spec does not meet requirements -- `off` disables `Convenient errors` at all +Use `generator.convenient_errors` option +```yaml +generator: + # `auto` (default) generates `NewError` if possible + # `on` tells generator to fail if spec does not meet requirements + # `off` disables `Convenient errors` at all + convenient_errors: "auto" +``` diff --git a/docs/spec/extensions.md b/docs/spec/extensions.md index 9a1fa35..a8522a8 100644 --- a/docs/spec/extensions.md +++ b/docs/spec/extensions.md @@ -106,6 +106,37 @@ Optionally, type name can be specified by `x-ogen-name`, for example: } ``` +### Custom field name + +Optionally, type name can be specified by `x-ogen-properties`, for example: + +```yaml +components: + schemas: + Node: + type: object + properties: + parent: + $ref: "#/components/schemas/Node" + child: + $ref: "#/components/schemas/Node" + x-ogen-properties: + parent: + name: "Prev" + child: + name: "Next" +``` + +The generated source code looks like: + +```go +// Ref: #/components/schemas/Node +type Node struct { + Prev *Node `json:"parent"` + Next *Node `json:"child"` +} +``` + ### Extra struct field tags Optionally, additional Go struct field tags can be specified by `x-oapi-codegen-extra-tags`, for example: @@ -151,3 +182,50 @@ requestBody: items: type: number ``` + +### Operation groups + +Optionally, operations can be grouped so a handler interface will be generated for each group of operations. +This is useful for organizing operations for large APIs. + +The group for operations on a path or individual operations can be specified by `x-ogen-operation-group`, for example: + +```yaml +paths: + /images: + x-ogen-operation-group: Images + get: + operationId: listImages + ... + /images/{imageID}: + x-ogen-operation-group: Images + get: + operationId: getImageByID + ... + /users: + x-ogen-operation-group: Users + get: + operationId: listUsers + ... +``` + +The generated handler interfaces look like this: + +```go +// x-ogen-operation-group: Images +type ImagesHandler interface { + ListImages(ctx context.Context, req *ListImagesRequest) (*ListImagesResponse, error) + GetImageByID(ctx context.Context, req *GetImagesByIDRequest) (*GetImagesByIDResponse, error) +} + +// x-ogen-operation-group: Users +type UsersHandler interface { + ListUsers(ctx context.Context, req *ListUsersRequest) (*ListUsersResponse, error) +} + +type Handler interface { + ImagesHandler + UsersHandler + // All un-grouped operations will be on this interface +} +```