diff --git a/ndc-http-schema/openapi/internal/oas2.go b/ndc-http-schema/openapi/internal/oas2.go index e187933..ac111ea 100644 --- a/ndc-http-schema/openapi/internal/oas2.go +++ b/ndc-http-schema/openapi/internal/oas2.go @@ -251,14 +251,14 @@ func (oc *OAS2Builder) convertComponentSchemas(schemaItem orderedmap.Pair[string scalar.Representation = schema.NewTypeRepresentationJSON().Encode() oc.schema.ScalarTypes[refName] = *scalar oc.schemaCache[cacheKey] = SchemaInfoCache{ - Name: refName, - Schema: schema.NewNamedType(refName), + TypeRead: schema.NewNamedType(refName), + TypeWrite: schema.NewNamedType(refName), TypeSchema: schemaResult, } } else { oc.schemaCache[cacheKey] = SchemaInfoCache{ - Name: typeName, - Schema: typeEncoder, + TypeRead: typeEncoder, + TypeWrite: typeEncoder, TypeSchema: schemaResult, } } diff --git a/ndc-http-schema/openapi/internal/oas2_schema.go b/ndc-http-schema/openapi/internal/oas2_schema.go index e0ffda9..94f150c 100644 --- a/ndc-http-schema/openapi/internal/oas2_schema.go +++ b/ndc-http-schema/openapi/internal/oas2_schema.go @@ -282,7 +282,10 @@ func (oc *oas2SchemaBuilder) getSchemaTypeFromProxy(schemaProxy *base.SchemaProx return nil, nil, err } } else if typeCache, ok := oc.builder.schemaCache[rawRefName]; ok { - ndcType = typeCache.Schema + ndcType = typeCache.TypeRead + if oc.writeMode { + ndcType = typeCache.TypeWrite + } typeSchema = createSchemaFromOpenAPISchema(innerSchema) if typeCache.TypeSchema != nil { typeSchema.Type = typeCache.TypeSchema.Type @@ -292,8 +295,8 @@ func (oc *oas2SchemaBuilder) getSchemaTypeFromProxy(schemaProxy *base.SchemaProx refName := getSchemaRefTypeNameV2(rawRefName) schemaName := utils.ToPascalCase(refName) oc.builder.schemaCache[rawRefName] = SchemaInfoCache{ - Name: schemaName, - Schema: schema.NewNamedType(schemaName), + TypeRead: schema.NewNamedType(schemaName), + TypeWrite: schema.NewNamedType(schemaName), } _, ok := oc.builder.schema.ObjectTypes[schemaName] @@ -303,8 +306,8 @@ func (oc *oas2SchemaBuilder) getSchemaTypeFromProxy(schemaProxy *base.SchemaProx return nil, nil, err } oc.builder.schemaCache[rawRefName] = SchemaInfoCache{ - Name: schemaName, - Schema: ndcType, + TypeRead: ndcType, + TypeWrite: ndcType, TypeSchema: typeSchema, } } else { diff --git a/ndc-http-schema/openapi/internal/oas3.go b/ndc-http-schema/openapi/internal/oas3.go index 848262f..b70181a 100644 --- a/ndc-http-schema/openapi/internal/oas3.go +++ b/ndc-http-schema/openapi/internal/oas3.go @@ -2,6 +2,7 @@ package internal import ( "fmt" + "log" "log/slog" "strings" @@ -23,14 +24,14 @@ type OAS3Builder struct { // stores prebuilt and evaluating information of component schema types. // some undefined schema types aren't stored in either object nor scalar, // or self-reference types that haven't added into the object_types map yet. - // This cache temporarily stores them to avoid infinite recursive reference. + // This cache temporarily stores them to avoid infinite recursive references. schemaCache map[string]SchemaInfoCache } // SchemaInfoCache stores prebuilt information of component schema types. type SchemaInfoCache struct { - Name string - Schema schema.TypeEncoder + TypeRead schema.TypeEncoder + TypeWrite schema.TypeEncoder TypeSchema *rest.TypeSchema } @@ -266,23 +267,29 @@ func (oc *OAS3Builder) convertComponentSchemas(schemaItem orderedmap.Pair[string return nil } - typeEncoder, schemaResult, err := newOAS3SchemaBuilder(oc, "", rest.InBody, false). + schemaResult, err := newOAS3SchemaBuilder(oc, "", rest.InBody, false). getSchemaType(typeSchema, []string{typeKey}) if err != nil { return err } + if schemaResult == nil { + log.Println("can not build schema for", typeKey) + + return nil + } + var typeName string - if typeEncoder != nil { - typeName = getNamedType(typeEncoder, true, "") + if schemaResult.TypeRead != nil { + typeName = getNamedType(schemaResult.TypeRead, true, "") } - if schemaResult != nil { - if schemaResult.XML == nil { - schemaResult.XML = &rest.XMLSchema{} + if schemaResult.TypeSchema != nil { + if schemaResult.TypeSchema.XML == nil { + schemaResult.TypeSchema.XML = &rest.XMLSchema{} } - if schemaResult.XML.Name == "" { - schemaResult.XML.Name = typeKey + if schemaResult.TypeSchema.XML.Name == "" { + schemaResult.TypeSchema.XML.Name = typeKey } } @@ -295,22 +302,18 @@ func (oc *OAS3Builder) convertComponentSchemas(schemaItem orderedmap.Pair[string cacheKey := "#/components/schemas/" + typeKey // treat no-property objects as a Arbitrary JSON scalar - if typeEncoder == nil || typeName == string(rest.ScalarJSON) { + if schemaResult.TypeRead == nil || typeName == string(rest.ScalarJSON) { refName := utils.ToPascalCase(typeKey) scalar := schema.NewScalarType() scalar.Representation = schema.NewTypeRepresentationJSON().Encode() oc.schema.ScalarTypes[refName] = *scalar oc.schemaCache[cacheKey] = SchemaInfoCache{ - Name: refName, - Schema: schema.NewNamedType(refName), - TypeSchema: schemaResult, + TypeRead: schema.NewNamedType(refName), + TypeWrite: schema.NewNamedType(refName), + TypeSchema: schemaResult.TypeSchema, } } else { - oc.schemaCache[cacheKey] = SchemaInfoCache{ - Name: typeName, - Schema: typeEncoder, - TypeSchema: schemaResult, - } + oc.schemaCache[cacheKey] = *schemaResult } return err @@ -369,8 +372,8 @@ func (oc *OAS3Builder) populateWriteSchemaType(schemaType schema.Type) (schema.T _, evaluated := oc.schemaCache[ty.Name] if !evaluated { oc.schemaCache[ty.Name] = SchemaInfoCache{ - Name: ty.Name, - Schema: schema.NewNamedType(ty.Name), + TypeRead: schema.NewNamedType(ty.Name), + TypeWrite: schema.NewNamedType(ty.Name), TypeSchema: &rest.TypeSchema{ Type: []string{"object"}, }, diff --git a/ndc-http-schema/openapi/internal/oas3_operation.go b/ndc-http-schema/openapi/internal/oas3_operation.go index ce8bdb2..0cfc6fa 100644 --- a/ndc-http-schema/openapi/internal/oas3_operation.go +++ b/ndc-http-schema/openapi/internal/oas3_operation.go @@ -182,7 +182,7 @@ func (oc *oas3OperationBuilder) convertParameters(params []*v3.Parameter, apiPat if param.Required != nil && *param.Required { paramRequired = true } - schemaType, apiSchema, err := newOAS3SchemaBuilder(oc.builder, apiPath, rest.ParameterLocation(param.In), true). + schemaResult, err := newOAS3SchemaBuilder(oc.builder, apiPath, rest.ParameterLocation(param.In), true). getSchemaTypeFromProxy(param.Schema, !paramRequired, append(fieldPaths, paramName)) if err != nil { return err @@ -207,12 +207,12 @@ func (oc *oas3OperationBuilder) convertParameters(params []*v3.Parameter, apiPat argument := rest.ArgumentInfo{ ArgumentInfo: schema.ArgumentInfo{ - Type: schemaType.Encode(), + Type: schemaResult.TypeWrite.Encode(), }, HTTP: &rest.RequestParameter{ Name: paramName, In: paramLocation, - Schema: apiSchema, + Schema: schemaResult.TypeSchema, EncodingObject: encoding, }, } @@ -263,13 +263,13 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP if contentType == rest.ContentTypeFormURLEncoded { location = rest.InQuery } - schemaType, typeSchema, err := newOAS3SchemaBuilder(oc.builder, apiPath, location, true). + typeResult, err := newOAS3SchemaBuilder(oc.builder, apiPath, location, true). getSchemaTypeFromProxy(content.Schema, !bodyRequired, fieldPaths) if err != nil { return nil, nil, err } - if typeSchema == nil { + if typeResult == nil || typeResult.TypeRead == nil { return nil, nil, nil } @@ -278,7 +278,7 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP } if content.Encoding == nil || content.Encoding.Len() == 0 { - return bodyResult, schemaType, nil + return bodyResult, typeResult.TypeWrite, nil } bodyResult.Encoding = make(map[string]rest.EncodingObject) @@ -311,7 +311,7 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP continue } - ndcType, typeSchema, err := newOAS3SchemaBuilder(oc.builder, apiPath, rest.InHeader, true). + typeResult, err := newOAS3SchemaBuilder(oc.builder, apiPath, rest.InHeader, true). getSchemaTypeFromProxy(header.Schema, header.AllowEmptyValue, append(fieldPaths, key)) if err != nil { return nil, nil, err @@ -333,12 +333,12 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP argumentName := encodeHeaderArgumentName(key) headerParam := rest.RequestParameter{ ArgumentName: argumentName, - Schema: typeSchema, + Schema: typeResult.TypeSchema, EncodingObject: headerEncoding, } argument := schema.ArgumentInfo{ - Type: ndcType.Encode(), + Type: typeResult.TypeWrite.Encode(), } headerDesc := utils.StripHTMLTags(header.Description) if headerDesc != "" { @@ -354,7 +354,7 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP bodyResult.Encoding[iter.Key()] = item } - return bodyResult, schemaType, nil + return bodyResult, typeResult.TypeWrite, nil } func (oc *oas3OperationBuilder) convertResponse(responses *v3.Responses, apiPath string, fieldPaths []string) (schema.TypeEncoder, *rest.Response, error) { @@ -428,7 +428,7 @@ func (oc *oas3OperationBuilder) convertResponse(responses *v3.Responses, apiPath return getResultTypeFromContentType(oc.builder.schema, contentType), schemaResponse, nil } - schemaType, _, err := newOAS3SchemaBuilder(oc.builder, apiPath, rest.InBody, false). + typeResult, err := newOAS3SchemaBuilder(oc.builder, apiPath, rest.InBody, false). getSchemaTypeFromProxy(bodyContent.Schema, false, fieldPaths) if err != nil { return nil, nil, err @@ -438,9 +438,9 @@ func (oc *oas3OperationBuilder) convertResponse(responses *v3.Responses, apiPath case rest.ContentTypeNdJSON: // Newline Delimited JSON (ndjson) format represents a stream of structured objects // so the response would be wrapped with an array - return schema.NewArrayType(schemaType), schemaResponse, nil + return schema.NewArrayType(typeResult.TypeRead), schemaResponse, nil default: - return schemaType, schemaResponse, nil + return typeResult.TypeRead, schemaResponse, nil } } diff --git a/ndc-http-schema/openapi/internal/oas3_schema.go b/ndc-http-schema/openapi/internal/oas3_schema.go index cfbec3a..573e1af 100644 --- a/ndc-http-schema/openapi/internal/oas3_schema.go +++ b/ndc-http-schema/openapi/internal/oas3_schema.go @@ -30,79 +30,71 @@ func newOAS3SchemaBuilder(builder *OAS3Builder, apiPath string, location rest.Pa } // get and convert an OpenAPI data type to a NDC type -func (oc *oas3SchemaBuilder) getSchemaTypeFromProxy(schemaProxy *base.SchemaProxy, nullable bool, fieldPaths []string) (schema.TypeEncoder, *rest.TypeSchema, error) { +func (oc *oas3SchemaBuilder) getSchemaTypeFromProxy(schemaProxy *base.SchemaProxy, nullable bool, fieldPaths []string) (*SchemaInfoCache, error) { if schemaProxy == nil { - return nil, nil, errParameterSchemaEmpty(fieldPaths) + return nil, errParameterSchemaEmpty(fieldPaths) } innerSchema := schemaProxy.Schema() if innerSchema == nil { - return nil, nil, fmt.Errorf("cannot get schema of $.%s from proxy: %s", strings.Join(fieldPaths, "."), schemaProxy.GetReference()) + return nil, fmt.Errorf("cannot get schema of $.%s from proxy: %s", strings.Join(fieldPaths, "."), schemaProxy.GetReference()) } - var ndcType schema.TypeEncoder - var typeSchema *rest.TypeSchema + var result *SchemaInfoCache var err error rawRefName := schemaProxy.GetReference() if rawRefName == "" { - ndcType, typeSchema, err = oc.getSchemaType(innerSchema, fieldPaths) + result, err = oc.getSchemaType(innerSchema, fieldPaths) if err != nil { - return nil, nil, err + return nil, err } } else if typeCache, ok := oc.builder.schemaCache[rawRefName]; ok { - ndcType = typeCache.Schema - typeSchema = createSchemaFromOpenAPISchema(innerSchema) - if typeCache.TypeSchema != nil { - typeSchema.Type = typeCache.TypeSchema.Type - } + result = &typeCache } else { // return early object from ref refName := getSchemaRefTypeNameV3(rawRefName) - schemaName := utils.ToPascalCase(refName) + readSchemaName := utils.ToPascalCase(refName) + writeSchemaName := formatWriteObjectName(readSchemaName) + oc.builder.schemaCache[rawRefName] = SchemaInfoCache{ - Name: schemaName, - Schema: schema.NewNamedType(schemaName), + TypeRead: schema.NewNamedType(readSchemaName), + TypeWrite: schema.NewNamedType(writeSchemaName), } - _, ok := oc.builder.schema.ObjectTypes[schemaName] - if !ok { - ndcType, typeSchema, err = oc.getSchemaType(innerSchema, []string{refName}) - if err != nil { - return nil, nil, err - } - oc.builder.schemaCache[rawRefName] = SchemaInfoCache{ - Name: schemaName, - Schema: ndcType, - TypeSchema: typeSchema, - } - } else { - ndcType = schema.NewNamedType(schemaName) - typeSchema = createSchemaFromOpenAPISchema(innerSchema) + result, err = oc.getSchemaType(innerSchema, []string{refName}) + if err != nil { + return nil, err } + + oc.builder.schemaCache[rawRefName] = *result } - if ndcType == nil { - return nil, nil, nil + if result == nil || result.TypeRead == nil { + return nil, nil } if nullable { - if !isNullableType(ndcType) { - ndcType = schema.NewNullableType(ndcType) + if !isNullableType(result.TypeRead) { + result.TypeRead = schema.NewNullableType(result.TypeRead) + } + + if !isNullableType(result.TypeWrite) { + result.TypeWrite = schema.NewNullableType(result.TypeWrite) } } - return ndcType, typeSchema, nil + return result, nil } // get and convert an OpenAPI data type to a NDC type -func (oc *oas3SchemaBuilder) getSchemaType(typeSchema *base.Schema, fieldPaths []string) (schema.TypeEncoder, *rest.TypeSchema, error) { +func (oc *oas3SchemaBuilder) getSchemaType(typeSchema *base.Schema, fieldPaths []string) (*SchemaInfoCache, error) { if typeSchema == nil { - return nil, nil, errParameterSchemaEmpty(fieldPaths) + return nil, errParameterSchemaEmpty(fieldPaths) } if oc.builder.ConvertOptions.NoDeprecation && typeSchema.Deprecated != nil && *typeSchema.Deprecated { - return nil, nil, nil + return nil, nil } if len(typeSchema.AllOf) > 0 { @@ -118,104 +110,109 @@ func (oc *oas3SchemaBuilder) getSchemaType(typeSchema *base.Schema, fieldPaths [ } if typeSchema.AdditionalProperties != nil && (typeSchema.AdditionalProperties.B || typeSchema.AdditionalProperties.A != nil) { - return oc.builder.buildScalarJSON(), createSchemaFromOpenAPISchema(typeSchema), nil + return &SchemaInfoCache{ + TypeRead: oc.builder.buildScalarJSON(), + TypeWrite: oc.builder.buildScalarJSON(), + TypeSchema: createSchemaFromOpenAPISchema(typeSchema), + }, nil } - var result schema.TypeEncoder if len(typeSchema.Type) == 0 { if oc.builder.Strict { - return nil, nil, errParameterSchemaEmpty(fieldPaths) + return nil, errParameterSchemaEmpty(fieldPaths) } - result = oc.builder.buildScalarJSON() + var result schema.TypeEncoder = oc.builder.buildScalarJSON() if typeSchema.Nullable != nil && *typeSchema.Nullable { result = schema.NewNullableType(result) } - return result, createSchemaFromOpenAPISchema(typeSchema), nil + return &SchemaInfoCache{ + TypeRead: result, + TypeWrite: result, + TypeSchema: createSchemaFromOpenAPISchema(typeSchema), + }, nil } if len(typeSchema.Type) > 1 || isPrimitiveScalar(typeSchema.Type) { scalarName, nullable := getScalarFromType(oc.builder.schema, typeSchema.Type, typeSchema.Format, typeSchema.Enum, oc.builder.trimPathPrefix(oc.apiPath), fieldPaths) - result = schema.NewNamedType(scalarName) + var resultType schema.TypeEncoder = schema.NewNamedType(scalarName) if nullable || (typeSchema.Nullable != nil && *typeSchema.Nullable) { - result = schema.NewNullableType(result) + resultType = schema.NewNullableType(resultType) } - return result, createSchemaFromOpenAPISchema(typeSchema), nil + return &SchemaInfoCache{ + TypeRead: resultType, + TypeWrite: resultType, + TypeSchema: createSchemaFromOpenAPISchema(typeSchema), + }, nil } + var typeResult *SchemaInfoCache typeName := typeSchema.Type[0] switch typeName { case "object": return oc.evalObjectType(typeSchema, false, fieldPaths) case "array": - typeResult := createSchemaFromOpenAPISchema(typeSchema) + var itemSchema *SchemaInfoCache + var err error nullable := (typeSchema.Nullable != nil && *typeSchema.Nullable) if typeSchema.Items == nil || typeSchema.Items.A == nil { if oc.builder.Strict { - return nil, nil, fmt.Errorf("%s: array item is empty", strings.Join(fieldPaths, ".")) + return nil, fmt.Errorf("%s: array item is empty", strings.Join(fieldPaths, ".")) } - - var result schema.TypeEncoder = oc.builder.buildScalarJSON() - if nullable { - result = schema.NewNullableType(result) + } else { + itemSchema, err = oc.getSchemaTypeFromProxy(typeSchema.Items.A, false, fieldPaths) + if err != nil { + return nil, err } - - return result, typeResult, nil } - itemName := getSchemaRefTypeNameV3(typeSchema.Items.A.GetReference()) - if itemName != "" { - result = schema.NewArrayType(schema.NewNamedType(utils.ToPascalCase(itemName))) - } else { - itemSchemaA := typeSchema.Items.A.Schema() - if itemSchemaA != nil { - itemSchema, propType, err := oc.getSchemaType(itemSchemaA, fieldPaths) - if err != nil { - return nil, nil, err - } - if itemSchema != nil { - result = schema.NewArrayType(itemSchema) - } else { - result = schema.NewArrayType(oc.builder.buildScalarJSON()) - } - - typeResult.Items = propType + if itemSchema == nil { + itemSchema = &SchemaInfoCache{ + TypeRead: schema.NewNullableType(oc.builder.buildScalarJSON()), + TypeWrite: schema.NewNullableType(oc.builder.buildScalarJSON()), } } - if result == nil { - return nil, nil, fmt.Errorf("cannot parse type reference name: %s", typeSchema.Items.A.GetReference()) + typeResult = &SchemaInfoCache{ + TypeSchema: createSchemaFromOpenAPISchema(typeSchema), + TypeRead: schema.NewArrayType(itemSchema.TypeRead), + TypeWrite: schema.NewArrayType(itemSchema.TypeWrite), + } + + if itemSchema.TypeSchema != nil { + typeResult.TypeSchema.Items = itemSchema.TypeSchema } if nullable { - result = schema.NewNullableType(result) + typeResult.TypeRead = schema.NewNullableType(typeResult.TypeRead) + typeResult.TypeWrite = schema.NewNullableType(typeResult.TypeWrite) } - return result, typeResult, nil + return typeResult, nil default: - return nil, nil, fmt.Errorf("unsupported schema type %s", typeName) + return nil, fmt.Errorf("unsupported schema type %s", typeName) } } -func (oc *oas3SchemaBuilder) evalObjectType(baseSchema *base.Schema, forcePropertiesNullable bool, fieldPaths []string) (schema.TypeEncoder, *rest.TypeSchema, error) { +func (oc *oas3SchemaBuilder) evalObjectType(baseSchema *base.Schema, forcePropertiesNullable bool, fieldPaths []string) (*SchemaInfoCache, error) { typeResult := createSchemaFromOpenAPISchema(baseSchema) refName := utils.StringSliceToPascalCase(fieldPaths) if baseSchema.Properties == nil || baseSchema.Properties.IsZero() { - if baseSchema.AdditionalProperties != nil && (baseSchema.AdditionalProperties.A == nil || !baseSchema.AdditionalProperties.B) { - return nil, nil, nil - } // treat no-property objects as a JSON scalar var scalarType schema.TypeEncoder = oc.builder.buildScalarJSON() if baseSchema.Nullable != nil && *baseSchema.Nullable { scalarType = schema.NewNullableType(scalarType) } - return scalarType, typeResult, nil + return &SchemaInfoCache{ + TypeRead: scalarType, + TypeWrite: scalarType, + TypeSchema: typeResult, + }, nil } - var result schema.TypeEncoder readObject := rest.ObjectType{ Fields: make(map[string]rest.ObjectField), XML: typeResult.XML, @@ -237,40 +234,48 @@ func (oc *oas3SchemaBuilder) evalObjectType(baseSchema *base.Schema, forceProper slog.String("name", propName), slog.Any("field", fieldPaths)) nullable := forcePropertiesNullable || !slices.Contains(baseSchema.Required, propName) - propType, propApiSchema, err := oc.getSchemaTypeFromProxy(prop.Value(), nullable, append(fieldPaths, propName)) + propResult, err := oc.getSchemaTypeFromProxy(prop.Value(), nullable, append(fieldPaths, propName)) if err != nil { - return nil, nil, err + return nil, err } - if propType == nil { + if propResult == nil || propResult.TypeRead == nil { continue } - objField := rest.ObjectField{ + if propResult.TypeSchema == nil { + propResult.TypeSchema = &rest.TypeSchema{ + Type: []string{}, + } + } + + readField := rest.ObjectField{ ObjectField: schema.ObjectField{ - Type: propType.Encode(), + Type: propResult.TypeRead.Encode(), }, - HTTP: propApiSchema, + HTTP: propResult.TypeSchema, } - if propApiSchema == nil { - propApiSchema = &rest.TypeSchema{ - Type: []string{}, - } + writeField := rest.ObjectField{ + ObjectField: schema.ObjectField{ + Type: propResult.TypeWrite.Encode(), + }, + HTTP: propResult.TypeSchema, } - if propApiSchema.Description != "" { - objField.Description = &propApiSchema.Description + if propResult.TypeSchema.Description != "" { + readField.Description = &propResult.TypeSchema.Description + writeField.Description = &propResult.TypeSchema.Description } switch { - case !propApiSchema.ReadOnly && !propApiSchema.WriteOnly: - readObject.Fields[propName] = objField - writeObject.Fields[propName] = objField - case !oc.writeMode && propApiSchema.ReadOnly: - readObject.Fields[propName] = objField + case !propResult.TypeSchema.ReadOnly && !propResult.TypeSchema.WriteOnly: + readObject.Fields[propName] = readField + writeObject.Fields[propName] = writeField + case !oc.writeMode && propResult.TypeSchema.ReadOnly: + readObject.Fields[propName] = readField default: - writeObject.Fields[propName] = objField + writeObject.Fields[propName] = writeField } } @@ -285,33 +290,35 @@ func (oc *oas3SchemaBuilder) evalObjectType(baseSchema *base.Schema, forceProper oc.builder.schema.ObjectTypes[refName] = readObject oc.builder.schema.ObjectTypes[writeRefName] = writeObject - if oc.writeMode { - result = schema.NewNamedType(writeRefName) - } else { - result = schema.NewNamedType(refName) + + result := &SchemaInfoCache{ + TypeRead: schema.NewNamedType(refName), + TypeWrite: schema.NewNamedType(writeRefName), + TypeSchema: typeResult, } if baseSchema.Nullable != nil && *baseSchema.Nullable { - result = schema.NewNullableType(result) + result.TypeRead = schema.NewNullableType(result.TypeRead) + result.TypeWrite = schema.NewNullableType(result.TypeWrite) } - return result, typeResult, nil + return result, nil } // Support converting oneOf, allOf or anyOf to object types with merge strategy -func (oc *oas3SchemaBuilder) buildUnionSchemaType(baseSchema *base.Schema, schemaProxies []*base.SchemaProxy, unionType oasUnionType, fieldPaths []string) (schema.TypeEncoder, *rest.TypeSchema, error) { +func (oc *oas3SchemaBuilder) buildUnionSchemaType(baseSchema *base.Schema, schemaProxies []*base.SchemaProxy, unionType oasUnionType, fieldPaths []string) (*SchemaInfoCache, error) { proxies, mergedType, isNullable := evalSchemaProxiesSlice(schemaProxies, oc.location) nullable := isNullable || (baseSchema.Nullable != nil && *baseSchema.Nullable) if mergedType != nil { - typeEncoder, typeSchema, err := oc.getSchemaType(mergedType, fieldPaths) + result, err := oc.getSchemaType(mergedType, fieldPaths) if err != nil { - return nil, nil, err + return nil, err } - if typeSchema != nil && typeSchema.Description == "" && baseSchema.Description != "" { - typeSchema.Description = utils.StripHTMLTags(baseSchema.Description) + if result != nil && result.TypeSchema != nil && result.TypeSchema.Description == "" && baseSchema.Description != "" { + result.TypeSchema.Description = utils.StripHTMLTags(baseSchema.Description) } - return typeEncoder, typeSchema, nil + return result, nil } switch len(proxies) { @@ -323,24 +330,42 @@ func (oc *oas3SchemaBuilder) buildUnionSchemaType(baseSchema *base.Schema, schem result = schema.NewNullableType(result) } - return result, createSchemaFromOpenAPISchema(baseSchema), nil + return &SchemaInfoCache{ + TypeRead: result, + TypeWrite: result, + TypeSchema: createSchemaFromOpenAPISchema(baseSchema), + }, nil } if len(baseSchema.Type) == 1 && baseSchema.Type[0] == "object" { return oc.evalObjectType(baseSchema, true, fieldPaths) } - return schema.NewNamedType(string(rest.ScalarJSON)), createSchemaFromOpenAPISchema(baseSchema), nil + var result schema.TypeEncoder = schema.NewNamedType(string(rest.ScalarJSON)) + if nullable { + result = schema.NewNullableType(result) + } + + return &SchemaInfoCache{ + TypeRead: result, + TypeWrite: result, + TypeSchema: createSchemaFromOpenAPISchema(baseSchema), + }, nil case 1: - typeEncoder, typeSchema, err := oc.getSchemaTypeFromProxy(proxies[0], nullable, fieldPaths) + result, err := oc.getSchemaTypeFromProxy(proxies[0], nullable, fieldPaths) if err != nil { - return nil, nil, err + return nil, err } - if typeSchema != nil && typeSchema.Description == "" && baseSchema.Description != "" { - typeSchema.Description = utils.StripHTMLTags(baseSchema.Description) + + if result == nil { + return nil, nil } - return typeEncoder, typeSchema, nil + if result.TypeSchema != nil && result.TypeSchema.Description == "" && baseSchema.Description != "" { + result.TypeSchema.Description = utils.StripHTMLTags(baseSchema.Description) + } + + return result, nil } typeSchema := &rest.TypeSchema{ @@ -355,15 +380,15 @@ func (oc *oas3SchemaBuilder) buildUnionSchemaType(baseSchema *base.Schema, schem var writeObjectItems []rest.ObjectType for i, item := range proxies { - enc, ty, err := newOAS3SchemaBuilder(oc.builder, oc.apiPath, oc.location, false). + schemaResult, err := newOAS3SchemaBuilder(oc.builder, oc.apiPath, oc.location, false). getSchemaTypeFromProxy(item, nullable, append(fieldPaths, strconv.Itoa(i))) if err != nil { - return nil, nil, err + return nil, err } var readObj rest.ObjectType - name := getNamedType(enc, false, "") - isObject := name != "" && !isPrimitiveScalar(ty.Type) && !slices.Contains(ty.Type, "array") + name := getNamedType(schemaResult.TypeRead, false, "") + isObject := name != "" && !isPrimitiveScalar(schemaResult.TypeSchema.Type) && !slices.Contains(schemaResult.TypeSchema.Type, "array") if isObject { readObj, isObject = oc.builder.schema.ObjectTypes[name] if isObject { @@ -372,12 +397,18 @@ func (oc *oas3SchemaBuilder) buildUnionSchemaType(baseSchema *base.Schema, schem } if !isObject { - ty = &rest.TypeSchema{ + schemaResult.TypeSchema = &rest.TypeSchema{ Description: typeSchema.Description, Type: []string{}, } - return oc.builder.buildScalarJSON(), ty, nil + jsonScalar := oc.builder.buildScalarJSON() + + return &SchemaInfoCache{ + TypeRead: jsonScalar, + TypeWrite: jsonScalar, + TypeSchema: schemaResult.TypeSchema, + }, nil } writeName := formatWriteObjectName(name) @@ -402,11 +433,11 @@ func (oc *oas3SchemaBuilder) buildUnionSchemaType(baseSchema *base.Schema, schem } if err := mergeUnionObjects(oc.builder.schema, &readObject, readObjectItems, unionType, fieldPaths); err != nil { - return nil, nil, err + return nil, err } if err := mergeUnionObjects(oc.builder.schema, &writeObject, writeObjectItems, unionType, fieldPaths); err != nil { - return nil, nil, err + return nil, err } refName := utils.ToPascalCase(strings.Join(fieldPaths, " ")) @@ -422,5 +453,9 @@ func (oc *oas3SchemaBuilder) buildUnionSchemaType(baseSchema *base.Schema, schem refName = writeRefName } - return schema.NewNamedType(refName), typeSchema, nil + return &SchemaInfoCache{ + TypeRead: schema.NewNamedType(refName), + TypeWrite: schema.NewNamedType(writeRefName), + TypeSchema: typeSchema, + }, nil } diff --git a/ndc-http-schema/openapi/internal/utils.go b/ndc-http-schema/openapi/internal/utils.go index accc12b..b11363e 100644 --- a/ndc-http-schema/openapi/internal/utils.go +++ b/ndc-http-schema/openapi/internal/utils.go @@ -785,26 +785,3 @@ func guessScalarResultTypeFromContentType(contentType string) rest.ScalarName { return rest.ScalarBinary } } - -func replaceNamedType(schemaType schema.Type, name string) (schema.TypeEncoder, error) { - switch t := schemaType.Interface().(type) { - case *schema.NullableType: - newType, err := replaceNamedType(t.UnderlyingType, name) - if err != nil { - return nil, err - } - - return schema.NewNullableType(newType), nil - case *schema.ArrayType: - newType, err := replaceNamedType(t.ElementType, name) - if err != nil { - return nil, err - } - - return schema.NewArrayType(newType), nil - case *schema.NamedType: - return schema.NewNamedType(name), nil - default: - return nil, fmt.Errorf("invalid type: %v", schemaType) - } -} diff --git a/ndc-http-schema/openapi/testdata/onesignal/expected.json b/ndc-http-schema/openapi/testdata/onesignal/expected.json index f2e328e..c144afc 100644 --- a/ndc-http-schema/openapi/testdata/onesignal/expected.json +++ b/ndc-http-schema/openapi/testdata/onesignal/expected.json @@ -206,7 +206,10 @@ } }, "http": { - "type": [] + "type": [], + "xml": { + "name": "Notification200Errors" + } } }, "external_id": { @@ -408,6 +411,64 @@ } } }, + "FilterInput": { + "fields": { + "field": { + "description": "Name of the field to use as the first operand in the filter expression.", + "type": { + "name": "String", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "key": { + "description": "If `field` is `tag`, this field is *required* to specify `key` inside the tags.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + }, + "relation": { + "description": "Operator of a filter expression.", + "type": { + "name": "FilterRelation", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "value": { + "description": "Constant value to use as the second operand in the filter expression. This value is *required* when the relation operator is a binary operator.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + } + }, "GetNotificationHistoryBodyInput": { "fields": { "app_id": { @@ -494,7 +555,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "StringMap", + "name": "StringMapInput", "type": "named" } }, @@ -560,7 +621,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "Filter", + "name": "FilterInput", "type": "named" }, "type": "array" @@ -569,14 +630,19 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "headings": { "type": { "type": "nullable", "underlying_type": { - "name": "StringMap", + "name": "StringMapInput", "type": "named" } }, @@ -641,7 +707,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "StringMap", + "name": "StringMapInput", "type": "named" } }, @@ -697,7 +763,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "NotificationWithMeta" + } + } } }, "offset": { @@ -843,7 +917,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "headings": { @@ -910,7 +989,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "platform_delivery_stats": { @@ -1222,6 +1306,25 @@ } } } + }, + "StringMapInput": { + "fields": { + "en": { + "description": "Text in English. Will be used as a fallback", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + } } }, "procedures": { diff --git a/ndc-http-schema/openapi/testdata/onesignal/schema.json b/ndc-http-schema/openapi/testdata/onesignal/schema.json index 0b6eeca..c0b1c2f 100644 --- a/ndc-http-schema/openapi/testdata/onesignal/schema.json +++ b/ndc-http-schema/openapi/testdata/onesignal/schema.json @@ -230,6 +230,44 @@ } } }, + "FilterInput": { + "fields": { + "field": { + "description": "Name of the field to use as the first operand in the filter expression.", + "type": { + "name": "String", + "type": "named" + } + }, + "key": { + "description": "If `field` is `tag`, this field is *required* to specify `key` inside the tags.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + }, + "relation": { + "description": "Operator of a filter expression.", + "type": { + "name": "FilterRelation", + "type": "named" + } + }, + "value": { + "description": "Constant value to use as the second operand in the filter expression. This value is *required* when the relation operator is a binary operator.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } + }, "GetNotificationHistoryBodyInput": { "fields": { "app_id": { @@ -291,7 +329,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "StringMap", + "name": "StringMapInput", "type": "named" } } @@ -332,7 +370,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "Filter", + "name": "FilterInput", "type": "named" }, "type": "array" @@ -343,7 +381,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "StringMap", + "name": "StringMapInput", "type": "named" } } @@ -382,7 +420,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "StringMap", + "name": "StringMapInput", "type": "named" } } @@ -760,6 +798,20 @@ } } } + }, + "StringMapInput": { + "fields": { + "en": { + "description": "Text in English. Will be used as a fallback", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } } }, "procedures": [ diff --git a/ndc-http-schema/openapi/testdata/openai/expected.json b/ndc-http-schema/openapi/testdata/openai/expected.json index 817d7ef..f3abd88 100644 --- a/ndc-http-schema/openapi/testdata/openai/expected.json +++ b/ndc-http-schema/openapi/testdata/openai/expected.json @@ -34,7 +34,7 @@ }, "functions": {}, "object_types": { - "ChatCompletionFunctions": { + "ChatCompletionFunctionsInput": { "fields": { "description": { "description": "A description of what the function does, used by the model to choose when and how to call the function.", @@ -75,7 +75,10 @@ "http": { "type": [ "object" - ] + ], + "xml": { + "name": "FunctionParameters" + } } } } @@ -149,7 +152,76 @@ } } }, - "ChatCompletionRequestAssistantMessageFunctionCall": { + "ChatCompletionMessageToolCallFunctionInput": { + "description": "The function that the model called.", + "fields": { + "arguments": { + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.", + "type": { + "name": "String", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "name": { + "description": "The name of the function to call.", + "type": { + "name": "String", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + } + } + }, + "ChatCompletionMessageToolCallInput": { + "fields": { + "function": { + "description": "The function that the model called.", + "type": { + "name": "ChatCompletionMessageToolCallFunctionInput", + "type": "named" + }, + "http": { + "type": [ + "object" + ] + } + }, + "id": { + "description": "The ID of the tool call.", + "type": { + "name": "String", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "type": { + "description": "The type of the tool. Currently, only `function` is supported.", + "type": { + "name": "ChatCompletionMessageToolCallType", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + } + } + }, + "ChatCompletionRequestAssistantMessageFunctionCallInput": { "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", "fields": { "arguments": { @@ -178,7 +250,7 @@ } } }, - "ChatCompletionRequestMessage": { + "ChatCompletionRequestMessageInput": { "fields": { "content": { "type": { @@ -194,7 +266,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "ChatCompletionRequestAssistantMessageFunctionCall", + "name": "ChatCompletionRequestAssistantMessageFunctionCallInput", "type": "named" } }, @@ -252,7 +324,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "ChatCompletionMessageToolCall", + "name": "ChatCompletionMessageToolCallInput", "type": "named" }, "type": "array" @@ -261,7 +333,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } } } @@ -326,7 +403,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + }, + "xml": { + "name": "ChatCompletionMessageToolCalls" + } } } } @@ -360,7 +445,7 @@ } } }, - "ChatCompletionStreamOptions": { + "ChatCompletionStreamOptionsInput": { "description": "Options for streaming response. Only set this when you set `stream: true`.", "fields": { "include_usage": { @@ -502,11 +587,11 @@ } } }, - "ChatCompletionTool": { + "ChatCompletionToolInput": { "fields": { "function": { "type": { - "name": "FunctionObject", + "name": "FunctionObjectInput", "type": "named" }, "http": { @@ -567,7 +652,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "ChatCompletionFunctions", + "name": "ChatCompletionFunctionsInput", "type": "named" }, "type": "array" @@ -576,7 +661,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "ChatCompletionFunctions" + } + } } }, "logit_bias": { @@ -628,7 +721,7 @@ "description": "A list of messages comprising the conversation so far. [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models).", "type": { "element_type": { - "name": "ChatCompletionRequestMessage", + "name": "ChatCompletionRequestMessageInput", "type": "named" }, "type": "array" @@ -636,7 +729,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "ChatCompletionRequestMessage" + } + } } }, "model": { @@ -680,7 +781,10 @@ "http": { "type": [ "boolean" - ] + ], + "xml": { + "name": "ParallelToolCalls" + } } }, "presence_penalty": { @@ -705,7 +809,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "CreateChatCompletionRequestResponseFormat", + "name": "CreateChatCompletionRequestResponseFormatInput", "type": "named" } }, @@ -780,14 +884,17 @@ "type": { "type": "nullable", "underlying_type": { - "name": "ChatCompletionStreamOptions", + "name": "ChatCompletionStreamOptionsInput", "type": "named" } }, "http": { "type": [ "object" - ] + ], + "xml": { + "name": "ChatCompletionStreamOptions" + } } }, "temperature": { @@ -817,7 +924,10 @@ } }, "http": { - "type": [] + "type": [], + "xml": { + "name": "ChatCompletionToolChoiceOption" + } } }, "tools": { @@ -826,7 +936,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "ChatCompletionTool", + "name": "ChatCompletionToolInput", "type": "named" }, "type": "array" @@ -835,7 +945,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "ChatCompletionTool" + } + } } }, "top_logprobs": { @@ -889,7 +1007,7 @@ } } }, - "CreateChatCompletionRequestResponseFormat": { + "CreateChatCompletionRequestResponseFormatInput": { "description": "An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ \"type\": \"json_object\" }` enables JSON mode, which guarantees the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.", "fields": { "type": { @@ -1062,7 +1180,10 @@ "http": { "type": [ "object" - ] + ], + "xml": { + "name": "ChatCompletionResponseMessage" + } } } } @@ -1085,7 +1206,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } } } @@ -1127,26 +1253,29 @@ "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequest", + "name": "CreateThreadRequestInput", "type": "named" } }, "http": { "type": [ "object" - ] + ], + "xml": { + "name": "CreateThreadRequest" + } } } } }, - "CreateThreadRequest": { + "CreateThreadRequestInput": { "fields": { "tool_resources": { "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.", "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResources", + "name": "CreateThreadRequestToolResourcesInput", "type": "named" } }, @@ -1158,40 +1287,7 @@ } } }, - "CreateThreadRequestToolResources": { - "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.", - "fields": { - "code_interpreter": { - "type": { - "type": "nullable", - "underlying_type": { - "name": "CreateThreadRequestToolResourcesCodeInterpreter", - "type": "named" - } - }, - "http": { - "type": [ - "object" - ] - } - }, - "file_search": { - "type": { - "type": "nullable", - "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearch", - "type": "named" - } - }, - "http": { - "type": [ - "object" - ] - } - } - } - }, - "CreateThreadRequestToolResourcesCodeInterpreter": { + "CreateThreadRequestToolResourcesCodeInterpreterInput": { "fields": { "file_ids": { "description": "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.", @@ -1218,7 +1314,7 @@ } } }, - "CreateThreadRequestToolResourcesFileSearch": { + "CreateThreadRequestToolResourcesFileSearchInput": { "fields": { "vector_store_ids": { "description": "The [vector store](/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.", @@ -1249,7 +1345,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStores", + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresInput", "type": "named" }, "type": "array" @@ -1268,14 +1364,78 @@ } } }, - "CreateThreadRequestToolResourcesFileSearchVectorStores": { + "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1StaticInput": { + "fields": { + "chunk_overlap_tokens": { + "description": "The number of tokens that overlap between chunks. The default value is `400`. Note that the overlap must not exceed half of `max_chunk_size_tokens`.", + "type": { + "name": "Int32", + "type": "named" + }, + "http": { + "type": [ + "integer" + ] + } + }, + "max_chunk_size_tokens": { + "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.", + "type": { + "name": "Int32", + "type": "named" + }, + "http": { + "type": [ + "integer" + ], + "maximum": 4096, + "minimum": 100 + } + } + } + }, + "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyInput": { + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "fields": { + "static": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1StaticInput", + "type": "named" + } + }, + "http": { + "type": [ + "object" + ] + } + }, + "type": { + "description": "Always `static`.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyTypeEnum", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + } + }, + "CreateThreadRequestToolResourcesFileSearchVectorStoresInput": { "fields": { "chunking_strategy": { "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy", + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyInput", "type": "named" } }, @@ -1325,14 +1485,14 @@ } } }, - "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy": { - "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "CreateThreadRequestToolResourcesInput": { + "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.", "fields": { - "static": { + "code_interpreter": { "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1Static", + "name": "CreateThreadRequestToolResourcesCodeInterpreterInput", "type": "named" } }, @@ -1342,54 +1502,23 @@ ] } }, - "type": { - "description": "Always `static`.", + "file_search": { "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyTypeEnum", + "name": "CreateThreadRequestToolResourcesFileSearchInput", "type": "named" } }, "http": { "type": [ - "string" - ] - } - } - } - }, - "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1Static": { - "fields": { - "chunk_overlap_tokens": { - "description": "The number of tokens that overlap between chunks. The default value is `400`. Note that the overlap must not exceed half of `max_chunk_size_tokens`.", - "type": { - "name": "Int32", - "type": "named" - }, - "http": { - "type": [ - "integer" + "object" ] } - }, - "max_chunk_size_tokens": { - "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.", - "type": { - "name": "Int32", - "type": "named" - }, - "http": { - "type": [ - "integer" - ], - "maximum": 4096, - "minimum": 100 - } } } }, - "FunctionObject": { + "FunctionObjectInput": { "fields": { "description": { "description": "A description of what the function does, used by the model to choose when and how to call the function.", @@ -1430,7 +1559,10 @@ "http": { "type": [ "object" - ] + ], + "xml": { + "name": "FunctionParameters" + } } } } diff --git a/ndc-http-schema/openapi/testdata/openai/schema.json b/ndc-http-schema/openapi/testdata/openai/schema.json index 5584068..0c03e52 100644 --- a/ndc-http-schema/openapi/testdata/openai/schema.json +++ b/ndc-http-schema/openapi/testdata/openai/schema.json @@ -2,7 +2,7 @@ "collections": [], "functions": [], "object_types": { - "ChatCompletionFunctions": { + "ChatCompletionFunctionsInput": { "fields": { "description": { "description": "A description of what the function does, used by the model to choose when and how to call the function.", @@ -77,7 +77,51 @@ } } }, - "ChatCompletionRequestAssistantMessageFunctionCall": { + "ChatCompletionMessageToolCallFunctionInput": { + "description": "The function that the model called.", + "fields": { + "arguments": { + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.", + "type": { + "name": "String", + "type": "named" + } + }, + "name": { + "description": "The name of the function to call.", + "type": { + "name": "String", + "type": "named" + } + } + } + }, + "ChatCompletionMessageToolCallInput": { + "fields": { + "function": { + "description": "The function that the model called.", + "type": { + "name": "ChatCompletionMessageToolCallFunctionInput", + "type": "named" + } + }, + "id": { + "description": "The ID of the tool call.", + "type": { + "name": "String", + "type": "named" + } + }, + "type": { + "description": "The type of the tool. Currently, only `function` is supported.", + "type": { + "name": "ChatCompletionMessageToolCallType", + "type": "named" + } + } + } + }, + "ChatCompletionRequestAssistantMessageFunctionCallInput": { "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", "fields": { "arguments": { @@ -96,7 +140,7 @@ } } }, - "ChatCompletionRequestMessage": { + "ChatCompletionRequestMessageInput": { "fields": { "content": { "type": { @@ -112,7 +156,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "ChatCompletionRequestAssistantMessageFunctionCall", + "name": "ChatCompletionRequestAssistantMessageFunctionCallInput", "type": "named" } } @@ -150,7 +194,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "ChatCompletionMessageToolCall", + "name": "ChatCompletionMessageToolCallInput", "type": "named" }, "type": "array" @@ -223,7 +267,7 @@ } } }, - "ChatCompletionStreamOptions": { + "ChatCompletionStreamOptionsInput": { "description": "Options for streaming response. Only set this when you set `stream: true`.", "fields": { "include_usage": { @@ -310,11 +354,11 @@ } } }, - "ChatCompletionTool": { + "ChatCompletionToolInput": { "fields": { "function": { "type": { - "name": "FunctionObject", + "name": "FunctionObjectInput", "type": "named" } }, @@ -355,7 +399,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "ChatCompletionFunctions", + "name": "ChatCompletionFunctionsInput", "type": "named" }, "type": "array" @@ -396,7 +440,7 @@ "description": "A list of messages comprising the conversation so far. [Example Python code](https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models).", "type": { "element_type": { - "name": "ChatCompletionRequestMessage", + "name": "ChatCompletionRequestMessageInput", "type": "named" }, "type": "array" @@ -444,7 +488,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "CreateChatCompletionRequestResponseFormat", + "name": "CreateChatCompletionRequestResponseFormatInput", "type": "named" } } @@ -494,7 +538,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "ChatCompletionStreamOptions", + "name": "ChatCompletionStreamOptionsInput", "type": "named" } } @@ -525,7 +569,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "ChatCompletionTool", + "name": "ChatCompletionToolInput", "type": "named" }, "type": "array" @@ -564,7 +608,7 @@ } } }, - "CreateChatCompletionRequestResponseFormat": { + "CreateChatCompletionRequestResponseFormatInput": { "description": "An object specifying the format that the model must output. Compatible with [GPT-4 Turbo](/docs/models/gpt-4-and-gpt-4-turbo) and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting to `{ \"type\": \"json_object\" }` enables JSON mode, which guarantees the message the model generates is valid JSON. **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.", "fields": { "type": { @@ -722,51 +766,28 @@ "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequest", + "name": "CreateThreadRequestInput", "type": "named" } } } } }, - "CreateThreadRequest": { + "CreateThreadRequestInput": { "fields": { "tool_resources": { "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.", "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResources", + "name": "CreateThreadRequestToolResourcesInput", "type": "named" } } } } }, - "CreateThreadRequestToolResources": { - "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.", - "fields": { - "code_interpreter": { - "type": { - "type": "nullable", - "underlying_type": { - "name": "CreateThreadRequestToolResourcesCodeInterpreter", - "type": "named" - } - } - }, - "file_search": { - "type": { - "type": "nullable", - "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearch", - "type": "named" - } - } - } - } - }, - "CreateThreadRequestToolResourcesCodeInterpreter": { + "CreateThreadRequestToolResourcesCodeInterpreterInput": { "fields": { "file_ids": { "description": "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.", @@ -783,7 +804,7 @@ } } }, - "CreateThreadRequestToolResourcesFileSearch": { + "CreateThreadRequestToolResourcesFileSearchInput": { "fields": { "vector_store_ids": { "description": "The [vector store](/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.", @@ -804,7 +825,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStores", + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresInput", "type": "named" }, "type": "array" @@ -813,14 +834,56 @@ } } }, - "CreateThreadRequestToolResourcesFileSearchVectorStores": { + "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1StaticInput": { + "fields": { + "chunk_overlap_tokens": { + "description": "The number of tokens that overlap between chunks. The default value is `400`. Note that the overlap must not exceed half of `max_chunk_size_tokens`.", + "type": { + "name": "Int32", + "type": "named" + } + }, + "max_chunk_size_tokens": { + "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.", + "type": { + "name": "Int32", + "type": "named" + } + } + } + }, + "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyInput": { + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "fields": { + "static": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1StaticInput", + "type": "named" + } + } + }, + "type": { + "description": "Always `static`.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyTypeEnum", + "type": "named" + } + } + } + } + }, + "CreateThreadRequestToolResourcesFileSearchVectorStoresInput": { "fields": { "chunking_strategy": { "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy", + "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyInput", "type": "named" } } @@ -850,49 +913,30 @@ } } }, - "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy": { - "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "CreateThreadRequestToolResourcesInput": { + "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.", "fields": { - "static": { + "code_interpreter": { "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1Static", + "name": "CreateThreadRequestToolResourcesCodeInterpreterInput", "type": "named" } } }, - "type": { - "description": "Always `static`.", + "file_search": { "type": { "type": "nullable", "underlying_type": { - "name": "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategyTypeEnum", + "name": "CreateThreadRequestToolResourcesFileSearchInput", "type": "named" } } } } }, - "CreateThreadRequestToolResourcesFileSearchVectorStoresChunkingStrategy1Static": { - "fields": { - "chunk_overlap_tokens": { - "description": "The number of tokens that overlap between chunks. The default value is `400`. Note that the overlap must not exceed half of `max_chunk_size_tokens`.", - "type": { - "name": "Int32", - "type": "named" - } - }, - "max_chunk_size_tokens": { - "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`.", - "type": { - "name": "Int32", - "type": "named" - } - } - } - }, - "FunctionObject": { + "FunctionObjectInput": { "fields": { "description": { "description": "A description of what the function does, used by the model to choose when and how to call the function.", diff --git a/ndc-http-schema/openapi/testdata/petstore3/expected.json b/ndc-http-schema/openapi/testdata/petstore3/expected.json index 02b81f0..ac1b775 100644 --- a/ndc-http-schema/openapi/testdata/petstore3/expected.json +++ b/ndc-http-schema/openapi/testdata/petstore3/expected.json @@ -1139,6 +1139,42 @@ "name": "category" } }, + "CategoryInput": { + "fields": { + "id": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "Int64", + "type": "named" + } + }, + "http": { + "type": [ + "integer" + ], + "format": "int64" + } + }, + "name": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + }, + "xml": { + "name": "category" + } + }, "CreateFineTuningJobRequest": { "fields": { "model": { @@ -1970,6 +2006,14 @@ "type": [ "array" ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "tag" + } + }, "xml": { "wrapped": true } @@ -1986,7 +2030,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "Category", + "name": "CategoryInput", "type": "named" } }, @@ -2083,7 +2127,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "Tag", + "name": "TagInput", "type": "named" }, "type": "array" @@ -2093,6 +2137,14 @@ "type": [ "array" ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "tag" + } + }, "xml": { "wrapped": true } @@ -4398,7 +4450,10 @@ "http": { "type": [ "object" - ] + ], + "xml": { + "name": "payment_method_affirm" + } } }, "currency": { @@ -7399,6 +7454,42 @@ "name": "tag" } }, + "TagInput": { + "fields": { + "id": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "Int64", + "type": "named" + } + }, + "http": { + "type": [ + "integer" + ], + "format": "int64" + } + }, + "name": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + }, + "xml": { + "name": "tag" + } + }, "TreasuryInboundTransfer": { "description": "Use [InboundTransfers](https://stripe.com/docs/treasury/moving-money/financial-accounts/into/inbound-transfers) to add funds to your [FinancialAccount](https://stripe.com/docs/api#financial_accounts) via a PaymentMethod that is owned by you. The funds will be transferred via an ACH debit.", "fields": { @@ -7719,7 +7810,7 @@ } } }, - "UploadPetMultipartBodyInputInput": { + "UploadPetMultipartBodyInput": { "fields": { "address": { "type": { @@ -7749,7 +7840,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "address" + } + } } }, "children": { @@ -8056,6 +8155,9 @@ "request": { "url": "/v1/accounts/{account}", "method": "delete", + "requestBody": { + "contentType": "application/x-www-form-urlencoded" + }, "response": { "contentType": "application/json" } @@ -8077,6 +8179,19 @@ "maxLength": 5000 } } + }, + "body": { + "description": "Request body of DELETE /v1/accounts/{account}", + "type": { + "type": "nullable", + "underlying_type": { + "name": "JSON", + "type": "named" + } + }, + "http": { + "in": "body" + } } }, "description": "DELETE /v1/accounts/{account}", @@ -8990,7 +9105,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "UploadPetMultipartBodyInputInput", + "name": "UploadPetMultipartBodyInput", "type": "named" } }, diff --git a/ndc-http-schema/openapi/testdata/petstore3/schema.json b/ndc-http-schema/openapi/testdata/petstore3/schema.json index 9705ac9..0425000 100644 --- a/ndc-http-schema/openapi/testdata/petstore3/schema.json +++ b/ndc-http-schema/openapi/testdata/petstore3/schema.json @@ -577,6 +577,28 @@ } } }, + "CategoryInput": { + "fields": { + "id": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "Int64", + "type": "named" + } + } + }, + "name": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } + }, "CreateFineTuningJobRequest": { "fields": { "model": { @@ -1075,7 +1097,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "Category", + "name": "CategoryInput", "type": "named" } } @@ -1129,7 +1151,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "Tag", + "name": "TagInput", "type": "named" }, "type": "array" @@ -4492,6 +4514,28 @@ } } }, + "TagInput": { + "fields": { + "id": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "Int64", + "type": "named" + } + } + }, + "name": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } + }, "TreasuryInboundTransfer": { "description": "Use [InboundTransfers](https://stripe.com/docs/treasury/moving-money/financial-accounts/into/inbound-transfers) to add funds to your [FinancialAccount](https://stripe.com/docs/api#financial_accounts) via a PaymentMethod that is owned by you. The funds will be transferred via an ACH debit.", "fields": { @@ -4691,7 +4735,7 @@ } } }, - "UploadPetMultipartBodyInputInput": { + "UploadPetMultipartBodyInput": { "fields": { "address": { "type": { @@ -4909,6 +4953,16 @@ "name": "String", "type": "named" } + }, + "body": { + "description": "Request body of DELETE /v1/accounts/{account}", + "type": { + "type": "nullable", + "underlying_type": { + "name": "JSON", + "type": "named" + } + } } }, "description": "DELETE /v1/accounts/{account}", @@ -5317,7 +5371,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "UploadPetMultipartBodyInputInput", + "name": "UploadPetMultipartBodyInput", "type": "named" } } diff --git a/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.json b/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.json index 8617f8a..3a5888a 100644 --- a/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.json +++ b/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.json @@ -137,7 +137,10 @@ } }, "http": { - "type": [] + "type": [], + "xml": { + "name": "Notification200Errors" + } } }, "external_id": { @@ -321,13 +324,71 @@ } } }, + "HasuraOneSignalFilterInput": { + "fields": { + "field": { + "description": "Name of the field to use as the first operand in the filter expression.", + "type": { + "name": "String", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "key": { + "description": "If `field` is `tag`, this field is *required* to specify `key` inside the tags.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + }, + "relation": { + "description": "Operator of a filter expression.", + "type": { + "name": "HasuraOneSignalFilterRelation", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "value": { + "description": "Constant value to use as the second operand in the filter expression. This value is *required* when the relation operator is a binary operator.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + } + }, "HasuraOneSignalNotificationInput": { "fields": { "contents": { "type": { "type": "nullable", "underlying_type": { - "name": "HasuraOneSignalStringMap", + "name": "HasuraOneSignalStringMapInput", "type": "named" } }, @@ -393,7 +454,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "HasuraOneSignalFilter", + "name": "HasuraOneSignalFilterInput", "type": "named" }, "type": "array" @@ -402,14 +463,19 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "headings": { "type": { "type": "nullable", "underlying_type": { - "name": "HasuraOneSignalStringMap", + "name": "HasuraOneSignalStringMapInput", "type": "named" } }, @@ -496,7 +562,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "HasuraOneSignalStringMap", + "name": "HasuraOneSignalStringMapInput", "type": "named" } }, @@ -552,7 +618,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "NotificationWithMeta" + } + } } }, "offset": { @@ -698,7 +772,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "headings": { @@ -787,7 +866,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "platform_delivery_stats": { @@ -1099,6 +1183,25 @@ } } } + }, + "HasuraOneSignalStringMapInput": { + "fields": { + "en": { + "description": "Text in English. Will be used as a fallback", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + } } }, "procedures": { diff --git a/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.schema.json b/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.schema.json index 065e0c8..8dd9857 100644 --- a/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.schema.json +++ b/ndc-http-schema/openapi/testdata/prefix3/expected_multi_words.schema.json @@ -182,13 +182,51 @@ } } }, + "HasuraOneSignalFilterInput": { + "fields": { + "field": { + "description": "Name of the field to use as the first operand in the filter expression.", + "type": { + "name": "String", + "type": "named" + } + }, + "key": { + "description": "If `field` is `tag`, this field is *required* to specify `key` inside the tags.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + }, + "relation": { + "description": "Operator of a filter expression.", + "type": { + "name": "HasuraOneSignalFilterRelation", + "type": "named" + } + }, + "value": { + "description": "Constant value to use as the second operand in the filter expression. This value is *required* when the relation operator is a binary operator.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } + }, "HasuraOneSignalNotificationInput": { "fields": { "contents": { "type": { "type": "nullable", "underlying_type": { - "name": "HasuraOneSignalStringMap", + "name": "HasuraOneSignalStringMapInput", "type": "named" } } @@ -229,7 +267,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "HasuraOneSignalFilter", + "name": "HasuraOneSignalFilterInput", "type": "named" }, "type": "array" @@ -240,7 +278,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "HasuraOneSignalStringMap", + "name": "HasuraOneSignalStringMapInput", "type": "named" } } @@ -291,7 +329,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "HasuraOneSignalStringMap", + "name": "HasuraOneSignalStringMapInput", "type": "named" } } @@ -681,6 +719,20 @@ } } } + }, + "HasuraOneSignalStringMapInput": { + "fields": { + "en": { + "description": "Text in English. Will be used as a fallback", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } } }, "procedures": [ diff --git a/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.json b/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.json index 0411be1..d1660e4 100644 --- a/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.json +++ b/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.json @@ -137,7 +137,10 @@ } }, "http": { - "type": [] + "type": [], + "xml": { + "name": "Notification200Errors" + } } }, "external_id": { @@ -321,13 +324,71 @@ } } }, + "HasuraFilterInput": { + "fields": { + "field": { + "description": "Name of the field to use as the first operand in the filter expression.", + "type": { + "name": "String", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "key": { + "description": "If `field` is `tag`, this field is *required* to specify `key` inside the tags.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + }, + "relation": { + "description": "Operator of a filter expression.", + "type": { + "name": "HasuraFilterRelation", + "type": "named" + }, + "http": { + "type": [ + "string" + ] + } + }, + "value": { + "description": "Constant value to use as the second operand in the filter expression. This value is *required* when the relation operator is a binary operator.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + } + }, "HasuraNotificationInput": { "fields": { "contents": { "type": { "type": "nullable", "underlying_type": { - "name": "HasuraStringMap", + "name": "HasuraStringMapInput", "type": "named" } }, @@ -393,7 +454,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "HasuraFilter", + "name": "HasuraFilterInput", "type": "named" }, "type": "array" @@ -402,14 +463,19 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "headings": { "type": { "type": "nullable", "underlying_type": { - "name": "HasuraStringMap", + "name": "HasuraStringMapInput", "type": "named" } }, @@ -496,7 +562,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "HasuraStringMap", + "name": "HasuraStringMapInput", "type": "named" } }, @@ -552,7 +618,15 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ], + "xml": { + "name": "NotificationWithMeta" + } + } } }, "offset": { @@ -698,7 +772,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "headings": { @@ -787,7 +866,12 @@ "http": { "type": [ "array" - ] + ], + "items": { + "type": [ + "object" + ] + } } }, "platform_delivery_stats": { @@ -1099,6 +1183,25 @@ } } } + }, + "HasuraStringMapInput": { + "fields": { + "en": { + "description": "Text in English. Will be used as a fallback", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "http": { + "type": [ + "string" + ] + } + } + } } }, "procedures": { diff --git a/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.schema.json b/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.schema.json index 0824d15..dd258ee 100644 --- a/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.schema.json +++ b/ndc-http-schema/openapi/testdata/prefix3/expected_single_word.schema.json @@ -182,13 +182,51 @@ } } }, + "HasuraFilterInput": { + "fields": { + "field": { + "description": "Name of the field to use as the first operand in the filter expression.", + "type": { + "name": "String", + "type": "named" + } + }, + "key": { + "description": "If `field` is `tag`, this field is *required* to specify `key` inside the tags.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + }, + "relation": { + "description": "Operator of a filter expression.", + "type": { + "name": "HasuraFilterRelation", + "type": "named" + } + }, + "value": { + "description": "Constant value to use as the second operand in the filter expression. This value is *required* when the relation operator is a binary operator.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } + }, "HasuraNotificationInput": { "fields": { "contents": { "type": { "type": "nullable", "underlying_type": { - "name": "HasuraStringMap", + "name": "HasuraStringMapInput", "type": "named" } } @@ -229,7 +267,7 @@ "type": "nullable", "underlying_type": { "element_type": { - "name": "HasuraFilter", + "name": "HasuraFilterInput", "type": "named" }, "type": "array" @@ -240,7 +278,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "HasuraStringMap", + "name": "HasuraStringMapInput", "type": "named" } } @@ -291,7 +329,7 @@ "type": { "type": "nullable", "underlying_type": { - "name": "HasuraStringMap", + "name": "HasuraStringMapInput", "type": "named" } } @@ -681,6 +719,20 @@ } } } + }, + "HasuraStringMapInput": { + "fields": { + "en": { + "description": "Text in English. Will be used as a fallback", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + } + } + } } }, "procedures": [