Skip to content

Commit

Permalink
use json scalar for unknown response
Browse files Browse the repository at this point in the history
  • Loading branch information
hgiasac committed Dec 14, 2024
1 parent 9ddfa64 commit 222c3d4
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 160 deletions.
47 changes: 1 addition & 46 deletions ndc-http-schema/command/testdata/auth/expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,7 @@
{
"url": {
"env": "PET_STORE_URL"
},
"securitySchemes": {
"api_key": {
"type": "apiKey",
"in": "header",
"name": "api_key",
"value": {
"env": "PET_STORE_API_KEY"
}
},
"basic": {
"type": "basic",
"header": "Authorization",
"username": {
"value": "user"
},
"password": {
"value": "password"
}
},
"bearer": {
"type": "http",
"header": "",
"scheme": "bearer",
"value": {
"env": "PET_STORE_BEARER_TOKEN"
}
},
"petstore_auth": {
"type": "oauth2",
"flows": {
"implicit": {
"authorizationUrl": "https://petstore3.swagger.io/oauth/authorize",
"scopes": {
"read:pets": "read your pets",
"write:pets": "modify pets in your account"
}
}
}
}
},
"security": [
{
"api_key": []
}
]
}
}
],
"securitySchemes": {
Expand Down
50 changes: 2 additions & 48 deletions ndc-http-schema/command/testdata/patch/expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,8 @@
"value": {
"value": "dog-secret"
}
},
"basic": {
"type": "basic",
"header": "Authorization",
"username": {
"value": "user"
},
"password": {
"value": "password"
}
},
"bearer": {
"type": "http",
"header": "",
"scheme": "bearer",
"value": {
"env": "PET_STORE_BEARER_TOKEN"
}
}
},
"security": [
{
"api_key": []
}
]
}
},
{
"url": {
Expand All @@ -55,31 +32,8 @@
"value": {
"value": "cat-secret"
}
},
"basic": {
"type": "basic",
"header": "Authorization",
"username": {
"value": "user"
},
"password": {
"value": "password"
}
},
"bearer": {
"type": "http",
"header": "",
"scheme": "bearer",
"value": {
"env": "PET_STORE_BEARER_TOKEN"
}
}
},
"security": [
{
"api_key": []
}
]
}
}
],
"securitySchemes": {
Expand Down
27 changes: 18 additions & 9 deletions ndc-http-schema/openapi/internal/oas2_operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,20 @@ func (oc *oas2OperationBuilder) BuildFunction(operation *v2.Operation, commonPar
}

description := oc.getOperationDescription(operation)
requestURL, arguments, err := evalOperationPath(oc.builder.schema, oc.pathKey, oc.Arguments)
if err != nil {
return nil, "", fmt.Errorf("%s: %w", funcName, err)
}
function := rest.OperationInfo{
Request: &rest.Request{
URL: oc.pathKey,
URL: requestURL,
Method: "get",
RequestBody: reqBody,
Response: *response,
Security: convertSecurities(operation.Security),
},
Description: &description,
Arguments: oc.Arguments,
Arguments: arguments,
ResultType: resultType.Encode(),
}

Expand Down Expand Up @@ -99,16 +103,20 @@ func (oc *oas2OperationBuilder) BuildProcedure(operation *v2.Operation, commonPa
}

description := oc.getOperationDescription(operation)
requestURL, arguments, err := evalOperationPath(oc.builder.schema, oc.pathKey, oc.Arguments)
if err != nil {
return nil, "", fmt.Errorf("%s: %w", procName, err)
}
procedure := rest.OperationInfo{
Request: &rest.Request{
URL: oc.pathKey,
URL: requestURL,
Method: oc.method,
RequestBody: reqBody,
Security: convertSecurities(operation.Security),
Response: *response,
},
Description: &description,
Arguments: oc.Arguments,
Arguments: arguments,
ResultType: resultType.Encode(),
}

Expand Down Expand Up @@ -316,13 +324,14 @@ func (oc *oas2OperationBuilder) convertResponse(operation *v2.Operation, fieldPa
}

// return nullable boolean type if the response content is null
if resp == nil || (resp.Schema == nil && statusCode == 204) {
scalarName := string(rest.ScalarBoolean)
if _, ok := oc.builder.schema.ScalarTypes[scalarName]; !ok {
oc.builder.schema.ScalarTypes[scalarName] = *defaultScalarTypes[rest.ScalarBoolean]
if resp == nil || resp.Schema == nil {
scalarName := rest.ScalarJSON
if statusCode == 204 {
scalarName = rest.ScalarBoolean
}
oc.builder.schema.AddScalar(string(scalarName), *defaultScalarTypes[scalarName])

return schema.NewNullableNamedType(scalarName), response, nil
return schema.NewNullableNamedType(string(scalarName)), response, nil
}

if resp.Schema == nil {
Expand Down
29 changes: 21 additions & 8 deletions ndc-http-schema/openapi/internal/oas3_operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,21 @@ func (oc *oas3OperationBuilder) BuildFunction(itemGet *v3.Operation) (*rest.Oper
}

description := oc.getOperationDescription(itemGet)
requestURL, arguments, err := evalOperationPath(oc.builder.schema, oc.pathKey, oc.Arguments)
if err != nil {
return nil, "", fmt.Errorf("%s: %w", funcName, err)
}

function := rest.OperationInfo{
Request: &rest.Request{
URL: oc.pathKey,
URL: requestURL,
Method: "get",
Security: convertSecurities(itemGet.Security),
Servers: oc.builder.convertServers(itemGet.Servers),
Response: *schemaResponse,
},
Description: &description,
Arguments: oc.Arguments,
Arguments: arguments,
ResultType: resultType.Encode(),
}

Expand Down Expand Up @@ -137,17 +142,22 @@ func (oc *oas3OperationBuilder) BuildProcedure(operation *v3.Operation) (*rest.O
}

description := oc.getOperationDescription(operation)
requestURL, arguments, err := evalOperationPath(oc.builder.schema, oc.pathKey, oc.Arguments)
if err != nil {
return nil, "", fmt.Errorf("%s: %w", procName, err)
}

procedure := rest.OperationInfo{
Request: &rest.Request{
URL: oc.pathKey,
URL: requestURL,
Method: oc.method,
Security: convertSecurities(operation.Security),
Servers: oc.builder.convertServers(operation.Servers),
RequestBody: reqBody,
Response: *schemaResponse,
},
Description: &description,
Arguments: oc.Arguments,
Arguments: arguments,
ResultType: resultType.Encode(),
}

Expand Down Expand Up @@ -374,12 +384,15 @@ func (oc *oas3OperationBuilder) convertResponse(responses *v3.Responses, apiPath
}
}

// return nullable boolean type if the response content is null
// return nullable JSON type if the response content is null
if resp == nil || resp.Content == nil {
scalarName := string(rest.ScalarBoolean)
oc.builder.schema.AddScalar(scalarName, *defaultScalarTypes[rest.ScalarBoolean])
scalarName := rest.ScalarJSON
if statusCode == 204 {
scalarName = rest.ScalarBoolean
}
oc.builder.schema.AddScalar(string(scalarName), *defaultScalarTypes[scalarName])

return schema.NewNullableNamedType(scalarName), &rest.Response{
return schema.NewNullableNamedType(string(scalarName)), &rest.Response{
ContentType: rest.ContentTypeJSON,
}, nil
}
Expand Down
1 change: 1 addition & 0 deletions ndc-http-schema/openapi/internal/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

var (
bracketRegexp = regexp.MustCompile(`[\{\}]`)
oasVariableRegex = regexp.MustCompile(`^\{([a-zA-Z0-9_-]+)\}$`)
schemaRefNameV2Regexp = regexp.MustCompile(`^#/definitions/(.+)$`)
schemaRefNameV3Regexp = regexp.MustCompile(`^#/components/schemas/(.+)$`)
)
Expand Down
73 changes: 73 additions & 0 deletions ndc-http-schema/openapi/internal/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package internal
import (
"fmt"
"log/slog"
"net/url"
"slices"
"strings"
"unicode"
Expand Down Expand Up @@ -487,3 +488,75 @@ func createTLSConfig(keys []string) *rest.TLSConfig {
ServerName: &serverName,
}
}

func evalOperationPath(httpSchema *rest.NDCHttpSchema, rawPath string, arguments map[string]rest.ArgumentInfo) (string, map[string]rest.ArgumentInfo, error) {
var pathURL *url.URL
var isAbsolute bool
var err error

if strings.HasPrefix(rawPath, "http") {
isAbsolute = true
pathURL, err = url.Parse(rawPath)
if err != nil {
return "", nil, err
}
} else {
pathURL, err = url.Parse("http://example.local" + rawPath)
if err != nil {
return "", nil, err
}
}

var newQuery url.Values
q := pathURL.Query()
for key, value := range q {
if len(value) == 0 || value[0] == "" {
continue
}

matches := oasVariableRegex.FindStringSubmatch(value[0])
if len(matches) < 2 {
newQuery.Set(key, value[0])

continue
}

variableName := matches[1]
if _, ok := arguments[variableName]; ok {
// the argument exists, skip the next value
continue
}

httpSchema.AddScalar(string(rest.ScalarString), *defaultScalarTypes[rest.ScalarString])
arguments[variableName] = rest.ArgumentInfo{
ArgumentInfo: schema.ArgumentInfo{
Type: schema.NewNamedType(string(rest.ScalarString)).Encode(),
},
HTTP: &rest.RequestParameter{
Name: variableName,
In: rest.InQuery,
Schema: &rest.TypeSchema{
Type: []string{"string"},
},
},
}
}

pathURL.RawQuery = newQuery.Encode()
if isAbsolute {
return pathURL.String(), arguments, nil
}

queryString := pathURL.Query().Encode()

if queryString != "" {
queryString = "?" + queryString
}

fragment := pathURL.EscapedFragment()
if fragment != "" {
fragment = "#" + fragment
}

return pathURL.Path + queryString + fragment, arguments, nil
}
9 changes: 1 addition & 8 deletions ndc-http-schema/openapi/oas3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import (
"encoding/json"
"errors"
"os"
"reflect"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hasura/ndc-http/ndc-http-schema/schema"
"gotest.tools/v3/assert"
)
Expand Down Expand Up @@ -129,9 +126,5 @@ func assertRESTSchemaEqual(t *testing.T, expected *schema.NDCHttpSchema, output

func assertDeepEqual(t *testing.T, expected any, reality any) {
t.Helper()
assert.DeepEqual(t,
expected, reality,
cmpopts.IgnoreUnexported(schema.ServerConfig{}, schema.NDCHttpSettings{}),
cmp.Exporter(func(t reflect.Type) bool { return true }),
)
assert.DeepEqual(t, expected, reality)
}
Loading

0 comments on commit 222c3d4

Please sign in to comment.