Initial commit

This commit is contained in:
2023-03-26 16:11:00 +03:00
commit 92469fa3a2
47 changed files with 5610 additions and 0 deletions

3
api/gen.go Normal file
View File

@@ -0,0 +1,3 @@
package api
//go:generate go run github.com/ogen-go/ogen/cmd/ogen@v0.60.1 --target ./openapi -package openapi --clean openapi.yaml

174
api/openapi.yaml Normal file
View File

@@ -0,0 +1,174 @@
openapi: 3.0.1
info:
title: Sample API
description: API description in Markdown.
version: 1.0.0
servers:
- url: 'https://api.example.com'
paths:
/pages:
get:
operationId: getPages
summary: Get all pages
responses:
200:
description: All pages data
content:
application/json:
schema:
$ref: '#/components/schemas/pages'
default:
$ref: '#/components/responses/undefinedError'
post:
operationId: addPage
summary: Add new page
requestBody:
content:
application/json:
schema:
type: object
properties:
url:
type: string
description:
type: string
formats:
type: array
items:
$ref: '#/components/schemas/format'
required:
- url
responses:
201:
description: Page added
content:
application/json:
schema:
$ref: '#/components/schemas/page'
default:
$ref: '#/components/responses/undefinedError'
/pages/{id}:
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
get:
operationId: getPage
description: Get page details
responses:
200:
description: Page data
content:
application/json:
schema:
$ref: '#/components/schemas/pageWithResults'
404:
description: Page not found
default:
$ref: '#/components/responses/undefinedError'
components:
responses:
undefinedError:
description: Undefined Error
content:
application/json:
schema:
$ref: '#/components/schemas/error'
schemas:
format:
type: string
enum:
- all
- pdf
- single_page
- headers
error:
type: object
properties:
message:
type: string
localized:
type: string
required:
- message
pages:
type: array
items:
$ref: '#/components/schemas/page'
page:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
created:
type: string
format: date-time
formats:
type: array
items:
$ref: '#/components/schemas/format'
status:
$ref: '#/components/schemas/status'
required:
- id
- url
- formats
- status
- created
result:
type: object
properties:
format:
$ref: '#/components/schemas/format'
error:
type: string
files:
type: array
items:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
mimetype:
type: string
size:
type: integer
format: int64
required:
- id
- name
- mimetype
- size
required:
- format
- files
pageWithResults:
allOf:
- $ref: '#/components/schemas/page'
- type: object
properties:
results:
type: array
items:
$ref: '#/components/schemas/result'
required:
- results
status:
type: string
enum:
- new
- processing
- done
- failed
- with_errors

277
api/openapi/oas_cfg_gen.go Normal file
View File

@@ -0,0 +1,277 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"net/http"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/trace"
ht "github.com/ogen-go/ogen/http"
"github.com/ogen-go/ogen/middleware"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/otelogen"
)
var (
// Allocate option closure once.
clientSpanKind = trace.WithSpanKind(trace.SpanKindClient)
// Allocate option closure once.
serverSpanKind = trace.WithSpanKind(trace.SpanKindServer)
)
type (
optionFunc[C any] func(*C)
otelOptionFunc func(*otelConfig)
)
type otelConfig struct {
TracerProvider trace.TracerProvider
Tracer trace.Tracer
MeterProvider metric.MeterProvider
Meter metric.Meter
}
func (cfg *otelConfig) initOTEL() {
if cfg.TracerProvider == nil {
cfg.TracerProvider = otel.GetTracerProvider()
}
if cfg.MeterProvider == nil {
cfg.MeterProvider = metric.NewNoopMeterProvider()
}
cfg.Tracer = cfg.TracerProvider.Tracer(otelogen.Name,
trace.WithInstrumentationVersion(otelogen.SemVersion()),
)
cfg.Meter = cfg.MeterProvider.Meter(otelogen.Name)
}
// ErrorHandler is error handler.
type ErrorHandler = ogenerrors.ErrorHandler
type serverConfig struct {
otelConfig
NotFound http.HandlerFunc
MethodNotAllowed func(w http.ResponseWriter, r *http.Request, allowed string)
ErrorHandler ErrorHandler
Prefix string
Middleware Middleware
MaxMultipartMemory int64
}
// ServerOption is server config option.
type ServerOption interface {
applyServer(*serverConfig)
}
var _ = []ServerOption{
(optionFunc[serverConfig])(nil),
(otelOptionFunc)(nil),
}
func (o optionFunc[C]) applyServer(c *C) {
o(c)
}
func (o otelOptionFunc) applyServer(c *serverConfig) {
o(&c.otelConfig)
}
func newServerConfig(opts ...ServerOption) serverConfig {
cfg := serverConfig{
NotFound: http.NotFound,
MethodNotAllowed: func(w http.ResponseWriter, r *http.Request, allowed string) {
w.Header().Set("Allow", allowed)
w.WriteHeader(http.StatusMethodNotAllowed)
},
ErrorHandler: ogenerrors.DefaultErrorHandler,
Middleware: nil,
MaxMultipartMemory: 32 << 20, // 32 MB
}
for _, opt := range opts {
opt.applyServer(&cfg)
}
cfg.initOTEL()
return cfg
}
type baseServer struct {
cfg serverConfig
requests instrument.Int64Counter
errors instrument.Int64Counter
duration instrument.Int64Histogram
}
func (s baseServer) notFound(w http.ResponseWriter, r *http.Request) {
s.cfg.NotFound(w, r)
}
func (s baseServer) notAllowed(w http.ResponseWriter, r *http.Request, allowed string) {
s.cfg.MethodNotAllowed(w, r, allowed)
}
func (cfg serverConfig) baseServer() (s baseServer, err error) {
s = baseServer{cfg: cfg}
if s.requests, err = s.cfg.Meter.Int64Counter(otelogen.ServerRequestCount); err != nil {
return s, err
}
if s.errors, err = s.cfg.Meter.Int64Counter(otelogen.ServerErrorsCount); err != nil {
return s, err
}
if s.duration, err = s.cfg.Meter.Int64Histogram(otelogen.ServerDuration); err != nil {
return s, err
}
return s, nil
}
type clientConfig struct {
otelConfig
Client ht.Client
}
// ClientOption is client config option.
type ClientOption interface {
applyClient(*clientConfig)
}
var _ = []ClientOption{
(optionFunc[clientConfig])(nil),
(otelOptionFunc)(nil),
}
func (o optionFunc[C]) applyClient(c *C) {
o(c)
}
func (o otelOptionFunc) applyClient(c *clientConfig) {
o(&c.otelConfig)
}
func newClientConfig(opts ...ClientOption) clientConfig {
cfg := clientConfig{
Client: http.DefaultClient,
}
for _, opt := range opts {
opt.applyClient(&cfg)
}
cfg.initOTEL()
return cfg
}
type baseClient struct {
cfg clientConfig
requests instrument.Int64Counter
errors instrument.Int64Counter
duration instrument.Int64Histogram
}
func (cfg clientConfig) baseClient() (c baseClient, err error) {
c = baseClient{cfg: cfg}
if c.requests, err = c.cfg.Meter.Int64Counter(otelogen.ClientRequestCount); err != nil {
return c, err
}
if c.errors, err = c.cfg.Meter.Int64Counter(otelogen.ClientErrorsCount); err != nil {
return c, err
}
if c.duration, err = c.cfg.Meter.Int64Histogram(otelogen.ClientDuration); err != nil {
return c, err
}
return c, nil
}
// Option is config option.
type Option interface {
ServerOption
ClientOption
}
// WithTracerProvider specifies a tracer provider to use for creating a tracer.
//
// If none is specified, the global provider is used.
func WithTracerProvider(provider trace.TracerProvider) Option {
return otelOptionFunc(func(cfg *otelConfig) {
if provider != nil {
cfg.TracerProvider = provider
}
})
}
// WithMeterProvider specifies a meter provider to use for creating a meter.
//
// If none is specified, the metric.NewNoopMeterProvider is used.
func WithMeterProvider(provider metric.MeterProvider) Option {
return otelOptionFunc(func(cfg *otelConfig) {
if provider != nil {
cfg.MeterProvider = provider
}
})
}
// WithClient specifies http client to use.
func WithClient(client ht.Client) ClientOption {
return optionFunc[clientConfig](func(cfg *clientConfig) {
if client != nil {
cfg.Client = client
}
})
}
// WithNotFound specifies Not Found handler to use.
func WithNotFound(notFound http.HandlerFunc) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if notFound != nil {
cfg.NotFound = notFound
}
})
}
// WithMethodNotAllowed specifies Method Not Allowed handler to use.
func WithMethodNotAllowed(methodNotAllowed func(w http.ResponseWriter, r *http.Request, allowed string)) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if methodNotAllowed != nil {
cfg.MethodNotAllowed = methodNotAllowed
}
})
}
// WithErrorHandler specifies error handler to use.
func WithErrorHandler(h ErrorHandler) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if h != nil {
cfg.ErrorHandler = h
}
})
}
// WithPathPrefix specifies server path prefix.
func WithPathPrefix(prefix string) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
cfg.Prefix = prefix
})
}
// WithMiddleware specifies middlewares to use.
func WithMiddleware(m ...Middleware) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
switch len(m) {
case 0:
cfg.Middleware = nil
case 1:
cfg.Middleware = m[0]
default:
cfg.Middleware = middleware.ChainMiddlewares(m...)
}
})
}
// WithMaxMultipartMemory specifies limit of memory for storing file parts.
// File parts which can't be stored in memory will be stored on disk in temporary files.
func WithMaxMultipartMemory(max int64) ServerOption {
return optionFunc[serverConfig](func(cfg *serverConfig) {
if max > 0 {
cfg.MaxMultipartMemory = max
}
})
}

View File

@@ -0,0 +1,319 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"context"
"net/url"
"strings"
"time"
"github.com/go-faster/errors"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
"github.com/ogen-go/ogen/conv"
ht "github.com/ogen-go/ogen/http"
"github.com/ogen-go/ogen/otelogen"
"github.com/ogen-go/ogen/uri"
)
// Client implements OAS client.
type Client struct {
serverURL *url.URL
baseClient
}
type errorHandler interface {
NewError(ctx context.Context, err error) *ErrorStatusCode
}
var _ Handler = struct {
errorHandler
*Client
}{}
func trimTrailingSlashes(u *url.URL) {
u.Path = strings.TrimRight(u.Path, "/")
u.RawPath = strings.TrimRight(u.RawPath, "/")
}
// NewClient initializes new Client defined by OAS.
func NewClient(serverURL string, opts ...ClientOption) (*Client, error) {
u, err := url.Parse(serverURL)
if err != nil {
return nil, err
}
trimTrailingSlashes(u)
c, err := newClientConfig(opts...).baseClient()
if err != nil {
return nil, err
}
return &Client{
serverURL: u,
baseClient: c,
}, nil
}
type serverURLKey struct{}
// WithServerURL sets context key to override server URL.
func WithServerURL(ctx context.Context, u *url.URL) context.Context {
return context.WithValue(ctx, serverURLKey{}, u)
}
func (c *Client) requestURL(ctx context.Context) *url.URL {
u, ok := ctx.Value(serverURLKey{}).(*url.URL)
if !ok {
return c.serverURL
}
return u
}
// AddPage invokes addPage operation.
//
// Add new page.
//
// POST /pages
func (c *Client) AddPage(ctx context.Context, request OptAddPageReq) (*Page, error) {
res, err := c.sendAddPage(ctx, request)
_ = res
return res, err
}
func (c *Client) sendAddPage(ctx context.Context, request OptAddPageReq) (res *Page, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("addPage"),
}
// Validate request before sending.
if err := func() error {
if request.Set {
if err := func() error {
if err := request.Value.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
return res, errors.Wrap(err, "validate")
}
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
}()
// Increment request counter.
c.requests.Add(ctx, 1, otelAttrs...)
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, "AddPage",
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, otelAttrs...)
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [1]string
pathParts[0] = "/pages"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "POST", u, nil)
if err != nil {
return res, errors.Wrap(err, "create request")
}
if err := encodeAddPageRequest(request, r); err != nil {
return res, errors.Wrap(err, "encode request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeAddPageResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// GetPage invokes getPage operation.
//
// Get page details.
//
// GET /pages/{id}
func (c *Client) GetPage(ctx context.Context, params GetPageParams) (GetPageRes, error) {
res, err := c.sendGetPage(ctx, params)
_ = res
return res, err
}
func (c *Client) sendGetPage(ctx context.Context, params GetPageParams) (res GetPageRes, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("getPage"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
}()
// Increment request counter.
c.requests.Add(ctx, 1, otelAttrs...)
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, "GetPage",
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, otelAttrs...)
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [2]string
pathParts[0] = "/pages/"
{
// Encode "id" parameter.
e := uri.NewPathEncoder(uri.PathEncoderConfig{
Param: "id",
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
return e.EncodeValue(conv.UUIDToString(params.ID))
}(); err != nil {
return res, errors.Wrap(err, "encode path")
}
encoded, err := e.Result()
if err != nil {
return res, errors.Wrap(err, "encode path")
}
pathParts[1] = encoded
}
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "GET", u, nil)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeGetPageResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}
// GetPages invokes getPages operation.
//
// Get all pages.
//
// GET /pages
func (c *Client) GetPages(ctx context.Context) (Pages, error) {
res, err := c.sendGetPages(ctx)
_ = res
return res, err
}
func (c *Client) sendGetPages(ctx context.Context) (res Pages, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("getPages"),
}
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
c.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
}()
// Increment request counter.
c.requests.Add(ctx, 1, otelAttrs...)
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, "GetPages",
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
// Track stage for error reporting.
var stage string
defer func() {
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
c.errors.Add(ctx, 1, otelAttrs...)
}
span.End()
}()
stage = "BuildURL"
u := uri.Clone(c.requestURL(ctx))
var pathParts [1]string
pathParts[0] = "/pages"
uri.AddPathParts(u, pathParts[:]...)
stage = "EncodeRequest"
r, err := ht.NewRequest(ctx, "GET", u, nil)
if err != nil {
return res, errors.Wrap(err, "create request")
}
stage = "SendRequest"
resp, err := c.cfg.Client.Do(r)
if err != nil {
return res, errors.Wrap(err, "do request")
}
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeGetPagesResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
return result, nil
}

View File

@@ -0,0 +1,331 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"context"
"net/http"
"time"
"github.com/go-faster/errors"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
"go.opentelemetry.io/otel/trace"
ht "github.com/ogen-go/ogen/http"
"github.com/ogen-go/ogen/middleware"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/otelogen"
)
// handleAddPageRequest handles addPage operation.
//
// Add new page.
//
// POST /pages
func (s *Server) handleAddPageRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("addPage"),
semconv.HTTPMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/pages"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), "AddPage",
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
s.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
}()
// Increment request counter.
s.requests.Add(ctx, 1, otelAttrs...)
var (
recordError = func(stage string, err error) {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
s.errors.Add(ctx, 1, otelAttrs...)
}
err error
opErrContext = ogenerrors.OperationContext{
Name: "AddPage",
ID: "addPage",
}
)
request, close, err := s.decodeAddPageRequest(r)
if err != nil {
err = &ogenerrors.DecodeRequestError{
OperationContext: opErrContext,
Err: err,
}
recordError("DecodeRequest", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
defer func() {
if err := close(); err != nil {
recordError("CloseRequest", err)
}
}()
var response *Page
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: "AddPage",
OperationID: "addPage",
Body: request,
Params: middleware.Parameters{},
Raw: r,
}
type (
Request = OptAddPageReq
Params = struct{}
Response = *Page
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
nil,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
response, err = s.h.AddPage(ctx, request)
return response, err
},
)
} else {
response, err = s.h.AddPage(ctx, request)
}
if err != nil {
recordError("Internal", err)
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
encodeErrorResponse(errRes, w, span)
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
encodeErrorResponse(s.h.NewError(ctx, err), w, span)
return
}
if err := encodeAddPageResponse(response, w, span); err != nil {
recordError("EncodeResponse", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
}
// handleGetPageRequest handles getPage operation.
//
// Get page details.
//
// GET /pages/{id}
func (s *Server) handleGetPageRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("getPage"),
semconv.HTTPMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/pages/{id}"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), "GetPage",
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
s.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
}()
// Increment request counter.
s.requests.Add(ctx, 1, otelAttrs...)
var (
recordError = func(stage string, err error) {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
s.errors.Add(ctx, 1, otelAttrs...)
}
err error
opErrContext = ogenerrors.OperationContext{
Name: "GetPage",
ID: "getPage",
}
)
params, err := decodeGetPageParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
Err: err,
}
recordError("DecodeParams", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
var response GetPageRes
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: "GetPage",
OperationID: "getPage",
Body: nil,
Params: middleware.Parameters{
{
Name: "id",
In: "path",
}: params.ID,
},
Raw: r,
}
type (
Request = struct{}
Params = GetPageParams
Response = GetPageRes
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
unpackGetPageParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
response, err = s.h.GetPage(ctx, params)
return response, err
},
)
} else {
response, err = s.h.GetPage(ctx, params)
}
if err != nil {
recordError("Internal", err)
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
encodeErrorResponse(errRes, w, span)
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
encodeErrorResponse(s.h.NewError(ctx, err), w, span)
return
}
if err := encodeGetPageResponse(response, w, span); err != nil {
recordError("EncodeResponse", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
}
// handleGetPagesRequest handles getPages operation.
//
// Get all pages.
//
// GET /pages
func (s *Server) handleGetPagesRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("getPages"),
semconv.HTTPMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/pages"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), "GetPages",
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
defer span.End()
// Run stopwatch.
startTime := time.Now()
defer func() {
elapsedDuration := time.Since(startTime)
s.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...)
}()
// Increment request counter.
s.requests.Add(ctx, 1, otelAttrs...)
var (
recordError = func(stage string, err error) {
span.RecordError(err)
span.SetStatus(codes.Error, stage)
s.errors.Add(ctx, 1, otelAttrs...)
}
err error
)
var response Pages
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: "GetPages",
OperationID: "getPages",
Body: nil,
Params: middleware.Parameters{},
Raw: r,
}
type (
Request = struct{}
Params = struct{}
Response = Pages
)
response, err = middleware.HookMiddleware[
Request,
Params,
Response,
](
m,
mreq,
nil,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
response, err = s.h.GetPages(ctx)
return response, err
},
)
} else {
response, err = s.h.GetPages(ctx)
}
if err != nil {
recordError("Internal", err)
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {
encodeErrorResponse(errRes, w, span)
return
}
if errors.Is(err, ht.ErrNotImplemented) {
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
encodeErrorResponse(s.h.NewError(ctx, err), w, span)
return
}
if err := encodeGetPagesResponse(response, w, span); err != nil {
recordError("EncodeResponse", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
}

View File

@@ -0,0 +1,6 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
type GetPageRes interface {
getPageRes()
}

1151
api/openapi/oas_json_gen.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"github.com/ogen-go/ogen/middleware"
)
// Middleware is middleware type.
type Middleware = middleware.Middleware

View File

@@ -0,0 +1,82 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"net/http"
"net/url"
"github.com/go-faster/errors"
"github.com/google/uuid"
"github.com/ogen-go/ogen/conv"
"github.com/ogen-go/ogen/middleware"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/uri"
"github.com/ogen-go/ogen/validate"
)
// GetPageParams is parameters of getPage operation.
type GetPageParams struct {
ID uuid.UUID
}
func unpackGetPageParams(packed middleware.Parameters) (params GetPageParams) {
{
key := middleware.ParameterKey{
Name: "id",
In: "path",
}
params.ID = packed[key].(uuid.UUID)
}
return params
}
func decodeGetPageParams(args [1]string, argsEscaped bool, r *http.Request) (params GetPageParams, _ error) {
// Decode path: id.
if err := func() error {
param := args[0]
if argsEscaped {
unescaped, err := url.PathUnescape(args[0])
if err != nil {
return errors.Wrap(err, "unescape path")
}
param = unescaped
}
if len(param) > 0 {
d := uri.NewPathDecoder(uri.PathDecoderConfig{
Param: "id",
Value: param,
Style: uri.PathStyleSimple,
Explode: false,
})
if err := func() error {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToUUID(val)
if err != nil {
return err
}
params.ID = c
return nil
}(); err != nil {
return err
}
} else {
return validate.ErrFieldRequired
}
return nil
}(); err != nil {
return params, &ogenerrors.DecodeParamError{
Name: "id",
In: "path",
Err: err,
}
}
return params, nil
}

View File

@@ -0,0 +1,98 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"io"
"mime"
"net/http"
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"go.uber.org/multierr"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/validate"
)
func (s *Server) decodeAddPageRequest(r *http.Request) (
req OptAddPageReq,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = multierr.Append(rerr, close())
}
}()
if _, ok := r.Header["Content-Type"]; !ok && r.ContentLength == 0 {
return req, close, nil
}
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, nil
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, nil
}
d := jx.DecodeBytes(buf)
var request OptAddPageReq
if err := func() error {
request.Reset()
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if request.Set {
if err := func() error {
if err := request.Value.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}

View File

@@ -0,0 +1,32 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"bytes"
"net/http"
"github.com/go-faster/jx"
ht "github.com/ogen-go/ogen/http"
)
func encodeAddPageRequest(
req OptAddPageReq,
r *http.Request,
) error {
const contentType = "application/json"
if !req.Set {
// Keep request with empty body if value is not set.
return nil
}
e := jx.GetEncoder()
{
if req.Set {
req.Encode(e)
}
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}

View File

@@ -0,0 +1,267 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"io"
"mime"
"net/http"
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/validate"
)
func decodeAddPageResponse(resp *http.Response) (res *Page, err error) {
switch resp.StatusCode {
case 201:
// Code 201.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Page
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &response, nil
default:
return res, validate.InvalidContentType(ct)
}
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrap(err, "default")
}
return res, errors.Wrap(defRes, "error")
}
func decodeGetPageResponse(resp *http.Response) (res GetPageRes, err error) {
switch resp.StatusCode {
case 200:
// Code 200.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response PageWithResults
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &response, nil
default:
return res, validate.InvalidContentType(ct)
}
case 404:
// Code 404.
return &GetPageNotFound{}, nil
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrap(err, "default")
}
return res, errors.Wrap(defRes, "error")
}
func decodeGetPagesResponse(resp *http.Response) (res Pages, err error) {
switch resp.StatusCode {
case 200:
// Code 200.
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Pages
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return response, nil
default:
return res, validate.InvalidContentType(ct)
}
}
// Convenient error response.
defRes, err := func() (res *ErrorStatusCode, err error) {
ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if err != nil {
return res, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
buf, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
d := jx.DecodeBytes(buf)
var response Error
if err := func() error {
if err := response.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return res, err
}
return &ErrorStatusCode{
StatusCode: resp.StatusCode,
Response: response,
}, nil
default:
return res, validate.InvalidContentType(ct)
}
}()
if err != nil {
return res, errors.Wrap(err, "default")
}
return res, errors.Wrap(defRes, "error")
}

View File

@@ -0,0 +1,87 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"net/http"
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
func encodeAddPageResponse(response *Page, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := jx.GetEncoder()
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeGetPageResponse(response GetPageRes, w http.ResponseWriter, span trace.Span) error {
switch response := response.(type) {
case *PageWithResults:
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := jx.GetEncoder()
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
case *GetPageNotFound:
w.WriteHeader(404)
span.SetStatus(codes.Error, http.StatusText(404))
return nil
default:
return errors.Errorf("unexpected response type: %T", response)
}
}
func encodeGetPagesResponse(response Pages, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := jx.GetEncoder()
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeErrorResponse(response *ErrorStatusCode, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json")
code := response.StatusCode
if code == 0 {
// Set default status code.
code = http.StatusOK
}
w.WriteHeader(code)
st := http.StatusText(code)
if code >= http.StatusBadRequest {
span.SetStatus(codes.Error, st)
} else {
span.SetStatus(codes.Ok, st)
}
e := jx.GetEncoder()
response.Response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}

View File

@@ -0,0 +1,220 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"net/http"
"net/url"
"strings"
"github.com/ogen-go/ogen/uri"
)
// ServeHTTP serves http request as defined by OpenAPI v3 specification,
// calling handler that matches the path or returning not found error.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
elem := r.URL.Path
elemIsEscaped := false
if rawPath := r.URL.RawPath; rawPath != "" {
if normalized, ok := uri.NormalizeEscapedPath(rawPath); ok {
elem = normalized
elemIsEscaped = strings.ContainsRune(elem, '%')
}
}
if prefix := s.cfg.Prefix; len(prefix) > 0 {
if strings.HasPrefix(elem, prefix) {
// Cut prefix from the path.
elem = strings.TrimPrefix(elem, prefix)
} else {
// Prefix doesn't match.
s.notFound(w, r)
return
}
}
if len(elem) == 0 {
s.notFound(w, r)
return
}
args := [1]string{}
// Static code generated router with unwrapped path search.
switch {
default:
if len(elem) == 0 {
break
}
switch elem[0] {
case '/': // Prefix: "/pages"
if l := len("/pages"); len(elem) >= l && elem[0:l] == "/pages" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
switch r.Method {
case "GET":
s.handleGetPagesRequest([0]string{}, elemIsEscaped, w, r)
case "POST":
s.handleAddPageRequest([0]string{}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "GET,POST")
}
return
}
switch elem[0] {
case '/': // Prefix: "/"
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
break
}
// Param: "id"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
// Leaf node.
switch r.Method {
case "GET":
s.handleGetPageRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "GET")
}
return
}
}
}
}
s.notFound(w, r)
}
// Route is route object.
type Route struct {
name string
operationID string
pathPattern string
count int
args [1]string
}
// Name returns ogen operation name.
//
// It is guaranteed to be unique and not empty.
func (r Route) Name() string {
return r.name
}
// OperationID returns OpenAPI operationId.
func (r Route) OperationID() string {
return r.operationID
}
// PathPattern returns OpenAPI path.
func (r Route) PathPattern() string {
return r.pathPattern
}
// Args returns parsed arguments.
func (r Route) Args() []string {
return r.args[:r.count]
}
// FindRoute finds Route for given method and path.
//
// Note: this method does not unescape path or handle reserved characters in path properly. Use FindPath instead.
func (s *Server) FindRoute(method, path string) (Route, bool) {
return s.FindPath(method, &url.URL{Path: path})
}
// FindPath finds Route for given method and URL.
func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
var (
elem = u.Path
args = r.args
)
if rawPath := u.RawPath; rawPath != "" {
if normalized, ok := uri.NormalizeEscapedPath(rawPath); ok {
elem = normalized
}
defer func() {
for i, arg := range r.args[:r.count] {
if unescaped, err := url.PathUnescape(arg); err == nil {
r.args[i] = unescaped
}
}
}()
}
// Static code generated router with unwrapped path search.
switch {
default:
if len(elem) == 0 {
break
}
switch elem[0] {
case '/': // Prefix: "/pages"
if l := len("/pages"); len(elem) >= l && elem[0:l] == "/pages" {
elem = elem[l:]
} else {
break
}
if len(elem) == 0 {
switch method {
case "GET":
r.name = "GetPages"
r.operationID = "getPages"
r.pathPattern = "/pages"
r.args = args
r.count = 0
return r, true
case "POST":
r.name = "AddPage"
r.operationID = "addPage"
r.pathPattern = "/pages"
r.args = args
r.count = 0
return r, true
default:
return
}
}
switch elem[0] {
case '/': // Prefix: "/"
if l := len("/"); len(elem) >= l && elem[0:l] == "/" {
elem = elem[l:]
} else {
break
}
// Param: "id"
// Leaf parameter
args[0] = elem
elem = ""
if len(elem) == 0 {
switch method {
case "GET":
// Leaf: GetPage
r.name = "GetPage"
r.operationID = "getPage"
r.pathPattern = "/pages/{id}"
r.args = args
r.count = 1
return r, true
default:
return
}
}
}
}
}
return r, false
}

View File

@@ -0,0 +1,516 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"fmt"
"time"
"github.com/go-faster/errors"
"github.com/google/uuid"
)
func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
}
type AddPageReq struct {
URL string `json:"url"`
Description OptString `json:"description"`
Formats []Format `json:"formats"`
}
// GetURL returns the value of URL.
func (s *AddPageReq) GetURL() string {
return s.URL
}
// GetDescription returns the value of Description.
func (s *AddPageReq) GetDescription() OptString {
return s.Description
}
// GetFormats returns the value of Formats.
func (s *AddPageReq) GetFormats() []Format {
return s.Formats
}
// SetURL sets the value of URL.
func (s *AddPageReq) SetURL(val string) {
s.URL = val
}
// SetDescription sets the value of Description.
func (s *AddPageReq) SetDescription(val OptString) {
s.Description = val
}
// SetFormats sets the value of Formats.
func (s *AddPageReq) SetFormats(val []Format) {
s.Formats = val
}
// Ref: #/components/schemas/error
type Error struct {
Message string `json:"message"`
Localized OptString `json:"localized"`
}
// GetMessage returns the value of Message.
func (s *Error) GetMessage() string {
return s.Message
}
// GetLocalized returns the value of Localized.
func (s *Error) GetLocalized() OptString {
return s.Localized
}
// SetMessage sets the value of Message.
func (s *Error) SetMessage(val string) {
s.Message = val
}
// SetLocalized sets the value of Localized.
func (s *Error) SetLocalized(val OptString) {
s.Localized = val
}
// ErrorStatusCode wraps Error with StatusCode.
type ErrorStatusCode struct {
StatusCode int
Response Error
}
// GetStatusCode returns the value of StatusCode.
func (s *ErrorStatusCode) GetStatusCode() int {
return s.StatusCode
}
// GetResponse returns the value of Response.
func (s *ErrorStatusCode) GetResponse() Error {
return s.Response
}
// SetStatusCode sets the value of StatusCode.
func (s *ErrorStatusCode) SetStatusCode(val int) {
s.StatusCode = val
}
// SetResponse sets the value of Response.
func (s *ErrorStatusCode) SetResponse(val Error) {
s.Response = val
}
// Ref: #/components/schemas/format
type Format string
const (
FormatAll Format = "all"
FormatPdf Format = "pdf"
FormatSinglePage Format = "single_page"
FormatHeaders Format = "headers"
)
// MarshalText implements encoding.TextMarshaler.
func (s Format) MarshalText() ([]byte, error) {
switch s {
case FormatAll:
return []byte(s), nil
case FormatPdf:
return []byte(s), nil
case FormatSinglePage:
return []byte(s), nil
case FormatHeaders:
return []byte(s), nil
default:
return nil, errors.Errorf("invalid value: %q", s)
}
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (s *Format) UnmarshalText(data []byte) error {
switch Format(data) {
case FormatAll:
*s = FormatAll
return nil
case FormatPdf:
*s = FormatPdf
return nil
case FormatSinglePage:
*s = FormatSinglePage
return nil
case FormatHeaders:
*s = FormatHeaders
return nil
default:
return errors.Errorf("invalid value: %q", data)
}
}
// GetPageNotFound is response for GetPage operation.
type GetPageNotFound struct{}
func (*GetPageNotFound) getPageRes() {}
// NewOptAddPageReq returns new OptAddPageReq with value set to v.
func NewOptAddPageReq(v AddPageReq) OptAddPageReq {
return OptAddPageReq{
Value: v,
Set: true,
}
}
// OptAddPageReq is optional AddPageReq.
type OptAddPageReq struct {
Value AddPageReq
Set bool
}
// IsSet returns true if OptAddPageReq was set.
func (o OptAddPageReq) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptAddPageReq) Reset() {
var v AddPageReq
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptAddPageReq) SetTo(v AddPageReq) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptAddPageReq) Get() (v AddPageReq, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptAddPageReq) Or(d AddPageReq) AddPageReq {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptString returns new OptString with value set to v.
func NewOptString(v string) OptString {
return OptString{
Value: v,
Set: true,
}
}
// OptString is optional string.
type OptString struct {
Value string
Set bool
}
// IsSet returns true if OptString was set.
func (o OptString) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptString) Reset() {
var v string
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptString) SetTo(v string) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptString) Get() (v string, ok bool) {
if !o.Set {
return v, false
}
return o.Value, true
}
// Or returns value if set, or given parameter if does not.
func (o OptString) Or(d string) string {
if v, ok := o.Get(); ok {
return v
}
return d
}
// Ref: #/components/schemas/page
type Page struct {
ID uuid.UUID `json:"id"`
URL string `json:"url"`
Created time.Time `json:"created"`
Formats []Format `json:"formats"`
Status Status `json:"status"`
}
// GetID returns the value of ID.
func (s *Page) GetID() uuid.UUID {
return s.ID
}
// GetURL returns the value of URL.
func (s *Page) GetURL() string {
return s.URL
}
// GetCreated returns the value of Created.
func (s *Page) GetCreated() time.Time {
return s.Created
}
// GetFormats returns the value of Formats.
func (s *Page) GetFormats() []Format {
return s.Formats
}
// GetStatus returns the value of Status.
func (s *Page) GetStatus() Status {
return s.Status
}
// SetID sets the value of ID.
func (s *Page) SetID(val uuid.UUID) {
s.ID = val
}
// SetURL sets the value of URL.
func (s *Page) SetURL(val string) {
s.URL = val
}
// SetCreated sets the value of Created.
func (s *Page) SetCreated(val time.Time) {
s.Created = val
}
// SetFormats sets the value of Formats.
func (s *Page) SetFormats(val []Format) {
s.Formats = val
}
// SetStatus sets the value of Status.
func (s *Page) SetStatus(val Status) {
s.Status = val
}
// Merged schema.
// Ref: #/components/schemas/pageWithResults
type PageWithResults struct {
ID uuid.UUID `json:"id"`
URL string `json:"url"`
Created time.Time `json:"created"`
Formats []Format `json:"formats"`
Status Status `json:"status"`
Results []Result `json:"results"`
}
// GetID returns the value of ID.
func (s *PageWithResults) GetID() uuid.UUID {
return s.ID
}
// GetURL returns the value of URL.
func (s *PageWithResults) GetURL() string {
return s.URL
}
// GetCreated returns the value of Created.
func (s *PageWithResults) GetCreated() time.Time {
return s.Created
}
// GetFormats returns the value of Formats.
func (s *PageWithResults) GetFormats() []Format {
return s.Formats
}
// GetStatus returns the value of Status.
func (s *PageWithResults) GetStatus() Status {
return s.Status
}
// GetResults returns the value of Results.
func (s *PageWithResults) GetResults() []Result {
return s.Results
}
// SetID sets the value of ID.
func (s *PageWithResults) SetID(val uuid.UUID) {
s.ID = val
}
// SetURL sets the value of URL.
func (s *PageWithResults) SetURL(val string) {
s.URL = val
}
// SetCreated sets the value of Created.
func (s *PageWithResults) SetCreated(val time.Time) {
s.Created = val
}
// SetFormats sets the value of Formats.
func (s *PageWithResults) SetFormats(val []Format) {
s.Formats = val
}
// SetStatus sets the value of Status.
func (s *PageWithResults) SetStatus(val Status) {
s.Status = val
}
// SetResults sets the value of Results.
func (s *PageWithResults) SetResults(val []Result) {
s.Results = val
}
func (*PageWithResults) getPageRes() {}
type Pages []Page
// Ref: #/components/schemas/result
type Result struct {
Format Format `json:"format"`
Error OptString `json:"error"`
Files []ResultFilesItem `json:"files"`
}
// GetFormat returns the value of Format.
func (s *Result) GetFormat() Format {
return s.Format
}
// GetError returns the value of Error.
func (s *Result) GetError() OptString {
return s.Error
}
// GetFiles returns the value of Files.
func (s *Result) GetFiles() []ResultFilesItem {
return s.Files
}
// SetFormat sets the value of Format.
func (s *Result) SetFormat(val Format) {
s.Format = val
}
// SetError sets the value of Error.
func (s *Result) SetError(val OptString) {
s.Error = val
}
// SetFiles sets the value of Files.
func (s *Result) SetFiles(val []ResultFilesItem) {
s.Files = val
}
type ResultFilesItem struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Mimetype string `json:"mimetype"`
Size int64 `json:"size"`
}
// GetID returns the value of ID.
func (s *ResultFilesItem) GetID() uuid.UUID {
return s.ID
}
// GetName returns the value of Name.
func (s *ResultFilesItem) GetName() string {
return s.Name
}
// GetMimetype returns the value of Mimetype.
func (s *ResultFilesItem) GetMimetype() string {
return s.Mimetype
}
// GetSize returns the value of Size.
func (s *ResultFilesItem) GetSize() int64 {
return s.Size
}
// SetID sets the value of ID.
func (s *ResultFilesItem) SetID(val uuid.UUID) {
s.ID = val
}
// SetName sets the value of Name.
func (s *ResultFilesItem) SetName(val string) {
s.Name = val
}
// SetMimetype sets the value of Mimetype.
func (s *ResultFilesItem) SetMimetype(val string) {
s.Mimetype = val
}
// SetSize sets the value of Size.
func (s *ResultFilesItem) SetSize(val int64) {
s.Size = val
}
// Ref: #/components/schemas/status
type Status string
const (
StatusNew Status = "new"
StatusProcessing Status = "processing"
StatusDone Status = "done"
StatusFailed Status = "failed"
StatusWithErrors Status = "with_errors"
)
// MarshalText implements encoding.TextMarshaler.
func (s Status) MarshalText() ([]byte, error) {
switch s {
case StatusNew:
return []byte(s), nil
case StatusProcessing:
return []byte(s), nil
case StatusDone:
return []byte(s), nil
case StatusFailed:
return []byte(s), nil
case StatusWithErrors:
return []byte(s), nil
default:
return nil, errors.Errorf("invalid value: %q", s)
}
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (s *Status) UnmarshalText(data []byte) error {
switch Status(data) {
case StatusNew:
*s = StatusNew
return nil
case StatusProcessing:
*s = StatusProcessing
return nil
case StatusDone:
*s = StatusDone
return nil
case StatusFailed:
*s = StatusFailed
return nil
case StatusWithErrors:
*s = StatusWithErrors
return nil
default:
return errors.Errorf("invalid value: %q", data)
}
}

View File

@@ -0,0 +1,52 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"context"
)
// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// AddPage implements addPage operation.
//
// Add new page.
//
// POST /pages
AddPage(ctx context.Context, req OptAddPageReq) (*Page, error)
// GetPage implements getPage operation.
//
// Get page details.
//
// GET /pages/{id}
GetPage(ctx context.Context, params GetPageParams) (GetPageRes, error)
// GetPages implements getPages operation.
//
// Get all pages.
//
// GET /pages
GetPages(ctx context.Context) (Pages, error)
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.
NewError(ctx context.Context, err error) *ErrorStatusCode
}
// Server implements http server based on OpenAPI v3 specification and
// calls Handler to handle requests.
type Server struct {
h Handler
baseServer
}
// NewServer creates new Server.
func NewServer(h Handler, opts ...ServerOption) (*Server, error) {
s, err := newServerConfig(opts...).baseServer()
if err != nil {
return nil, err
}
return &Server{
h: h,
baseServer: s,
}, nil
}

View File

@@ -0,0 +1,49 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"context"
ht "github.com/ogen-go/ogen/http"
)
// UnimplementedHandler is no-op Handler which returns http.ErrNotImplemented.
type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{}
// AddPage implements addPage operation.
//
// Add new page.
//
// POST /pages
func (UnimplementedHandler) AddPage(ctx context.Context, req OptAddPageReq) (r *Page, _ error) {
return r, ht.ErrNotImplemented
}
// GetPage implements getPage operation.
//
// Get page details.
//
// GET /pages/{id}
func (UnimplementedHandler) GetPage(ctx context.Context, params GetPageParams) (r GetPageRes, _ error) {
return r, ht.ErrNotImplemented
}
// GetPages implements getPages operation.
//
// Get all pages.
//
// GET /pages
func (UnimplementedHandler) GetPages(ctx context.Context) (r Pages, _ error) {
return r, ht.ErrNotImplemented
}
// NewError creates *ErrorStatusCode from error returned by handler.
//
// Used for common default response.
func (UnimplementedHandler) NewError(ctx context.Context, err error) (r *ErrorStatusCode) {
r = new(ErrorStatusCode)
return r
}

View File

@@ -0,0 +1,247 @@
// Code generated by ogen, DO NOT EDIT.
package openapi
import (
"fmt"
"github.com/go-faster/errors"
"github.com/ogen-go/ogen/validate"
)
func (s *AddPageReq) Validate() error {
var failures []validate.FieldError
if err := func() error {
var failures []validate.FieldError
for i, elem := range s.Formats {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "formats",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s Format) Validate() error {
switch s {
case "all":
return nil
case "pdf":
return nil
case "single_page":
return nil
case "headers":
return nil
default:
return errors.Errorf("invalid value: %v", s)
}
}
func (s *Page) Validate() error {
var failures []validate.FieldError
if err := func() error {
if s.Formats == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range s.Formats {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "formats",
Error: err,
})
}
if err := func() error {
if err := s.Status.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "status",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *PageWithResults) Validate() error {
var failures []validate.FieldError
if err := func() error {
if s.Formats == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range s.Formats {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "formats",
Error: err,
})
}
if err := func() error {
if err := s.Status.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "status",
Error: err,
})
}
if err := func() error {
if s.Results == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range s.Results {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "results",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s Pages) Validate() error {
if s == nil {
return errors.New("nil is invalid value")
}
var failures []validate.FieldError
for i, elem := range s {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *Result) Validate() error {
var failures []validate.FieldError
if err := func() error {
if err := s.Format.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "format",
Error: err,
})
}
if err := func() error {
if s.Files == nil {
return errors.New("nil is invalid value")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "files",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s Status) Validate() error {
switch s {
case "new":
return nil
case "processing":
return nil
case "done":
return nil
case "failed":
return nil
case "with_errors":
return nil
default:
return errors.Errorf("invalid value: %v", s)
}
}