Skip to content

Commit

Permalink
update query parameter styles
Browse files Browse the repository at this point in the history
  • Loading branch information
hgiasac committed Apr 1, 2024
1 parent 0ed7bcb commit c9a3c01
Show file tree
Hide file tree
Showing 10 changed files with 612 additions and 102 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
toolchain go1.22.0

require (
github.com/hasura/ndc-rest-schema v0.0.0-20240331093809-e65320e1560c
github.com/hasura/ndc-rest-schema v0.0.0-20240331101658-8eabde102ac0
github.com/hasura/ndc-sdk-go v1.0.1-0.20240331071727-9c3db01b3219
github.com/lmittmann/tint v1.0.4
github.com/stretchr/testify v1.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ github.com/hasura/ndc-rest-schema v0.0.0-20240331090952-d46c51a7ff3b h1:xO4ZLoyr
github.com/hasura/ndc-rest-schema v0.0.0-20240331090952-d46c51a7ff3b/go.mod h1:jcUMvsv1DjSONUk713W+jDNRTE0UQDVqHg6bRHk+Dcc=
github.com/hasura/ndc-rest-schema v0.0.0-20240331093809-e65320e1560c h1:N98Gvv2AuvhbQiypqnsybGdc0zDucV9yzWtwDLK5NrQ=
github.com/hasura/ndc-rest-schema v0.0.0-20240331093809-e65320e1560c/go.mod h1:jcUMvsv1DjSONUk713W+jDNRTE0UQDVqHg6bRHk+Dcc=
github.com/hasura/ndc-rest-schema v0.0.0-20240331101658-8eabde102ac0 h1:SjAV1NDYMPrHUySypAuth2aou+xPPHXzLUgIO7LbKVk=
github.com/hasura/ndc-rest-schema v0.0.0-20240331101658-8eabde102ac0/go.mod h1:jcUMvsv1DjSONUk713W+jDNRTE0UQDVqHg6bRHk+Dcc=
github.com/hasura/ndc-sdk-go v1.0.0 h1:vLbv1k1UIkIXUUrNzi3B4QvAqT1/q2xxlrzff8+Z6+E=
github.com/hasura/ndc-sdk-go v1.0.0/go.mod h1:EeM3hKbhCfBjDDva8mP4D2KeptTqAaxNqNw8rFQAnMs=
github.com/hasura/ndc-sdk-go v1.0.1-0.20240331071727-9c3db01b3219 h1:1HniD4n41sKrKaQIBGO71tivLM3m435FCIJNUKJTX2k=
Expand Down
96 changes: 0 additions & 96 deletions rest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import (
"io"
"log/slog"
"net/http"
"net/url"
"reflect"
"slices"
"strings"
"time"

Expand Down Expand Up @@ -161,99 +158,6 @@ func evalHTTPResponse(resp *http.Response, selection schema.NestedField) (any, e
}
}

func evalURLAndHeaderParameters(request *rest.Request, argumentsSchema map[string]schema.ArgumentInfo, arguments map[string]any) (string, http.Header, error) {
endpoint, err := url.Parse(request.URL)
if err != nil {
return "", nil, err
}
headers := http.Header{}
for k, h := range request.Headers {
headers.Add(k, h)
}

for _, param := range request.Parameters {
argSchema, schemaOk := argumentsSchema[param.Name]
value, ok := arguments[param.Name]

if !schemaOk || !ok || utils.IsNil(value) {
if param.Required {
return "", nil, fmt.Errorf("parameter %s is required", param.Name)
}
} else if err := evalURLAndHeaderParameterBySchema(endpoint, &headers, &param, argSchema.Type, value); err != nil {
return "", nil, err
}
}
return endpoint.String(), headers, nil
}

// the query parameters serialization follows [OAS 3.1 spec]
//
// [OAS 3.1 spec]: https://swagger.io/docs/specification/serialization/
func evalURLAndHeaderParameterBySchema(endpoint *url.URL, header *http.Header, param *rest.RequestParameter, argumentType schema.Type, value any) error {
if utils.IsNil(value) {
return nil
}

var values []string
switch arg := argumentType.Interface().(type) {
case *schema.NamedType:
switch arg.Name {
case "Boolean":
values = []string{fmt.Sprintf("%t", value)}
case "Int", "Float", "String":
values = []string{fmt.Sprint(value)}
default:
b, err := json.Marshal(value)
if err != nil {
return err
}
values = []string{string(b)}
}
case *schema.NullableType:
return evalURLAndHeaderParameterBySchema(endpoint, header, param, arg.UnderlyingType, value)
case *schema.ArrayType:
if !slices.Contains([]rest.ParameterLocation{rest.InHeader, rest.InQuery}, param.In) {
return fmt.Errorf("cannot evaluate array parameter to %s", param.In)
}
if !utils.IsNil(value) {
arrayValue, ok := value.([]any)
if !ok {
return fmt.Errorf("cannot evaluate array parameter, expected array, got: %+v", reflect.TypeOf(value).Name())
}
for _, item := range arrayValue {
values = append(values, fmt.Sprint(item))
}
}
}

// following the OAS spec to serialize parameters
// https://swagger.io/docs/specification/serialization/
switch param.In {
case rest.InHeader:
header.Set(param.Name, strings.Join(values, ","))
case rest.InQuery:
q := endpoint.Query()
if param.Explode {
for _, v := range values {
q.Add(param.Name, v)
}
} else {
switch param.Style {
case rest.EncodingStyleSpaceDelimited:
q.Add(param.Name, strings.Join(values, "%20"))
case rest.EncodingStylePipeDelimited:
q.Add(param.Name, strings.Join(values, "|"))
default:
q.Add(param.Name, strings.Join(values, ","))
}
}
endpoint.RawQuery = q.Encode()
case rest.InPath:
endpoint.Path = strings.ReplaceAll(endpoint.Path, fmt.Sprintf("{%s}", param.Name), strings.Join(values, ","))
}
return nil
}

func parseContentType(input string) string {
if input == "" {
return ""
Expand Down
3 changes: 2 additions & 1 deletion rest/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (
type RESTConnector struct {
metadata RESTMetadataCollection
capabilities *schema.RawCapabilitiesResponse
schema *schema.RawSchemaResponse
rawSchema *schema.RawSchemaResponse
schema *schema.SchemaResponse
client *httpClient
}

Expand Down
99 changes: 99 additions & 0 deletions rest/internal/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package internal

type StringSlicePairs []*StringSlicePair

func (ssp *StringSlicePairs) Add(keys []string, values []string) {
index := ssp.FindIndex(keys)
if index == -1 {
ssp.Add(keys, values)
return
}
(*ssp)[index].AddValues(values)
}

func (ssp StringSlicePairs) Find(keys []string) *StringSlicePair {
item, _ := ssp.find(keys)
return item
}

func (ssp StringSlicePairs) FindIndex(keys []string) int {
_, i := ssp.find(keys)
return i
}

func (ssp StringSlicePairs) find(keys []string) (*StringSlicePair, int) {
for i, item := range ssp {
if len(keys) != len(item.keys) {
continue
}
if len(keys) == 0 {
return item, i
}
isEqual := false
for j, value := range item.keys {
isEqual = value == keys[j]
}

if isEqual {
return item, i
}
}
return nil, -1
}

type StringSlicePair struct {
keys []string
values []string
}

func NewStringSlicePair(keys []string, values []string) *StringSlicePair {
return &StringSlicePair{
keys: keys,
values: values,
}
}

func (ssp StringSlicePair) Keys() []string {
return ssp.keys
}

func (ssp StringSlicePair) Values() []string {
return ssp.values
}

func (ssp *StringSlicePair) Add(key string, value string) {
ssp.AddKey(key)
ssp.AddValue(value)
}

func (ssp *StringSlicePair) AddKey(key string) {
ssp.keys = append(ssp.keys, key)
}

func (ssp *StringSlicePair) AddKeys(keys []string) {
ssp.keys = append(ssp.keys, keys...)
}

func (ssp *StringSlicePair) AddValue(value string) {
ssp.values = append(ssp.values, value)
}

func (ssp *StringSlicePair) AddValues(values []string) {
ssp.values = append(ssp.values, values...)
}

func (ssp *StringSlicePair) PrependKey(key string) {
ssp.keys = append([]string{key}, ssp.keys...)
}

func (ssp *StringSlicePair) PrependKeys(keys []string) {
ssp.keys = append(keys, ssp.keys...)
}

func (ssp *StringSlicePair) PrependValue(value string) {
ssp.values = append([]string{value}, ssp.values...)
}

func (ssp *StringSlicePair) PrependValues(values []string) {
ssp.values = append(values, ssp.values...)
}
2 changes: 1 addition & 1 deletion rest/mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (c *RESTConnector) execProcedure(ctx context.Context, operation *schema.Mut
})
}

endpoint, headers, err := evalURLAndHeaderParameters(procedure.Request, procedure.Arguments, rawArgs)
endpoint, headers, err := c.evalURLAndHeaderParameters(procedure.Request, procedure.Arguments, rawArgs)
if err != nil {
return nil, schema.BadRequestError("failed to evaluate URL and Headers from parameters", map[string]any{
"cause": err.Error(),
Expand Down
Loading

0 comments on commit c9a3c01

Please sign in to comment.