From acece12737487dd261cf6eaed0ee08511c16829f Mon Sep 17 00:00:00 2001 From: Quaternions Date: Wed, 27 Nov 2024 15:38:17 -0800 Subject: [PATCH] api: SubmissionCreate in request body --- go.mod | 2 +- openapi.yaml | 35 ++++- pkg/api/oas_client_gen.go | 11 +- pkg/api/oas_handlers_gen.go | 29 +++- pkg/api/oas_json_gen.go | 215 ++++++++++++++++++++++++++++ pkg/api/oas_request_decoders_gen.go | 80 +++++++++++ pkg/api/oas_request_encoders_gen.go | 29 ++++ pkg/api/oas_schemas_gen.go | 138 ++++++++++++++++++ pkg/api/oas_server_gen.go | 2 +- pkg/api/oas_unimplemented_gen.go | 2 +- pkg/service/submissions.go | 25 +++- 11 files changed, 549 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 3538101..5e1cec6 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // 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 golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect golang.org/x/net v0.25.0 // indirect diff --git a/openapi.yaml b/openapi.yaml index 70d2b51..158a847 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -51,11 +51,11 @@ paths: operationId: createSubmission tags: - Submissions -# how to do this? body should have the submission in json or something -# content: -# application/json: -# schema: -# $ref: "#/components/schemas/SubmissionCreate" + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubmissionCreate' responses: "200": description: Successful response @@ -255,6 +255,31 @@ components: Date: type: integer 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: type: object required: diff --git a/pkg/api/oas_client_gen.go b/pkg/api/oas_client_gen.go index 38e1cd5..4faf1cf 100644 --- a/pkg/api/oas_client_gen.go +++ b/pkg/api/oas_client_gen.go @@ -28,7 +28,7 @@ type Invoker interface { // Create new submission. // // POST /submissions - CreateSubmission(ctx context.Context) (*ID, error) + CreateSubmission(ctx context.Context, request OptSubmissionCreate) (*ID, error) // GetSubmission invokes getSubmission operation. // // Retrieve map with ID. @@ -118,12 +118,12 @@ func (c *Client) requestURL(ctx context.Context) *url.URL { // Create new submission. // // POST /submissions -func (c *Client) CreateSubmission(ctx context.Context) (*ID, error) { - res, err := c.sendCreateSubmission(ctx) +func (c *Client) CreateSubmission(ctx context.Context, request OptSubmissionCreate) (*ID, error) { + res, err := c.sendCreateSubmission(ctx, request) 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{ otelogen.OperationID("createSubmission"), semconv.HTTPRequestMethodKey.String("POST"), @@ -168,6 +168,9 @@ func (c *Client) sendCreateSubmission(ctx context.Context) (res *ID, err error) if err != nil { return res, errors.Wrap(err, "create request") } + if err := encodeCreateSubmissionRequest(request, r); err != nil { + return res, errors.Wrap(err, "encode request") + } stage = "SendRequest" resp, err := c.cfg.Client.Do(r) diff --git a/pkg/api/oas_handlers_gen.go b/pkg/api/oas_handlers_gen.go index c8b22fe..06c1faf 100644 --- a/pkg/api/oas_handlers_gen.go +++ b/pkg/api/oas_handlers_gen.go @@ -62,8 +62,27 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool, span.SetStatus(codes.Error, stage) 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 if m := s.cfg.Middleware; m != nil { @@ -72,13 +91,13 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool, OperationName: "CreateSubmission", OperationSummary: "Create new submission", OperationID: "createSubmission", - Body: nil, + Body: request, Params: middleware.Parameters{}, Raw: r, } type ( - Request = struct{} + Request = OptSubmissionCreate Params = struct{} Response = *ID ) @@ -91,12 +110,12 @@ func (s *Server) handleCreateSubmissionRequest(args [0]string, argsEscaped bool, mreq, nil, 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 }, ) } else { - response, err = s.h.CreateSubmission(ctx) + response, err = s.h.CreateSubmission(ctx, request) } if err != nil { if errRes, ok := errors.Into[*ErrorStatusCode](err); ok { diff --git a/pkg/api/oas_json_gen.go b/pkg/api/oas_json_gen.go index efde54a..67e5033 100644 --- a/pkg/api/oas_json_gen.go +++ b/pkg/api/oas_json_gen.go @@ -328,6 +328,39 @@ func (s *OptString) UnmarshalJSON(data []byte) error { 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. func (s *Submission) Encode(e *jx.Encoder) { e.ObjStart() @@ -577,3 +610,185 @@ func (s *Submission) UnmarshalJSON(data []byte) error { d := jx.DecodeBytes(data) 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) +} diff --git a/pkg/api/oas_request_decoders_gen.go b/pkg/api/oas_request_decoders_gen.go index ae379a2..5a3d275 100644 --- a/pkg/api/oas_request_decoders_gen.go +++ b/pkg/api/oas_request_decoders_gen.go @@ -1,3 +1,83 @@ // Code generated by ogen, DO NOT EDIT. 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) + } +} diff --git a/pkg/api/oas_request_encoders_gen.go b/pkg/api/oas_request_encoders_gen.go index ae379a2..ab746dc 100644 --- a/pkg/api/oas_request_encoders_gen.go +++ b/pkg/api/oas_request_encoders_gen.go @@ -1,3 +1,32 @@ // Code generated by ogen, DO NOT EDIT. 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 +} diff --git a/pkg/api/oas_schemas_gen.go b/pkg/api/oas_schemas_gen.go index 3f84df4..c3ba10d 100644 --- a/pkg/api/oas_schemas_gen.go +++ b/pkg/api/oas_schemas_gen.go @@ -262,6 +262,52 @@ func (o OptString) Or(d string) string { 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. func NewOptSubmissionFilter(v SubmissionFilter) OptSubmissionFilter { return OptSubmissionFilter{ @@ -479,6 +525,98 @@ func (s *Submission) SetStatusID(val OptInt32) { 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 type SubmissionFilter struct { ID OptInt64 `json:"ID"` diff --git a/pkg/api/oas_server_gen.go b/pkg/api/oas_server_gen.go index af27b39..63dbd03 100644 --- a/pkg/api/oas_server_gen.go +++ b/pkg/api/oas_server_gen.go @@ -13,7 +13,7 @@ type Handler interface { // Create new submission. // // POST /submissions - CreateSubmission(ctx context.Context) (*ID, error) + CreateSubmission(ctx context.Context, req OptSubmissionCreate) (*ID, error) // GetSubmission implements getSubmission operation. // // Retrieve map with ID. diff --git a/pkg/api/oas_unimplemented_gen.go b/pkg/api/oas_unimplemented_gen.go index 3c6954a..bd7d9b9 100644 --- a/pkg/api/oas_unimplemented_gen.go +++ b/pkg/api/oas_unimplemented_gen.go @@ -18,7 +18,7 @@ var _ Handler = UnimplementedHandler{} // Create new submission. // // 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 } diff --git a/pkg/service/submissions.go b/pkg/service/submissions.go index 7534754..fc55bfd 100644 --- a/pkg/service/submissions.go +++ b/pkg/service/submissions.go @@ -1,13 +1,34 @@ package service import ( + "time" "context" "git.itzana.me/strafesnet/maps-service/pkg/api" + "git.itzana.me/strafesnet/maps-service/pkg/model" ) // POST /submissions -func (svc *Service) CreateSubmission(ctx context.Context) (*api.ID, error) { - return nil, nil +func (svc *Service) CreateSubmission(ctx context.Context, request api.OptSubmissionCreate) (*api.ID, error) { + //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.