api: SubmissionCreate in request body

This commit is contained in:
Quaternions 2024-11-27 15:38:17 -08:00
parent f7aff4bbaa
commit acece12737
11 changed files with 549 additions and 19 deletions

2
go.mod
View File

@ -43,7 +43,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/asm v1.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect

View File

@ -51,11 +51,11 @@ paths:
operationId: createSubmission operationId: createSubmission
tags: tags:
- Submissions - Submissions
# how to do this? body should have the submission in json or something requestBody:
# content: content:
# application/json: application/json:
# schema: schema:
# $ref: "#/components/schemas/SubmissionCreate" $ref: '#/components/schemas/SubmissionCreate'
responses: responses:
"200": "200":
description: Successful response description: Successful response
@ -255,6 +255,31 @@ components:
Date: Date:
type: integer type: integer
format: int64 format: int64
SubmissionCreate:
type: object
properties:
DisplayName:
type: string
Creator:
type: string
GameID:
type: integer
format: int32
Submitter:
type: integer
format: int64
AssetID:
type: integer
format: int64
AssetVersion:
type: integer
format: int64
SubmissionType:
type: integer
format: int32
TargetAssetID:
type: integer
format: int64
Pagination: Pagination:
type: object type: object
required: required:

View File

@ -28,7 +28,7 @@ type Invoker interface {
// Create new submission. // Create new submission.
// //
// POST /submissions // POST /submissions
CreateSubmission(ctx context.Context) (*ID, error) CreateSubmission(ctx context.Context, request OptSubmissionCreate) (*ID, error)
// GetSubmission invokes getSubmission operation. // GetSubmission invokes getSubmission operation.
// //
// Retrieve map with ID. // Retrieve map with ID.
@ -118,12 +118,12 @@ func (c *Client) requestURL(ctx context.Context) *url.URL {
// Create new submission. // Create new submission.
// //
// POST /submissions // POST /submissions
func (c *Client) CreateSubmission(ctx context.Context) (*ID, error) { func (c *Client) CreateSubmission(ctx context.Context, request OptSubmissionCreate) (*ID, error) {
res, err := c.sendCreateSubmission(ctx) res, err := c.sendCreateSubmission(ctx, request)
return res, err return res, err
} }
func (c *Client) sendCreateSubmission(ctx context.Context) (res *ID, err error) { func (c *Client) sendCreateSubmission(ctx context.Context, request OptSubmissionCreate) (res *ID, err error) {
otelAttrs := []attribute.KeyValue{ otelAttrs := []attribute.KeyValue{
otelogen.OperationID("createSubmission"), otelogen.OperationID("createSubmission"),
semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRequestMethodKey.String("POST"),
@ -168,6 +168,9 @@ func (c *Client) sendCreateSubmission(ctx context.Context) (res *ID, err error)
if err != nil { if err != nil {
return res, errors.Wrap(err, "create request") return res, errors.Wrap(err, "create request")
} }
if err := encodeCreateSubmissionRequest(request, r); err != nil {
return res, errors.Wrap(err, "encode request")
}
stage = "SendRequest" stage = "SendRequest"
resp, err := c.cfg.Client.Do(r) resp, err := c.cfg.Client.Do(r)

View File

@ -63,7 +63,26 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool,
s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet())) s.errors.Add(ctx, 1, metric.WithAttributeSet(labeler.AttributeSet()))
} }
err error err error
opErrContext = ogenerrors.OperationContext{
Name: "CreateSubmission",
ID: "createSubmission",
}
) )
request, close, err := s.decodeCreateSubmissionRequest(r)
if err != nil {
err = &ogenerrors.DecodeRequestError{
OperationContext: opErrContext,
Err: err,
}
defer recordError("DecodeRequest", err)
s.cfg.ErrorHandler(ctx, w, r, err)
return
}
defer func() {
if err := close(); err != nil {
recordError("CloseRequest", err)
}
}()
var response *ID var response *ID
if m := s.cfg.Middleware; m != nil { if m := s.cfg.Middleware; m != nil {
@ -72,13 +91,13 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool,
OperationName: "CreateSubmission", OperationName: "CreateSubmission",
OperationSummary: "Create new submission", OperationSummary: "Create new submission",
OperationID: "createSubmission", OperationID: "createSubmission",
Body: nil, Body: request,
Params: middleware.Parameters{}, Params: middleware.Parameters{},
Raw: r, Raw: r,
} }
type ( type (
Request = struct{} Request = OptSubmissionCreate
Params = struct{} Params = struct{}
Response = *ID Response = *ID
) )
@ -91,12 +110,12 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool,
mreq, mreq,
nil, nil,
func(ctx context.Context, request Request, params Params) (response Response, err error) { func(ctx context.Context, request Request, params Params) (response Response, err error) {
response, err = s.h.CreateSubmission(ctx) response, err = s.h.CreateSubmission(ctx, request)
return response, err return response, err
}, },
) )
} else { } else {
response, err = s.h.CreateSubmission(ctx) response, err = s.h.CreateSubmission(ctx, request)
} }
if err != nil { if err != nil {
if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { if errRes, ok := errors.Into[*ErrorStatusCode](err); ok {

View File

@ -328,6 +328,39 @@ func (s *OptString) UnmarshalJSON(data []byte) error {
return s.Decode(d) return s.Decode(d)
} }
// Encode encodes SubmissionCreate as json.
func (o OptSubmissionCreate) Encode(e *jx.Encoder) {
if !o.Set {
return
}
o.Value.Encode(e)
}
// Decode decodes SubmissionCreate from json.
func (o *OptSubmissionCreate) Decode(d *jx.Decoder) error {
if o == nil {
return errors.New("invalid: unable to decode OptSubmissionCreate to nil")
}
o.Set = true
if err := o.Value.Decode(d); err != nil {
return err
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s OptSubmissionCreate) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *OptSubmissionCreate) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler. // Encode implements json.Marshaler.
func (s *Submission) Encode(e *jx.Encoder) { func (s *Submission) Encode(e *jx.Encoder) {
e.ObjStart() e.ObjStart()
@ -577,3 +610,185 @@ func (s *Submission) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data) d := jx.DecodeBytes(data)
return s.Decode(d) return s.Decode(d)
} }
// Encode implements json.Marshaler.
func (s *SubmissionCreate) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
{
if s.DisplayName.Set {
e.FieldStart("DisplayName")
s.DisplayName.Encode(e)
}
}
{
if s.Creator.Set {
e.FieldStart("Creator")
s.Creator.Encode(e)
}
}
{
if s.GameID.Set {
e.FieldStart("GameID")
s.GameID.Encode(e)
}
}
{
if s.Submitter.Set {
e.FieldStart("Submitter")
s.Submitter.Encode(e)
}
}
{
if s.AssetID.Set {
e.FieldStart("AssetID")
s.AssetID.Encode(e)
}
}
{
if s.AssetVersion.Set {
e.FieldStart("AssetVersion")
s.AssetVersion.Encode(e)
}
}
{
if s.SubmissionType.Set {
e.FieldStart("SubmissionType")
s.SubmissionType.Encode(e)
}
}
{
if s.TargetAssetID.Set {
e.FieldStart("TargetAssetID")
s.TargetAssetID.Encode(e)
}
}
}
var jsonFieldsNameOfSubmissionCreate = [8]string{
0: "DisplayName",
1: "Creator",
2: "GameID",
3: "Submitter",
4: "AssetID",
5: "AssetVersion",
6: "SubmissionType",
7: "TargetAssetID",
}
// Decode decodes SubmissionCreate from json.
func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode SubmissionCreate to nil")
}
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "DisplayName":
if err := func() error {
s.DisplayName.Reset()
if err := s.DisplayName.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"DisplayName\"")
}
case "Creator":
if err := func() error {
s.Creator.Reset()
if err := s.Creator.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Creator\"")
}
case "GameID":
if err := func() error {
s.GameID.Reset()
if err := s.GameID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"GameID\"")
}
case "Submitter":
if err := func() error {
s.Submitter.Reset()
if err := s.Submitter.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Submitter\"")
}
case "AssetID":
if err := func() error {
s.AssetID.Reset()
if err := s.AssetID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetID\"")
}
case "AssetVersion":
if err := func() error {
s.AssetVersion.Reset()
if err := s.AssetVersion.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetVersion\"")
}
case "SubmissionType":
if err := func() error {
s.SubmissionType.Reset()
if err := s.SubmissionType.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionType\"")
}
case "TargetAssetID":
if err := func() error {
s.TargetAssetID.Reset()
if err := s.TargetAssetID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"TargetAssetID\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode SubmissionCreate")
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *SubmissionCreate) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *SubmissionCreate) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}

View File

@ -1,3 +1,83 @@
// Code generated by ogen, DO NOT EDIT. // Code generated by ogen, DO NOT EDIT.
package api package api
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) decodeCreateSubmissionRequest(r *http.Request) (
req OptSubmissionCreate,
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 OptSubmissionCreate
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
}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}

View File

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

View File

@ -262,6 +262,52 @@ func (o OptString) Or(d string) string {
return d return d
} }
// NewOptSubmissionCreate returns new OptSubmissionCreate with value set to v.
func NewOptSubmissionCreate(v SubmissionCreate) OptSubmissionCreate {
return OptSubmissionCreate{
Value: v,
Set: true,
}
}
// OptSubmissionCreate is optional SubmissionCreate.
type OptSubmissionCreate struct {
Value SubmissionCreate
Set bool
}
// IsSet returns true if OptSubmissionCreate was set.
func (o OptSubmissionCreate) IsSet() bool { return o.Set }
// Reset unsets value.
func (o *OptSubmissionCreate) Reset() {
var v SubmissionCreate
o.Value = v
o.Set = false
}
// SetTo sets value to v.
func (o *OptSubmissionCreate) SetTo(v SubmissionCreate) {
o.Set = true
o.Value = v
}
// Get returns value and boolean that denotes whether value was set.
func (o OptSubmissionCreate) Get() (v SubmissionCreate, 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 OptSubmissionCreate) Or(d SubmissionCreate) SubmissionCreate {
if v, ok := o.Get(); ok {
return v
}
return d
}
// NewOptSubmissionFilter returns new OptSubmissionFilter with value set to v. // NewOptSubmissionFilter returns new OptSubmissionFilter with value set to v.
func NewOptSubmissionFilter(v SubmissionFilter) OptSubmissionFilter { func NewOptSubmissionFilter(v SubmissionFilter) OptSubmissionFilter {
return OptSubmissionFilter{ return OptSubmissionFilter{
@ -479,6 +525,98 @@ func (s *Submission) SetStatusID(val OptInt32) {
s.StatusID = val s.StatusID = val
} }
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
DisplayName OptString `json:"DisplayName"`
Creator OptString `json:"Creator"`
GameID OptInt32 `json:"GameID"`
Submitter OptInt64 `json:"Submitter"`
AssetID OptInt64 `json:"AssetID"`
AssetVersion OptInt64 `json:"AssetVersion"`
SubmissionType OptInt32 `json:"SubmissionType"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
}
// GetDisplayName returns the value of DisplayName.
func (s *SubmissionCreate) GetDisplayName() OptString {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *SubmissionCreate) GetCreator() OptString {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *SubmissionCreate) GetGameID() OptInt32 {
return s.GameID
}
// GetSubmitter returns the value of Submitter.
func (s *SubmissionCreate) GetSubmitter() OptInt64 {
return s.Submitter
}
// GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() OptInt64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() OptInt64 {
return s.AssetVersion
}
// GetSubmissionType returns the value of SubmissionType.
func (s *SubmissionCreate) GetSubmissionType() OptInt32 {
return s.SubmissionType
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *SubmissionCreate) GetTargetAssetID() OptInt64 {
return s.TargetAssetID
}
// SetDisplayName sets the value of DisplayName.
func (s *SubmissionCreate) SetDisplayName(val OptString) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *SubmissionCreate) SetCreator(val OptString) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *SubmissionCreate) SetGameID(val OptInt32) {
s.GameID = val
}
// SetSubmitter sets the value of Submitter.
func (s *SubmissionCreate) SetSubmitter(val OptInt64) {
s.Submitter = val
}
// SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val OptInt64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val OptInt64) {
s.AssetVersion = val
}
// SetSubmissionType sets the value of SubmissionType.
func (s *SubmissionCreate) SetSubmissionType(val OptInt32) {
s.SubmissionType = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
}
// Ref: #/components/schemas/SubmissionFilter // Ref: #/components/schemas/SubmissionFilter
type SubmissionFilter struct { type SubmissionFilter struct {
ID OptInt64 `json:"ID"` ID OptInt64 `json:"ID"`

View File

@ -13,7 +13,7 @@ type Handler interface {
// Create new submission. // Create new submission.
// //
// POST /submissions // POST /submissions
CreateSubmission(ctx context.Context) (*ID, error) CreateSubmission(ctx context.Context, req OptSubmissionCreate) (*ID, error)
// GetSubmission implements getSubmission operation. // GetSubmission implements getSubmission operation.
// //
// Retrieve map with ID. // Retrieve map with ID.

View File

@ -18,7 +18,7 @@ var _ Handler = UnimplementedHandler{}
// Create new submission. // Create new submission.
// //
// POST /submissions // POST /submissions
func (UnimplementedHandler) CreateSubmission(ctx context.Context) (r *ID, _ error) { func (UnimplementedHandler) CreateSubmission(ctx context.Context, req OptSubmissionCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented return r, ht.ErrNotImplemented
} }

View File

@ -1,13 +1,34 @@
package service package service
import ( import (
"time"
"context" "context"
"git.itzana.me/strafesnet/maps-service/pkg/api" "git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/model"
) )
// POST /submissions // POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context) (*api.ID, error) { func (svc *Service) CreateSubmission(ctx context.Context, request api.OptSubmissionCreate) (*api.ID, error) {
return nil, nil //I don't know how to read the http body
submission, err := svc.DB.Submissions().Create(ctx, model.Submission{
ID: 0,
DisplayName: request.Value.DisplayName.Value,
Creator: request.Value.Creator.Value,
GameID: request.Value.GameID.Value,
Date: time.Now(),
Submitter: request.Value.Submitter.Value,
AssetID: request.Value.AssetID.Value,
AssetVersion: request.Value.AssetVersion.Value,
Completed: false,
TargetAssetID: request.Value.TargetAssetID.Value,
StatusID: 0,
})
if err != nil{
return nil, err
}
return &api.ID{
ID:api.NewOptInt64(submission.ID),
}, nil
} }
// GetSubmission implements getSubmission operation. // GetSubmission implements getSubmission operation.