From adbef2a46fb1e1d1bcfed619f9dba2e6ec1281c7 Mon Sep 17 00:00:00 2001 From: Joshua Sosso Date: Fri, 6 Sep 2024 01:07:14 -0500 Subject: [PATCH] adsfadsf --- go.mod | 1 + go.sum | 2 ++ languages/go/go-server/app.go | 13 ++++++++++--- languages/go/go-server/decode_json.go | 4 ++-- languages/go/go-server/decode_url_query.go | 5 ++++- languages/go/go-server/error.go | 2 +- playground/go/main.go | 16 +++++++++++++--- 7 files changed, 33 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index f9eeb227..28d701be 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( ) require ( + github.com/google/uuid v1.6.0 github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index 2f4affed..59d0da10 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= diff --git a/languages/go/go-server/app.go b/languages/go/go-server/app.go index a6c64534..754f0968 100644 --- a/languages/go/go-server/app.go +++ b/languages/go/go-server/app.go @@ -143,6 +143,7 @@ func NewApp[TContext any](mux *http.ServeMux, options AppOptions[TContext], crea onError = func(r *http.Request, t *TContext, err error) {} } mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") ctx, ctxErr := app.CreateContext(r) if ctxErr != nil { handleError(false, w, r, ctx, ctxErr.ErrorResponse(), onError) @@ -184,6 +185,7 @@ func NewApp[TContext any](mux *http.ServeMux, options AppOptions[TContext], crea }) mux.HandleFunc(defPath, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") ctx, ctxErr := app.CreateContext(r) if ctxErr != nil { handleError(false, w, r, ctx, ctxErr.ErrorResponse(), onError) @@ -223,7 +225,7 @@ func handleError[TContext any]( return } w.WriteHeader(int(err.Code)) - body, _ := ToJson(err, KeyCasingCamelCase) + body := err.ToJson() w.Write(body) } @@ -287,6 +289,7 @@ func rpc[TParams, TResponse, TContext any](app *App[TContext], options *RpcOptio paramsZero := reflect.Zero(reflect.TypeFor[TParams]()) app.Mux.HandleFunc(rpcSchema.Http.Path, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") ctx, ctxErr := app.CreateContext(r) if ctxErr != nil { handleError(false, w, r, nil, ctxErr.ErrorResponse(), app.Options.OnError) @@ -308,8 +311,12 @@ func rpc[TParams, TResponse, TContext any](app *App[TContext], options *RpcOptio } switch rpcSchema.Http.Method { case HttpMethodGet: - handleError(false, w, r, ctx, ErrorResponse{Code: 400, Message: "Get requests not yet supported"}, onError) - return + urlValues := r.URL.Query() + fromUrlQueryErr := FromUrlQuery(urlValues, ¶ms, app.Options.KeyCasing) + if fromUrlQueryErr != nil { + handleError(false, w, r, ctx, fromUrlQueryErr.ErrorResponse(), onError) + return + } default: b, bErr := io.ReadAll(r.Body) if bErr != nil { diff --git a/languages/go/go-server/decode_json.go b/languages/go/go-server/decode_json.go index 6fbe3e3e..53d4273e 100644 --- a/languages/go/go-server/decode_json.go +++ b/languages/go/go-server/decode_json.go @@ -19,13 +19,13 @@ type ValidationError struct { } func (e ValidationError) Error() string { - return e.Message + " at \"" + e.InstancePath + "\"" + return e.Message + " at " + e.InstancePath } func (e ValidationError) ErrorResponse() ErrorResponse { return ErrorResponse{ Code: 400, - Message: e.Message + " at \"" + e.InstancePath + "\"", + Message: e.Message + " at " + e.InstancePath, Data: Some[any](e), Stack: None[[]string](), } diff --git a/languages/go/go-server/decode_url_query.go b/languages/go/go-server/decode_url_query.go index 6c5da50b..b137fda9 100644 --- a/languages/go/go-server/decode_url_query.go +++ b/languages/go/go-server/decode_url_query.go @@ -44,7 +44,6 @@ func FromUrlQuery[T any](values url.Values, target *T, keyCasing KeyCasing) *Val } urlValue := values.Get(key) - isOptional := isOptionalType(fieldType) if isOptional { ctx := ctx.copyWith( @@ -59,6 +58,10 @@ func FromUrlQuery[T any](values url.Values, target *T, keyCasing KeyCasing) *Val } continue } + hasUrlValue := values.Has(key) + if !hasUrlValue { + return &ValidationError{Message: "missing required field", InstancePath: "/" + key, SchemaPath: "/properties/" + key} + } ctx := ctx.copyWith( None[uint32](), Some(enumValues), diff --git a/languages/go/go-server/error.go b/languages/go/go-server/error.go index ef5cb9b4..c8e786a4 100644 --- a/languages/go/go-server/error.go +++ b/languages/go/go-server/error.go @@ -26,7 +26,7 @@ func (e ErrorResponse) ErrorResponse() ErrorResponse { func (e ErrorResponse) ToJson() []byte { output := []byte{} - output = append(output, "{\"code\":"+strconv.FormatUint(uint64(e.Code), 32)...) + output = append(output, "{\"code\":"+strconv.FormatUint(uint64(e.Code), 10)...) output = append(output, ",\"message\":"...) appendNormalizedString(&output, e.Message) if e.Data.IsSome() { diff --git a/playground/go/main.go b/playground/go/main.go index 189526b6..b2a3f5b0 100644 --- a/playground/go/main.go +++ b/playground/go/main.go @@ -6,9 +6,15 @@ import ( "log" "net/http" "time" + + "github.com/google/uuid" ) -type MyCustomContext struct{} +type MyCustomContext struct { + ReqId string + ReqStart time.Time + IsLoggedIn bool +} func onRequest(r *http.Request, c MyCustomContext) arri.Error { fmt.Println("NEW REQUEST", r.URL.Path) @@ -33,11 +39,15 @@ func main() { // create the RPC context for each request // this is generic so users can user whatever struct they want for their context (func(r *http.Request) (*MyCustomContext, arri.Error) { - return &MyCustomContext{}, nil + ctx := MyCustomContext{ + ReqId: uuid.New().String(), + ReqStart: time.Now(), + } + return &ctx, nil }), ) // register an RPC - arri.Rpc(&app, GetUser) + arri.RpcWithOptions(&app, arri.RpcOptions{Method: arri.HttpMethodGet}, GetUser) arri.Rpc(&app, DeleteUser) // register an RPC with a custom HTTP method and path arri.RpcWithOptions(&app, arri.RpcOptions{