diff --git a/README.md b/README.md index b0d36a5..a080c59 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,19 @@ curl -X POST --location "http://localhost:5001/pages" \ -d "{ \"url\": \"https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1937\", \"formats\": [ - \"all\" + \"pdf\", + \"headers\" ] }" | jq . ``` +or + +```shell +curl -X POST --location \ + "http://localhost:5001/pages?url=https%3A%2F%2Fgithub.com%2Fwkhtmltopdf%2Fwkhtmltopdf%2Fissues%2F1937&formats=pdf%2Cheaders&description=Foo+Bar" +``` + #### 3. Get the page's info ```shell diff --git a/api/openapi.yaml b/api/openapi.yaml index 33efb7c..00b7293 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -22,6 +22,23 @@ paths: post: operationId: addPage summary: Add new page + parameters: + - in: query + name: url + schema: + type: string + - in: query + name: description + schema: + type: string + - in: query + name: formats + style: form + explode: false + schema: + type: array + items: + $ref: '#/components/schemas/format' requestBody: content: application/json: diff --git a/api/openapi/oas_client_gen.go b/api/openapi/oas_client_gen.go index 1a1763d..36eed1a 100644 --- a/api/openapi/oas_client_gen.go +++ b/api/openapi/oas_client_gen.go @@ -76,13 +76,13 @@ func (c *Client) requestURL(ctx context.Context) *url.URL { // Add new page. // // POST /pages -func (c *Client) AddPage(ctx context.Context, request OptAddPageReq) (*Page, error) { - res, err := c.sendAddPage(ctx, request) +func (c *Client) AddPage(ctx context.Context, request OptAddPageReq, params AddPageParams) (*Page, error) { + res, err := c.sendAddPage(ctx, request, params) _ = res return res, err } -func (c *Client) sendAddPage(ctx context.Context, request OptAddPageReq) (res *Page, err error) { +func (c *Client) sendAddPage(ctx context.Context, request OptAddPageReq, params AddPageParams) (res *Page, err error) { otelAttrs := []attribute.KeyValue{ otelogen.OperationID("addPage"), } @@ -135,6 +135,67 @@ func (c *Client) sendAddPage(ctx context.Context, request OptAddPageReq) (res *P pathParts[0] = "/pages" uri.AddPathParts(u, pathParts[:]...) + stage = "EncodeQueryParams" + q := uri.NewQueryEncoder() + { + // Encode "url" parameter. + cfg := uri.QueryParameterEncodingConfig{ + Name: "url", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.EncodeParam(cfg, func(e uri.Encoder) error { + if val, ok := params.URL.Get(); ok { + return e.EncodeValue(conv.StringToString(val)) + } + return nil + }); err != nil { + return res, errors.Wrap(err, "encode query") + } + } + { + // Encode "description" parameter. + cfg := uri.QueryParameterEncodingConfig{ + Name: "description", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.EncodeParam(cfg, func(e uri.Encoder) error { + if val, ok := params.Description.Get(); ok { + return e.EncodeValue(conv.StringToString(val)) + } + return nil + }); err != nil { + return res, errors.Wrap(err, "encode query") + } + } + { + // Encode "formats" parameter. + cfg := uri.QueryParameterEncodingConfig{ + Name: "formats", + Style: uri.QueryStyleForm, + Explode: false, + } + + if err := q.EncodeParam(cfg, func(e uri.Encoder) error { + return e.EncodeArray(func(e uri.Encoder) error { + for i, item := range params.Formats { + if err := func() error { + return e.EncodeValue(conv.StringToString(string(item))) + }(); err != nil { + return errors.Wrapf(err, "[%d]", i) + } + } + return nil + }) + }); err != nil { + return res, errors.Wrap(err, "encode query") + } + } + u.RawQuery = q.Values().Encode() + stage = "EncodeRequest" r, err := ht.NewRequest(ctx, "POST", u, nil) if err != nil { diff --git a/api/openapi/oas_handlers_gen.go b/api/openapi/oas_handlers_gen.go index eec8fad..6fe2fd0 100644 --- a/api/openapi/oas_handlers_gen.go +++ b/api/openapi/oas_handlers_gen.go @@ -60,6 +60,16 @@ func (s *Server) handleAddPageRequest(args [0]string, argsEscaped bool, w http.R ID: "addPage", } ) + params, err := decodeAddPageParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } request, close, err := s.decodeAddPageRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ @@ -83,13 +93,26 @@ func (s *Server) handleAddPageRequest(args [0]string, argsEscaped bool, w http.R OperationName: "AddPage", OperationID: "addPage", Body: request, - Params: middleware.Parameters{}, - Raw: r, + Params: middleware.Parameters{ + { + Name: "url", + In: "query", + }: params.URL, + { + Name: "description", + In: "query", + }: params.Description, + { + Name: "formats", + In: "query", + }: params.Formats, + }, + Raw: r, } type ( Request = OptAddPageReq - Params = struct{} + Params = AddPageParams Response = *Page ) response, err = middleware.HookMiddleware[ @@ -99,14 +122,14 @@ func (s *Server) handleAddPageRequest(args [0]string, argsEscaped bool, w http.R ]( m, mreq, - nil, + unpackAddPageParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { - response, err = s.h.AddPage(ctx, request) + response, err = s.h.AddPage(ctx, request, params) return response, err }, ) } else { - response, err = s.h.AddPage(ctx, request) + response, err = s.h.AddPage(ctx, request, params) } if err != nil { recordError("Internal", err) diff --git a/api/openapi/oas_parameters_gen.go b/api/openapi/oas_parameters_gen.go index f76812a..48c1c5c 100644 --- a/api/openapi/oas_parameters_gen.go +++ b/api/openapi/oas_parameters_gen.go @@ -3,6 +3,7 @@ package openapi import ( + "fmt" "net/http" "net/url" @@ -16,6 +17,196 @@ import ( "github.com/ogen-go/ogen/validate" ) +// AddPageParams is parameters of addPage operation. +type AddPageParams struct { + URL OptString + Description OptString + Formats []Format +} + +func unpackAddPageParams(packed middleware.Parameters) (params AddPageParams) { + { + key := middleware.ParameterKey{ + Name: "url", + In: "query", + } + if v, ok := packed[key]; ok { + params.URL = v.(OptString) + } + } + { + key := middleware.ParameterKey{ + Name: "description", + In: "query", + } + if v, ok := packed[key]; ok { + params.Description = v.(OptString) + } + } + { + key := middleware.ParameterKey{ + Name: "formats", + In: "query", + } + if v, ok := packed[key]; ok { + params.Formats = v.([]Format) + } + } + return params +} + +func decodeAddPageParams(args [0]string, argsEscaped bool, r *http.Request) (params AddPageParams, _ error) { + q := uri.NewQueryDecoder(r.URL.Query()) + // Decode query: url. + if err := func() error { + cfg := uri.QueryParameterDecodingConfig{ + Name: "url", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.HasParam(cfg); err == nil { + if err := q.DecodeParam(cfg, func(d uri.Decoder) error { + var paramsDotURLVal string + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToString(val) + if err != nil { + return err + } + + paramsDotURLVal = c + return nil + }(); err != nil { + return err + } + params.URL.SetTo(paramsDotURLVal) + return nil + }); err != nil { + return err + } + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "url", + In: "query", + Err: err, + } + } + // Decode query: description. + if err := func() error { + cfg := uri.QueryParameterDecodingConfig{ + Name: "description", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.HasParam(cfg); err == nil { + if err := q.DecodeParam(cfg, func(d uri.Decoder) error { + var paramsDotDescriptionVal string + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToString(val) + if err != nil { + return err + } + + paramsDotDescriptionVal = c + return nil + }(); err != nil { + return err + } + params.Description.SetTo(paramsDotDescriptionVal) + return nil + }); err != nil { + return err + } + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "description", + In: "query", + Err: err, + } + } + // Decode query: formats. + if err := func() error { + cfg := uri.QueryParameterDecodingConfig{ + Name: "formats", + Style: uri.QueryStyleForm, + Explode: false, + } + + if err := q.HasParam(cfg); err == nil { + if err := q.DecodeParam(cfg, func(d uri.Decoder) error { + return d.DecodeArray(func(d uri.Decoder) error { + var paramsDotFormatsVal Format + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToString(val) + if err != nil { + return err + } + + paramsDotFormatsVal = Format(c) + return nil + }(); err != nil { + return err + } + params.Formats = append(params.Formats, paramsDotFormatsVal) + return nil + }) + }); err != nil { + return err + } + if err := func() error { + var failures []validate.FieldError + for i, elem := range params.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 { + return err + } + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "formats", + In: "query", + Err: err, + } + } + return params, nil +} + // GetFileParams is parameters of getFile operation. type GetFileParams struct { ID uuid.UUID diff --git a/api/openapi/oas_server_gen.go b/api/openapi/oas_server_gen.go index add7ea3..05a094a 100644 --- a/api/openapi/oas_server_gen.go +++ b/api/openapi/oas_server_gen.go @@ -13,7 +13,7 @@ type Handler interface { // Add new page. // // POST /pages - AddPage(ctx context.Context, req OptAddPageReq) (*Page, error) + AddPage(ctx context.Context, req OptAddPageReq, params AddPageParams) (*Page, error) // GetFile implements getFile operation. // // Get file content. diff --git a/api/openapi/oas_unimplemented_gen.go b/api/openapi/oas_unimplemented_gen.go index 934ecd8..d2c2849 100644 --- a/api/openapi/oas_unimplemented_gen.go +++ b/api/openapi/oas_unimplemented_gen.go @@ -18,7 +18,7 @@ var _ Handler = UnimplementedHandler{} // Add new page. // // POST /pages -func (UnimplementedHandler) AddPage(ctx context.Context, req OptAddPageReq) (r *Page, _ error) { +func (UnimplementedHandler) AddPage(ctx context.Context, req OptAddPageReq, params AddPageParams) (r *Page, _ error) { return r, ht.ErrNotImplemented } diff --git a/entity/page.go b/entity/page.go index 508d143..53edd20 100644 --- a/entity/page.go +++ b/entity/page.go @@ -21,6 +21,12 @@ const ( FormatPDF ) +var AllFormats = []Format{ + FormatHeaders, + FormatPDF, + FormatSingleFile, +} + type Status uint8 const ( diff --git a/ports/rest/converter.go b/ports/rest/converter.go index edfd47f..2d9d367 100644 --- a/ports/rest/converter.go +++ b/ports/rest/converter.go @@ -98,11 +98,7 @@ func FormatFromRest(format []openapi.Format) []entity.Format { switch { case len(format) == 0 || (len(format) == 1 && format[0] == openapi.FormatAll): - formats = []entity.Format{ - entity.FormatHeaders, - entity.FormatPDF, - entity.FormatSingleFile, - } + formats = entity.AllFormats default: formats = make([]entity.Format, len(format)) diff --git a/ports/rest/service.go b/ports/rest/service.go index fe0a595..a70654e 100644 --- a/ports/rest/service.go +++ b/ports/rest/service.go @@ -41,8 +41,26 @@ func (s *Service) GetPage(ctx context.Context, params openapi.GetPageParams) (op return &restPage, nil } -func (s *Service) AddPage(ctx context.Context, req openapi.OptAddPageReq) (*openapi.Page, error) { - page := entity.NewPage(req.Value.URL, req.Value.Description.Value, FormatFromRest(req.Value.Formats)...) +func (s *Service) AddPage(ctx context.Context, req openapi.OptAddPageReq, params openapi.AddPageParams) (*openapi.Page, error) { + url := params.URL.Or(req.Value.URL) + description := params.Description.Or(req.Value.Description.Value) + + formats := req.Value.Formats + if len(formats) == 0 { + formats = params.Formats + } + if len(formats) == 0 { + formats = []openapi.Format{"all"} + } + + switch { + case req.Value.URL != "": + url = req.Value.URL + case params.URL.IsSet(): + url = params.URL.Value + } + + page := entity.NewPage(url, description, FormatFromRest(formats)...) page.Status = entity.StatusProcessing