feat: implement retrieve by hash function

This commit is contained in:
Bastien Riviere 2024-01-29 19:51:23 +01:00
parent e17aa7dc38
commit 8e03a1e087
Signed by: babariviere
GPG key ID: 4E5F0839249F162E
19 changed files with 275 additions and 155 deletions

38
internal/api/handler.go Normal file
View file

@ -0,0 +1,38 @@
package api
import (
"context"
"errors"
"fmt"
"github.com/babariviere/short/internal/db"
"github.com/babariviere/short/internal/oas"
"github.com/jackc/pgx/v5"
)
var _ oas.Handler = (*handler)(nil)
type handler struct {
oas.UnimplementedHandler
queries *db.Queries
}
func NewHandler(queries *db.Queries) *handler {
return &handler{
queries: queries,
}
}
func (h *handler) RedirectLongURL(ctx context.Context, params oas.RedirectLongURLParams) (oas.RedirectLongURLRes, error) {
res, err := h.queries.GetURLByHash(ctx, params.Hash)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return &oas.RedirectLongURLNotFound{}, nil
}
return nil, fmt.Errorf("unable to fetch URL by hash: %w", err)
}
return &oas.RedirectLongURLTemporaryRedirect{
Location: oas.NewOptString(res.LongUrl),
}, nil
}

View file

@ -17,21 +17,24 @@ import (
"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"
)
// Invoker invokes operations described by OpenAPI v3 specification.
type Invoker interface {
// CreatePost invokes POST /create operation.
// CreateShortURL invokes createShortURL operation.
//
// Create a shorten URL.
//
// POST /create
CreatePost(ctx context.Context, request *CreatePostReq) (CreatePostRes, error)
// HashGet invokes GET /{hash} operation.
CreateShortURL(ctx context.Context, request *CreateShortURLReq) (CreateShortURLRes, error)
// RedirectLongURL invokes redirectLongURL operation.
//
// Redirect client to long URL.
//
// GET /{hash}
HashGet(ctx context.Context, params HashGetParams) (HashGetRes, error)
RedirectLongURL(ctx context.Context, params RedirectLongURLParams) (RedirectLongURLRes, error)
}
// Client implements OAS client.
@ -82,16 +85,19 @@ func (c *Client) requestURL(ctx context.Context) *url.URL {
return u
}
// CreatePost invokes POST /create operation.
// CreateShortURL invokes createShortURL operation.
//
// Create a shorten URL.
//
// POST /create
func (c *Client) CreatePost(ctx context.Context, request *CreatePostReq) (CreatePostRes, error) {
res, err := c.sendCreatePost(ctx, request)
func (c *Client) CreateShortURL(ctx context.Context, request *CreateShortURLReq) (CreateShortURLRes, error) {
res, err := c.sendCreateShortURL(ctx, request)
return res, err
}
func (c *Client) sendCreatePost(ctx context.Context, request *CreatePostReq) (res CreatePostRes, err error) {
func (c *Client) sendCreateShortURL(ctx context.Context, request *CreateShortURLReq) (res CreateShortURLRes, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createShortURL"),
semconv.HTTPMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/create"),
}
@ -108,7 +114,7 @@ func (c *Client) sendCreatePost(ctx context.Context, request *CreatePostReq) (re
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, "CreatePost",
ctx, span := c.cfg.Tracer.Start(ctx, "CreateShortURL",
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
@ -134,7 +140,7 @@ func (c *Client) sendCreatePost(ctx context.Context, request *CreatePostReq) (re
if err != nil {
return res, errors.Wrap(err, "create request")
}
if err := encodeCreatePostRequest(request, r); err != nil {
if err := encodeCreateShortURLRequest(request, r); err != nil {
return res, errors.Wrap(err, "encode request")
}
@ -146,7 +152,7 @@ func (c *Client) sendCreatePost(ctx context.Context, request *CreatePostReq) (re
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeCreatePostResponse(resp)
result, err := decodeCreateShortURLResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}
@ -154,18 +160,19 @@ func (c *Client) sendCreatePost(ctx context.Context, request *CreatePostReq) (re
return result, nil
}
// HashGet invokes GET /{hash} operation.
// RedirectLongURL invokes redirectLongURL operation.
//
// Redirect client to long URL.
//
// GET /{hash}
func (c *Client) HashGet(ctx context.Context, params HashGetParams) (HashGetRes, error) {
res, err := c.sendHashGet(ctx, params)
func (c *Client) RedirectLongURL(ctx context.Context, params RedirectLongURLParams) (RedirectLongURLRes, error) {
res, err := c.sendRedirectLongURL(ctx, params)
return res, err
}
func (c *Client) sendHashGet(ctx context.Context, params HashGetParams) (res HashGetRes, err error) {
func (c *Client) sendRedirectLongURL(ctx context.Context, params RedirectLongURLParams) (res RedirectLongURLRes, err error) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("redirectLongURL"),
semconv.HTTPMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/{hash}"),
}
@ -182,7 +189,7 @@ func (c *Client) sendHashGet(ctx context.Context, params HashGetParams) (res Has
c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...))
// Start a span for this request.
ctx, span := c.cfg.Tracer.Start(ctx, "HashGet",
ctx, span := c.cfg.Tracer.Start(ctx, "RedirectLongURL",
trace.WithAttributes(otelAttrs...),
clientSpanKind,
)
@ -235,7 +242,7 @@ func (c *Client) sendHashGet(ctx context.Context, params HashGetParams) (res Has
defer resp.Body.Close()
stage = "DecodeResponse"
result, err := decodeHashGetResponse(resp)
result, err := decodeRedirectLongURLResponse(resp)
if err != nil {
return res, errors.Wrap(err, "decode response")
}

View file

@ -17,19 +17,23 @@ import (
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"
)
// handleCreatePostRequest handles POST /create operation.
// handleCreateShortURLRequest handles createShortURL operation.
//
// Create a shorten URL.
//
// POST /create
func (s *Server) handleCreatePostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
func (s *Server) handleCreateShortURLRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createShortURL"),
semconv.HTTPMethodKey.String("POST"),
semconv.HTTPRouteKey.String("/create"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), "CreatePost",
ctx, span := s.cfg.Tracer.Start(r.Context(), "CreateShortURL",
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
@ -54,11 +58,11 @@ func (s *Server) handleCreatePostRequest(args [0]string, argsEscaped bool, w htt
}
err error
opErrContext = ogenerrors.OperationContext{
Name: "CreatePost",
ID: "",
Name: "CreateShortURL",
ID: "createShortURL",
}
)
request, close, err := s.decodeCreatePostRequest(r)
request, close, err := s.decodeCreateShortURLRequest(r)
if err != nil {
err = &ogenerrors.DecodeRequestError{
OperationContext: opErrContext,
@ -74,22 +78,22 @@ func (s *Server) handleCreatePostRequest(args [0]string, argsEscaped bool, w htt
}
}()
var response CreatePostRes
var response CreateShortURLRes
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: "CreatePost",
OperationName: "CreateShortURL",
OperationSummary: "",
OperationID: "",
OperationID: "createShortURL",
Body: request,
Params: middleware.Parameters{},
Raw: r,
}
type (
Request = *CreatePostReq
Request = *CreateShortURLReq
Params = struct{}
Response = CreatePostRes
Response = CreateShortURLRes
)
response, err = middleware.HookMiddleware[
Request,
@ -100,12 +104,12 @@ func (s *Server) handleCreatePostRequest(args [0]string, argsEscaped bool, w htt
mreq,
nil,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
response, err = s.h.CreatePost(ctx, request)
response, err = s.h.CreateShortURL(ctx, request)
return response, err
},
)
} else {
response, err = s.h.CreatePost(ctx, request)
response, err = s.h.CreateShortURL(ctx, request)
}
if err != nil {
recordError("Internal", err)
@ -113,7 +117,7 @@ func (s *Server) handleCreatePostRequest(args [0]string, argsEscaped bool, w htt
return
}
if err := encodeCreatePostResponse(response, w, span); err != nil {
if err := encodeCreateShortURLResponse(response, w, span); err != nil {
recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)
@ -122,19 +126,20 @@ func (s *Server) handleCreatePostRequest(args [0]string, argsEscaped bool, w htt
}
}
// handleHashGetRequest handles GET /{hash} operation.
// handleRedirectLongURLRequest handles redirectLongURL operation.
//
// Redirect client to long URL.
//
// GET /{hash}
func (s *Server) handleHashGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
func (s *Server) handleRedirectLongURLRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) {
otelAttrs := []attribute.KeyValue{
otelogen.OperationID("redirectLongURL"),
semconv.HTTPMethodKey.String("GET"),
semconv.HTTPRouteKey.String("/{hash}"),
}
// Start a span for this request.
ctx, span := s.cfg.Tracer.Start(r.Context(), "HashGet",
ctx, span := s.cfg.Tracer.Start(r.Context(), "RedirectLongURL",
trace.WithAttributes(otelAttrs...),
serverSpanKind,
)
@ -159,11 +164,11 @@ func (s *Server) handleHashGetRequest(args [1]string, argsEscaped bool, w http.R
}
err error
opErrContext = ogenerrors.OperationContext{
Name: "HashGet",
ID: "",
Name: "RedirectLongURL",
ID: "redirectLongURL",
}
)
params, err := decodeHashGetParams(args, argsEscaped, r)
params, err := decodeRedirectLongURLParams(args, argsEscaped, r)
if err != nil {
err = &ogenerrors.DecodeParamsError{
OperationContext: opErrContext,
@ -174,13 +179,13 @@ func (s *Server) handleHashGetRequest(args [1]string, argsEscaped bool, w http.R
return
}
var response HashGetRes
var response RedirectLongURLRes
if m := s.cfg.Middleware; m != nil {
mreq := middleware.Request{
Context: ctx,
OperationName: "HashGet",
OperationName: "RedirectLongURL",
OperationSummary: "",
OperationID: "",
OperationID: "redirectLongURL",
Body: nil,
Params: middleware.Parameters{
{
@ -193,8 +198,8 @@ func (s *Server) handleHashGetRequest(args [1]string, argsEscaped bool, w http.R
type (
Request = struct{}
Params = HashGetParams
Response = HashGetRes
Params = RedirectLongURLParams
Response = RedirectLongURLRes
)
response, err = middleware.HookMiddleware[
Request,
@ -203,14 +208,14 @@ func (s *Server) handleHashGetRequest(args [1]string, argsEscaped bool, w http.R
](
m,
mreq,
unpackHashGetParams,
unpackRedirectLongURLParams,
func(ctx context.Context, request Request, params Params) (response Response, err error) {
response, err = s.h.HashGet(ctx, params)
response, err = s.h.RedirectLongURL(ctx, params)
return response, err
},
)
} else {
response, err = s.h.HashGet(ctx, params)
response, err = s.h.RedirectLongURL(ctx, params)
}
if err != nil {
recordError("Internal", err)
@ -218,7 +223,7 @@ func (s *Server) handleHashGetRequest(args [1]string, argsEscaped bool, w http.R
return
}
if err := encodeHashGetResponse(response, w, span); err != nil {
if err := encodeRedirectLongURLResponse(response, w, span); err != nil {
recordError("EncodeResponse", err)
if !errors.Is(err, ht.ErrInternalServerErrorResponse) {
s.cfg.ErrorHandler(ctx, w, r, err)

View file

@ -1,10 +1,10 @@
// Code generated by ogen, DO NOT EDIT.
package oas
type CreatePostRes interface {
createPostRes()
type CreateShortURLRes interface {
createShortURLRes()
}
type HashGetRes interface {
hashGetRes()
type RedirectLongURLRes interface {
redirectLongURLRes()
}

View file

@ -13,14 +13,14 @@ import (
)
// Encode implements json.Marshaler.
func (s *CreatePostBadRequest) Encode(e *jx.Encoder) {
func (s *CreateShortURLBadRequest) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *CreatePostBadRequest) encodeFields(e *jx.Encoder) {
func (s *CreateShortURLBadRequest) encodeFields(e *jx.Encoder) {
{
if s.Message.Set {
e.FieldStart("message")
@ -29,14 +29,14 @@ func (s *CreatePostBadRequest) encodeFields(e *jx.Encoder) {
}
}
var jsonFieldsNameOfCreatePostBadRequest = [1]string{
var jsonFieldsNameOfCreateShortURLBadRequest = [1]string{
0: "message",
}
// Decode decodes CreatePostBadRequest from json.
func (s *CreatePostBadRequest) Decode(d *jx.Decoder) error {
// Decode decodes CreateShortURLBadRequest from json.
func (s *CreateShortURLBadRequest) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode CreatePostBadRequest to nil")
return errors.New("invalid: unable to decode CreateShortURLBadRequest to nil")
}
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
@ -56,34 +56,34 @@ func (s *CreatePostBadRequest) Decode(d *jx.Decoder) error {
}
return nil
}); err != nil {
return errors.Wrap(err, "decode CreatePostBadRequest")
return errors.Wrap(err, "decode CreateShortURLBadRequest")
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *CreatePostBadRequest) MarshalJSON() ([]byte, error) {
func (s *CreateShortURLBadRequest) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *CreatePostBadRequest) UnmarshalJSON(data []byte) error {
func (s *CreateShortURLBadRequest) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *CreatePostCreated) Encode(e *jx.Encoder) {
func (s *CreateShortURLCreated) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *CreatePostCreated) encodeFields(e *jx.Encoder) {
func (s *CreateShortURLCreated) encodeFields(e *jx.Encoder) {
{
if s.Shorten.Set {
e.FieldStart("shorten")
@ -92,14 +92,14 @@ func (s *CreatePostCreated) encodeFields(e *jx.Encoder) {
}
}
var jsonFieldsNameOfCreatePostCreated = [1]string{
var jsonFieldsNameOfCreateShortURLCreated = [1]string{
0: "shorten",
}
// Decode decodes CreatePostCreated from json.
func (s *CreatePostCreated) Decode(d *jx.Decoder) error {
// Decode decodes CreateShortURLCreated from json.
func (s *CreateShortURLCreated) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode CreatePostCreated to nil")
return errors.New("invalid: unable to decode CreateShortURLCreated to nil")
}
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
@ -119,48 +119,48 @@ func (s *CreatePostCreated) Decode(d *jx.Decoder) error {
}
return nil
}); err != nil {
return errors.Wrap(err, "decode CreatePostCreated")
return errors.Wrap(err, "decode CreateShortURLCreated")
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *CreatePostCreated) MarshalJSON() ([]byte, error) {
func (s *CreateShortURLCreated) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *CreatePostCreated) UnmarshalJSON(data []byte) error {
func (s *CreateShortURLCreated) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *CreatePostReq) Encode(e *jx.Encoder) {
func (s *CreateShortURLReq) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *CreatePostReq) encodeFields(e *jx.Encoder) {
func (s *CreateShortURLReq) encodeFields(e *jx.Encoder) {
{
e.FieldStart("url")
e.Str(s.URL)
}
}
var jsonFieldsNameOfCreatePostReq = [1]string{
var jsonFieldsNameOfCreateShortURLReq = [1]string{
0: "url",
}
// Decode decodes CreatePostReq from json.
func (s *CreatePostReq) Decode(d *jx.Decoder) error {
// Decode decodes CreateShortURLReq from json.
func (s *CreateShortURLReq) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode CreatePostReq to nil")
return errors.New("invalid: unable to decode CreateShortURLReq to nil")
}
var requiredBitSet [1]uint8
@ -183,7 +183,7 @@ func (s *CreatePostReq) Decode(d *jx.Decoder) error {
}
return nil
}); err != nil {
return errors.Wrap(err, "decode CreatePostReq")
return errors.Wrap(err, "decode CreateShortURLReq")
}
// Validate required fields.
var failures []validate.FieldError
@ -200,8 +200,8 @@ func (s *CreatePostReq) Decode(d *jx.Decoder) error {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfCreatePostReq) {
name = jsonFieldsNameOfCreatePostReq[fieldIdx]
if fieldIdx < len(jsonFieldsNameOfCreateShortURLReq) {
name = jsonFieldsNameOfCreateShortURLReq[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
@ -222,14 +222,14 @@ func (s *CreatePostReq) Decode(d *jx.Decoder) error {
}
// MarshalJSON implements stdjson.Marshaler.
func (s *CreatePostReq) MarshalJSON() ([]byte, error) {
func (s *CreateShortURLReq) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *CreatePostReq) UnmarshalJSON(data []byte) error {
func (s *CreateShortURLReq) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}

View file

@ -15,13 +15,13 @@ import (
"github.com/ogen-go/ogen/validate"
)
// HashGetParams is parameters of GET /{hash} operation.
type HashGetParams struct {
// RedirectLongURLParams is parameters of redirectLongURL operation.
type RedirectLongURLParams struct {
// Hash of shorten URL.
Hash string
}
func unpackHashGetParams(packed middleware.Parameters) (params HashGetParams) {
func unpackRedirectLongURLParams(packed middleware.Parameters) (params RedirectLongURLParams) {
{
key := middleware.ParameterKey{
Name: "hash",
@ -32,7 +32,7 @@ func unpackHashGetParams(packed middleware.Parameters) (params HashGetParams) {
return params
}
func decodeHashGetParams(args [1]string, argsEscaped bool, r *http.Request) (params HashGetParams, _ error) {
func decodeRedirectLongURLParams(args [1]string, argsEscaped bool, r *http.Request) (params RedirectLongURLParams, _ error) {
// Decode path: hash.
if err := func() error {
param := args[0]

View file

@ -15,8 +15,8 @@ import (
"github.com/ogen-go/ogen/validate"
)
func (s *Server) decodeCreatePostRequest(r *http.Request) (
req *CreatePostReq,
func (s *Server) decodeCreateShortURLRequest(r *http.Request) (
req *CreateShortURLReq,
close func() error,
rerr error,
) {
@ -55,7 +55,7 @@ func (s *Server) decodeCreatePostRequest(r *http.Request) (
d := jx.DecodeBytes(buf)
var request CreatePostReq
var request CreateShortURLReq
if err := func() error {
if err := request.Decode(d); err != nil {
return err

View file

@ -11,8 +11,8 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeCreatePostRequest(
req *CreatePostReq,
func encodeCreateShortURLRequest(
req *CreateShortURLReq,
r *http.Request,
) error {
const contentType = "application/json"

View file

@ -16,7 +16,7 @@ import (
"github.com/ogen-go/ogen/validate"
)
func decodeCreatePostResponse(resp *http.Response) (res CreatePostRes, _ error) {
func decodeCreateShortURLResponse(resp *http.Response) (res CreateShortURLRes, _ error) {
switch resp.StatusCode {
case 201:
// Code 201.
@ -32,7 +32,7 @@ func decodeCreatePostResponse(resp *http.Response) (res CreatePostRes, _ error)
}
d := jx.DecodeBytes(buf)
var response CreatePostCreated
var response CreateShortURLCreated
if err := func() error {
if err := response.Decode(d); err != nil {
return err
@ -67,7 +67,7 @@ func decodeCreatePostResponse(resp *http.Response) (res CreatePostRes, _ error)
}
d := jx.DecodeBytes(buf)
var response CreatePostBadRequest
var response CreateShortURLBadRequest
if err := func() error {
if err := response.Decode(d); err != nil {
return err
@ -92,11 +92,11 @@ func decodeCreatePostResponse(resp *http.Response) (res CreatePostRes, _ error)
return res, validate.UnexpectedStatusCode(resp.StatusCode)
}
func decodeHashGetResponse(resp *http.Response) (res HashGetRes, _ error) {
func decodeRedirectLongURLResponse(resp *http.Response) (res RedirectLongURLRes, _ error) {
switch resp.StatusCode {
case 307:
// Code 307.
var wrapper HashGetTemporaryRedirect
var wrapper RedirectLongURLTemporaryRedirect
h := uri.NewHeaderDecoder(resp.Header)
// Parse "Location" header.
{
@ -138,7 +138,7 @@ func decodeHashGetResponse(resp *http.Response) (res HashGetRes, _ error) {
return &wrapper, nil
case 404:
// Code 404.
return &HashGetNotFound{}, nil
return &RedirectLongURLNotFound{}, nil
}
return res, validate.UnexpectedStatusCode(resp.StatusCode)
}

View file

@ -14,9 +14,9 @@ import (
"github.com/ogen-go/ogen/uri"
)
func encodeCreatePostResponse(response CreatePostRes, w http.ResponseWriter, span trace.Span) error {
func encodeCreateShortURLResponse(response CreateShortURLRes, w http.ResponseWriter, span trace.Span) error {
switch response := response.(type) {
case *CreatePostCreated:
case *CreateShortURLCreated:
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
@ -29,7 +29,7 @@ func encodeCreatePostResponse(response CreatePostRes, w http.ResponseWriter, spa
return nil
case *CreatePostBadRequest:
case *CreateShortURLBadRequest:
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(400)
span.SetStatus(codes.Error, http.StatusText(400))
@ -47,9 +47,9 @@ func encodeCreatePostResponse(response CreatePostRes, w http.ResponseWriter, spa
}
}
func encodeHashGetResponse(response HashGetRes, w http.ResponseWriter, span trace.Span) error {
func encodeRedirectLongURLResponse(response RedirectLongURLRes, w http.ResponseWriter, span trace.Span) error {
switch response := response.(type) {
case *HashGetTemporaryRedirect:
case *RedirectLongURLTemporaryRedirect:
// Encoding response headers.
{
h := uri.NewHeaderEncoder(w.Header())
@ -74,7 +74,7 @@ func encodeHashGetResponse(response HashGetRes, w http.ResponseWriter, span trac
return nil
case *HashGetNotFound:
case *RedirectLongURLNotFound:
w.WriteHeader(404)
span.SetStatus(codes.Error, http.StatusText(404))

View file

@ -73,7 +73,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Leaf node.
switch r.Method {
case "POST":
s.handleCreatePostRequest([0]string{}, elemIsEscaped, w, r)
s.handleCreateShortURLRequest([0]string{}, elemIsEscaped, w, r)
default:
s.notAllowed(w, r, "POST")
}
@ -92,7 +92,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Leaf node.
switch r.Method {
case "GET":
s.handleHashGetRequest([1]string{
s.handleRedirectLongURLRequest([1]string{
args[0],
}, elemIsEscaped, w, r)
default:
@ -206,10 +206,10 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 {
switch method {
case "POST":
// Leaf: CreatePost
r.name = "CreatePost"
// Leaf: CreateShortURL
r.name = "CreateShortURL"
r.summary = ""
r.operationID = ""
r.operationID = "createShortURL"
r.pathPattern = "/create"
r.args = args
r.count = 0
@ -229,10 +229,10 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) {
if len(elem) == 0 {
switch method {
case "GET":
// Leaf: HashGet
r.name = "HashGet"
// Leaf: RedirectLongURL
r.name = "RedirectLongURL"
r.summary = ""
r.operationID = ""
r.operationID = "redirectLongURL"
r.pathPattern = "/{hash}"
r.args = args
r.count = 1

View file

@ -2,76 +2,54 @@
package oas
type CreatePostBadRequest struct {
type CreateShortURLBadRequest struct {
Message OptString `json:"message"`
}
// GetMessage returns the value of Message.
func (s *CreatePostBadRequest) GetMessage() OptString {
func (s *CreateShortURLBadRequest) GetMessage() OptString {
return s.Message
}
// SetMessage sets the value of Message.
func (s *CreatePostBadRequest) SetMessage(val OptString) {
func (s *CreateShortURLBadRequest) SetMessage(val OptString) {
s.Message = val
}
func (*CreatePostBadRequest) createPostRes() {}
func (*CreateShortURLBadRequest) createShortURLRes() {}
type CreatePostCreated struct {
type CreateShortURLCreated struct {
// Created shorten URL. Going to this URL should redirect to URL from request body.
Shorten OptString `json:"shorten"`
}
// GetShorten returns the value of Shorten.
func (s *CreatePostCreated) GetShorten() OptString {
func (s *CreateShortURLCreated) GetShorten() OptString {
return s.Shorten
}
// SetShorten sets the value of Shorten.
func (s *CreatePostCreated) SetShorten(val OptString) {
func (s *CreateShortURLCreated) SetShorten(val OptString) {
s.Shorten = val
}
func (*CreatePostCreated) createPostRes() {}
func (*CreateShortURLCreated) createShortURLRes() {}
type CreatePostReq struct {
type CreateShortURLReq struct {
// URL to shorten.
URL string `json:"url"`
}
// GetURL returns the value of URL.
func (s *CreatePostReq) GetURL() string {
func (s *CreateShortURLReq) GetURL() string {
return s.URL
}
// SetURL sets the value of URL.
func (s *CreatePostReq) SetURL(val string) {
func (s *CreateShortURLReq) SetURL(val string) {
s.URL = val
}
// HashGetNotFound is response for HashGet operation.
type HashGetNotFound struct{}
func (*HashGetNotFound) hashGetRes() {}
// HashGetTemporaryRedirect is response for HashGet operation.
type HashGetTemporaryRedirect struct {
Location OptString
}
// GetLocation returns the value of Location.
func (s *HashGetTemporaryRedirect) GetLocation() OptString {
return s.Location
}
// SetLocation sets the value of Location.
func (s *HashGetTemporaryRedirect) SetLocation(val OptString) {
s.Location = val
}
func (*HashGetTemporaryRedirect) hashGetRes() {}
// NewOptString returns new OptString with value set to v.
func NewOptString(v string) OptString {
return OptString{
@ -117,3 +95,25 @@ func (o OptString) Or(d string) string {
}
return d
}
// RedirectLongURLNotFound is response for RedirectLongURL operation.
type RedirectLongURLNotFound struct{}
func (*RedirectLongURLNotFound) redirectLongURLRes() {}
// RedirectLongURLTemporaryRedirect is response for RedirectLongURL operation.
type RedirectLongURLTemporaryRedirect struct {
Location OptString
}
// GetLocation returns the value of Location.
func (s *RedirectLongURLTemporaryRedirect) GetLocation() OptString {
return s.Location
}
// SetLocation sets the value of Location.
func (s *RedirectLongURLTemporaryRedirect) SetLocation(val OptString) {
s.Location = val
}
func (*RedirectLongURLTemporaryRedirect) redirectLongURLRes() {}

View file

@ -8,16 +8,18 @@ import (
// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// CreatePost implements POST /create operation.
// CreateShortURL implements createShortURL operation.
//
// Create a shorten URL.
//
// POST /create
CreatePost(ctx context.Context, req *CreatePostReq) (CreatePostRes, error)
// HashGet implements GET /{hash} operation.
CreateShortURL(ctx context.Context, req *CreateShortURLReq) (CreateShortURLRes, error)
// RedirectLongURL implements redirectLongURL operation.
//
// Redirect client to long URL.
//
// GET /{hash}
HashGet(ctx context.Context, params HashGetParams) (HashGetRes, error)
RedirectLongURL(ctx context.Context, params RedirectLongURLParams) (RedirectLongURLRes, error)
}
// Server implements http server based on OpenAPI v3 specification and

View file

@ -13,18 +13,20 @@ type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{}
// CreatePost implements POST /create operation.
// CreateShortURL implements createShortURL operation.
//
// Create a shorten URL.
//
// POST /create
func (UnimplementedHandler) CreatePost(ctx context.Context, req *CreatePostReq) (r CreatePostRes, _ error) {
func (UnimplementedHandler) CreateShortURL(ctx context.Context, req *CreateShortURLReq) (r CreateShortURLRes, _ error) {
return r, ht.ErrNotImplemented
}
// HashGet implements GET /{hash} operation.
// RedirectLongURL implements redirectLongURL operation.
//
// Redirect client to long URL.
//
// GET /{hash}
func (UnimplementedHandler) HashGet(ctx context.Context, params HashGetParams) (r HashGetRes, _ error) {
func (UnimplementedHandler) RedirectLongURL(ctx context.Context, params RedirectLongURLParams) (r RedirectLongURLRes, _ error) {
return r, ht.ErrNotImplemented
}