diff --git a/go.mod b/go.mod index 255ec1e..ce66416 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,9 @@ go 1.23.0 toolchain go1.23.1 require ( + github.com/google/uuid v1.6.0 github.com/hasura/ndc-rest/ndc-rest-schema v0.2.5 - github.com/hasura/ndc-sdk-go v1.5.2-0.20241013153211-ca0e28914f0c + github.com/hasura/ndc-sdk-go v1.5.2-0.20241013164146-0bf9f9a9ab8c github.com/lmittmann/tint v1.0.5 go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel/trace v1.31.0 @@ -27,7 +28,6 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/invopop/jsonschema v0.12.0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/go.sum b/go.sum index 8b05e1d..d384c13 100644 --- a/go.sum +++ b/go.sum @@ -56,8 +56,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/hasura/ndc-sdk-go v1.5.2-0.20241013153211-ca0e28914f0c h1:C+2uejHoZbaU7AuVGP3xCaT+l0HPA6Mgvcu1E5eqbLA= -github.com/hasura/ndc-sdk-go v1.5.2-0.20241013153211-ca0e28914f0c/go.mod h1:oik0JrwuN5iZwZjZJzIRMw9uO2xDJbCXwhS1GgaRejk= +github.com/hasura/ndc-sdk-go v1.5.2-0.20241013164146-0bf9f9a9ab8c h1:t6Cff9qFRMecdgzs4/V26A3rofaIQK/Hc2OZ9O66HcM= +github.com/hasura/ndc-sdk-go v1.5.2-0.20241013164146-0bf9f9a9ab8c/go.mod h1:oik0JrwuN5iZwZjZJzIRMw9uO2xDJbCXwhS1GgaRejk= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= diff --git a/ndc-rest-schema/go.mod b/ndc-rest-schema/go.mod index ec2cff6..d7c1f71 100644 --- a/ndc-rest-schema/go.mod +++ b/ndc-rest-schema/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.1 require ( github.com/alecthomas/kong v1.2.1 github.com/evanphx/json-patch v0.5.2 - github.com/hasura/ndc-sdk-go v1.5.2-0.20241013153211-ca0e28914f0c + github.com/hasura/ndc-sdk-go v1.5.2-0.20241013164146-0bf9f9a9ab8c github.com/invopop/jsonschema v0.12.0 github.com/lmittmann/tint v1.0.5 github.com/pb33f/libopenapi v0.18.3 diff --git a/ndc-rest-schema/go.sum b/ndc-rest-schema/go.sum index e60d988..2ccd96f 100644 --- a/ndc-rest-schema/go.sum +++ b/ndc-rest-schema/go.sum @@ -39,8 +39,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/hasura/ndc-sdk-go v1.5.2-0.20241013153211-ca0e28914f0c h1:C+2uejHoZbaU7AuVGP3xCaT+l0HPA6Mgvcu1E5eqbLA= -github.com/hasura/ndc-sdk-go v1.5.2-0.20241013153211-ca0e28914f0c/go.mod h1:oik0JrwuN5iZwZjZJzIRMw9uO2xDJbCXwhS1GgaRejk= +github.com/hasura/ndc-sdk-go v1.5.2-0.20241013164146-0bf9f9a9ab8c h1:t6Cff9qFRMecdgzs4/V26A3rofaIQK/Hc2OZ9O66HcM= +github.com/hasura/ndc-sdk-go v1.5.2-0.20241013164146-0bf9f9a9ab8c/go.mod h1:oik0JrwuN5iZwZjZJzIRMw9uO2xDJbCXwhS1GgaRejk= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= diff --git a/ndc-rest-schema/jsonschema/convert-config.schema.json b/ndc-rest-schema/jsonschema/convert-config.schema.json new file mode 100644 index 0000000..9f93c94 --- /dev/null +++ b/ndc-rest-schema/jsonschema/convert-config.schema.json @@ -0,0 +1,107 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/hasura/ndc-rest/ndc-rest-schema/command/convert-config", + "$ref": "#/$defs/ConvertConfig", + "$defs": { + "ConvertConfig": { + "properties": { + "file": { + "type": "string", + "description": "File path needs to be converted" + }, + "spec": { + "$ref": "#/$defs/SchemaSpecType", + "description": "The API specification of the file, is one of oas3 (openapi3), oas2 (openapi2)" + }, + "methodAlias": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "Alias names for HTTP method. Used for prefix renaming, e.g. getUsers, postUser" + }, + "prefix": { + "type": "string", + "description": "Add a prefix to the function and procedure names" + }, + "trimPrefix": { + "type": "string", + "description": "Trim the prefix in URL, e.g. /v1" + }, + "envPrefix": { + "type": "string", + "description": "The environment variable prefix for security values, e.g. PET_STORE" + }, + "pure": { + "type": "boolean", + "description": "Return the pure NDC schema only" + }, + "strict": { + "type": "boolean", + "description": "Require strict validation" + }, + "patchBefore": { + "items": { + "$ref": "#/$defs/PatchConfig" + }, + "type": "array", + "description": "Patch files to be applied into the input file before converting" + }, + "patchAfter": { + "items": { + "$ref": "#/$defs/PatchConfig" + }, + "type": "array", + "description": "Patch files to be applied into the input file after converting" + }, + "allowedContentTypes": { + "items": { + "type": "string" + }, + "type": "array", + "description": "Allowed content types. All content types are allowed by default" + }, + "output": { + "type": "string", + "description": "The location where the ndc schema file will be generated. Print to stdout if not set" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "file" + ], + "description": "ConvertConfig represents the content of convert config file" + }, + "PatchConfig": { + "properties": { + "path": { + "type": "string" + }, + "strategy": { + "type": "string", + "enum": [ + "merge", + "json6902" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "path", + "strategy" + ] + }, + "SchemaSpecType": { + "type": "string", + "enum": [ + "oas3", + "oas2", + "openapi3", + "openapi2", + "ndc" + ] + } + } +} \ No newline at end of file diff --git a/ndc-rest-schema/jsonschema/ndc-rest-schema.schema.json b/ndc-rest-schema/jsonschema/ndc-rest-schema.schema.json new file mode 100644 index 0000000..ee41355 --- /dev/null +++ b/ndc-rest-schema/jsonschema/ndc-rest-schema.schema.json @@ -0,0 +1,779 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/hasura/ndc-rest/ndc-rest-schema/schema/ndc-rest-schema", + "$ref": "#/$defs/NDCRestSchema", + "$defs": { + "AggregateFunctionDefinition": { + "properties": { + "result_type": { + "$ref": "#/$defs/Type" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result_type" + ] + }, + "ArgumentInfo": { + "properties": { + "description": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/Type" + }, + "rest": { + "$ref": "#/$defs/RequestParameter", + "description": "The request parameter information of the REST request" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "type" + ], + "description": "ArgumentInfo the information of REST request argument" + }, + "AuthSecurities": { + "items": { + "$ref": "#/$defs/AuthSecurity" + }, + "type": "array", + "description": "AuthSecurities wraps list of security requirements with helpers" + }, + "AuthSecurity": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object", + "description": "AuthSecurity wraps the raw security requirement with helpers" + }, + "ComparisonOperatorDefinition": { + "type": "object" + }, + "EncodingObject": { + "properties": { + "style": { + "$ref": "#/$defs/ParameterEncodingStyle", + "description": "Describes how a specific property value will be serialized depending on its type.\nSee Parameter Object for details on the style property.\nThe behavior follows the same values as query parameters, including default values.\nThis property SHALL be ignored if the request body media type is not application/x-www-form-urlencoded or multipart/form-data.\nIf a value is explicitly defined, then the value of contentType (implicit or explicit) SHALL be ignored" + }, + "explode": { + "type": "boolean", + "description": "When this is true, property values of type array or object generate separate parameters for each value of the array, or key-value-pair of the map.\nFor other types of properties this property has no effect. When style is form, the default value is true. For all other styles, the default value is false.\nThis property SHALL be ignored if the request body media type is not application/x-www-form-urlencoded or multipart/form-data.\nIf a value is explicitly defined, then the value of contentType (implicit or explicit) SHALL be ignored" + }, + "allowReserved": { + "type": "boolean", + "description": "By default, reserved characters :/?#[]@!$\u0026'()*+,;= in form field values within application/x-www-form-urlencoded bodies are percent-encoded when sent.\nAllowReserved allows these characters to be sent as is:" + }, + "contentType": { + "items": { + "type": "string" + }, + "type": "array", + "description": "For more complex scenarios, such as nested arrays or JSON in form data, use the contentType keyword to specify the media type for encoding the value of a complex field." + }, + "headers": { + "additionalProperties": { + "$ref": "#/$defs/RequestParameter" + }, + "type": "object", + "description": "A map allowing additional information to be provided as headers, for example Content-Disposition.\nContent-Type is described separately and SHALL be ignored in this section.\nThis property SHALL be ignored if the request body media type is not a multipart." + } + }, + "additionalProperties": false, + "type": "object", + "description": "EncodingObject represents the Encoding Object that contains serialization strategy for application/x-www-form-urlencoded\n\n[Encoding Object]: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#encoding-object" + }, + "EnvBoolean": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "EnvInt": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "EnvInts": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "integer" + }, + "type": "array" + } + ] + }, + "EnvString": { + "type": "string" + }, + "EnvStrings": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ] + }, + "NDCRestSchema": { + "properties": { + "$schema": { + "type": "string" + }, + "settings": { + "$ref": "#/$defs/NDCRestSettings" + }, + "functions": { + "items": { + "$ref": "#/$defs/OperationInfo" + }, + "type": "array", + "description": "Functions (i.e. collections which return a single column and row)" + }, + "object_types": { + "additionalProperties": { + "$ref": "#/$defs/ObjectType" + }, + "type": "object", + "description": "A list of object types which can be used as the types of arguments, or return\ntypes of procedures. Names should not overlap with scalar type names." + }, + "procedures": { + "items": { + "$ref": "#/$defs/OperationInfo" + }, + "type": "array", + "description": "Procedures which are available for execution as part of mutations" + }, + "scalar_types": { + "$ref": "#/$defs/SchemaResponseScalarTypes", + "description": "A list of scalar types which will be used as the types of collection columns" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "functions", + "object_types", + "procedures", + "scalar_types" + ], + "description": "NDCRestSchema extends the [NDC SchemaResponse] with OpenAPI REST information" + }, + "NDCRestSettings": { + "properties": { + "servers": { + "items": { + "$ref": "#/$defs/ServerConfig" + }, + "type": "array" + }, + "headers": { + "additionalProperties": { + "$ref": "#/$defs/EnvString" + }, + "type": "object" + }, + "timeout": { + "$ref": "#/$defs/EnvInt", + "description": "configure the request timeout in seconds, default 30s" + }, + "retry": { + "$ref": "#/$defs/RetryPolicySetting" + }, + "securitySchemes": { + "additionalProperties": { + "$ref": "#/$defs/SecurityScheme" + }, + "type": "object" + }, + "security": { + "$ref": "#/$defs/AuthSecurities" + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "servers" + ], + "description": "NDCRestSettings represent global settings of the REST API, including base URL, headers, etc..." + }, + "ObjectField": { + "properties": { + "arguments": { + "$ref": "#/$defs/ObjectFieldArguments" + }, + "description": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/Type" + }, + "rest": { + "$ref": "#/$defs/TypeSchema", + "description": "The field schema information of the REST request" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "type" + ], + "description": "ObjectField defined on this object type" + }, + "ObjectFieldArguments": { + "additionalProperties": { + "$ref": "#/$defs/ArgumentInfo" + }, + "type": "object" + }, + "ObjectType": { + "properties": { + "description": { + "type": "string", + "description": "Description of this type" + }, + "fields": { + "additionalProperties": { + "$ref": "#/$defs/ObjectField" + }, + "type": "object", + "description": "Fields defined on this object type" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "fields" + ], + "description": "ObjectType represents the object type of rest schema" + }, + "OperationInfo": { + "properties": { + "request": { + "$ref": "#/$defs/Request" + }, + "arguments": { + "additionalProperties": { + "$ref": "#/$defs/ArgumentInfo" + }, + "type": "object", + "description": "Any arguments that this collection requires" + }, + "description": { + "type": "string", + "description": "Column description" + }, + "name": { + "type": "string", + "description": "The name of the procedure" + }, + "result_type": { + "$ref": "#/$defs/Type", + "description": "The name of the result type" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "request", + "arguments", + "name", + "result_type" + ], + "description": "OperationInfo extends connector command operation with OpenAPI REST information" + }, + "ParameterEncodingStyle": { + "type": "string", + "enum": [ + "simple", + "label", + "matrix", + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "ParameterLocation": { + "type": "string", + "enum": [ + "query", + "header", + "path", + "cookie", + "body", + "formData" + ] + }, + "Request": { + "properties": { + "url": { + "type": "string" + }, + "method": { + "type": "string", + "enum": [ + "get", + "post", + "put", + "patch", + "delete" + ] + }, + "type": { + "type": "string" + }, + "headers": { + "additionalProperties": { + "$ref": "#/$defs/EnvString" + }, + "type": "object" + }, + "security": { + "$ref": "#/$defs/AuthSecurities" + }, + "timeout": { + "type": "integer", + "description": "configure the request timeout in seconds, default 30s" + }, + "servers": { + "items": { + "$ref": "#/$defs/ServerConfig" + }, + "type": "array" + }, + "requestBody": { + "$ref": "#/$defs/RequestBody" + }, + "response": { + "$ref": "#/$defs/Response" + }, + "retry": { + "$ref": "#/$defs/RetryPolicy" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "response" + ], + "description": "Request represents the HTTP request information of the webhook" + }, + "RequestBody": { + "properties": { + "contentType": { + "type": "string" + }, + "encoding": { + "additionalProperties": { + "$ref": "#/$defs/EncodingObject" + }, + "type": "object" + } + }, + "additionalProperties": false, + "type": "object", + "description": "RequestBody defines flexible request body with content types" + }, + "RequestParameter": { + "properties": { + "style": { + "$ref": "#/$defs/ParameterEncodingStyle", + "description": "Describes how a specific property value will be serialized depending on its type.\nSee Parameter Object for details on the style property.\nThe behavior follows the same values as query parameters, including default values.\nThis property SHALL be ignored if the request body media type is not application/x-www-form-urlencoded or multipart/form-data.\nIf a value is explicitly defined, then the value of contentType (implicit or explicit) SHALL be ignored" + }, + "explode": { + "type": "boolean", + "description": "When this is true, property values of type array or object generate separate parameters for each value of the array, or key-value-pair of the map.\nFor other types of properties this property has no effect. When style is form, the default value is true. For all other styles, the default value is false.\nThis property SHALL be ignored if the request body media type is not application/x-www-form-urlencoded or multipart/form-data.\nIf a value is explicitly defined, then the value of contentType (implicit or explicit) SHALL be ignored" + }, + "allowReserved": { + "type": "boolean", + "description": "By default, reserved characters :/?#[]@!$\u0026'()*+,;= in form field values within application/x-www-form-urlencoded bodies are percent-encoded when sent.\nAllowReserved allows these characters to be sent as is:" + }, + "contentType": { + "items": { + "type": "string" + }, + "type": "array", + "description": "For more complex scenarios, such as nested arrays or JSON in form data, use the contentType keyword to specify the media type for encoding the value of a complex field." + }, + "headers": { + "additionalProperties": { + "$ref": "#/$defs/RequestParameter" + }, + "type": "object", + "description": "A map allowing additional information to be provided as headers, for example Content-Disposition.\nContent-Type is described separately and SHALL be ignored in this section.\nThis property SHALL be ignored if the request body media type is not a multipart." + }, + "name": { + "type": "string" + }, + "argumentName": { + "type": "string" + }, + "in": { + "$ref": "#/$defs/ParameterLocation" + }, + "schema": { + "$ref": "#/$defs/TypeSchema" + } + }, + "additionalProperties": false, + "type": "object", + "description": "RequestParameter represents an HTTP request parameter" + }, + "Response": { + "properties": { + "contentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "contentType" + ] + }, + "RetryPolicy": { + "properties": { + "times": { + "type": "integer", + "description": "Number of retry times" + }, + "delay": { + "type": "integer", + "description": "Delay retry delay in milliseconds" + }, + "httpStatus": { + "items": { + "type": "integer" + }, + "type": "array", + "description": "HTTPStatus retries if the remote service returns one of these http status" + } + }, + "additionalProperties": false, + "type": "object", + "description": "RetryPolicy represents the retry policy of request" + }, + "RetryPolicySetting": { + "properties": { + "times": { + "$ref": "#/$defs/EnvInt", + "description": "Number of retry times" + }, + "delay": { + "$ref": "#/$defs/EnvInt", + "description": "Delay retry delay in milliseconds" + }, + "httpStatus": { + "$ref": "#/$defs/EnvInts", + "description": "HTTPStatus retries if the remote service returns one of these http status" + } + }, + "additionalProperties": false, + "type": "object", + "description": "RetryPolicySetting represents retry policy settings" + }, + "ScalarType": { + "properties": { + "aggregate_functions": { + "$ref": "#/$defs/ScalarTypeAggregateFunctions" + }, + "comparison_operators": { + "additionalProperties": { + "$ref": "#/$defs/ComparisonOperatorDefinition" + }, + "type": "object" + }, + "representation": { + "$ref": "#/$defs/TypeRepresentation" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "aggregate_functions", + "comparison_operators" + ] + }, + "ScalarTypeAggregateFunctions": { + "additionalProperties": { + "$ref": "#/$defs/AggregateFunctionDefinition" + }, + "type": "object" + }, + "SchemaResponseScalarTypes": { + "additionalProperties": { + "$ref": "#/$defs/ScalarType" + }, + "type": "object" + }, + "SecurityScheme": { + "oneOf": [ + { + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ] + }, + "value": { + "type": "string" + }, + "in": { + "type": "string", + "enum": [ + "header", + "query", + "cookie" + ] + }, + "name": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "value", + "in", + "name" + ] + }, + { + "properties": { + "type": { + "type": "string", + "enum": [ + "http" + ] + }, + "value": { + "type": "string" + }, + "header": { + "type": "string" + }, + "scheme": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "value", + "header", + "scheme" + ] + }, + { + "properties": { + "type": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "flows": { + "additionalProperties": true, + "type": "object" + } + }, + "type": "object", + "required": [ + "type", + "flows" + ] + }, + { + "properties": { + "type": { + "type": "string", + "enum": [ + "openIdConnect" + ] + }, + "openIdConnectUrl": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "openIdConnectUrl" + ] + } + ] + }, + "ServerConfig": { + "properties": { + "url": { + "$ref": "#/$defs/EnvString" + }, + "id": { + "type": "string" + }, + "headers": { + "additionalProperties": { + "$ref": "#/$defs/EnvString" + }, + "type": "object" + }, + "timeout": { + "$ref": "#/$defs/EnvInt", + "description": "configure the request timeout in seconds, default 30s" + }, + "retry": { + "$ref": "#/$defs/RetryPolicySetting" + }, + "securitySchemes": { + "additionalProperties": { + "$ref": "#/$defs/SecurityScheme" + }, + "type": "object" + }, + "security": { + "$ref": "#/$defs/AuthSecurities" + }, + "tls": { + "$ref": "#/$defs/TLSConfig" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "url" + ], + "description": "ServerConfig contains server configurations" + }, + "TLSConfig": { + "properties": { + "certFile": { + "$ref": "#/$defs/EnvString", + "description": "Path to the TLS cert to use for TLS required connections." + }, + "certPem": { + "$ref": "#/$defs/EnvString", + "description": "Alternative to cert_file. Provide the certificate contents as a string instead of a filepath." + }, + "keyFile": { + "$ref": "#/$defs/EnvString", + "description": "Path to the TLS key to use for TLS required connections." + }, + "keyPem": { + "$ref": "#/$defs/EnvString", + "description": "Alternative to key_file. Provide the key contents as a string instead of a filepath." + }, + "caFile": { + "$ref": "#/$defs/EnvString", + "description": "Path to the CA cert. For a client this verifies the server certificate. For a server this verifies client certificates.\nIf empty uses system root CA." + }, + "caPem": { + "$ref": "#/$defs/EnvString", + "description": "Alternative to ca_file. Provide the CA cert contents as a string instead of a filepath." + }, + "insecureSkipVerify": { + "$ref": "#/$defs/EnvBoolean", + "description": "Additionally you can configure TLS to be enabled but skip verifying the server's certificate chain." + }, + "includeSystemCACertsPool": { + "$ref": "#/$defs/EnvBoolean", + "description": "Whether to load the system certificate authorities pool alongside the certificate authority." + }, + "minVersion": { + "$ref": "#/$defs/EnvString", + "description": "Minimum acceptable TLS version." + }, + "maxVersion": { + "$ref": "#/$defs/EnvString", + "description": "Maximum acceptable TLS version." + }, + "cipherSuites": { + "$ref": "#/$defs/EnvStrings", + "description": "Explicit cipher suites can be set. If left blank, a safe default list is used.\nSee https://go.dev/src/crypto/tls/cipher_suites.go for a list of supported cipher suites." + }, + "reloadInterval": { + "$ref": "#/$defs/EnvInt", + "description": "Specifies the duration after which the certificate will be reloaded. If not set, it will never be reloaded.\nThe interval unit is minute" + } + }, + "additionalProperties": false, + "type": "object", + "description": "TLSConfig represents the transport layer security (LTS) configuration for the mutualTLS authentication" + }, + "Type": { + "type": "object" + }, + "TypeRepresentation": { + "type": "object" + }, + "TypeSchema": { + "properties": { + "type": { + "items": { + "type": "string" + }, + "type": "array" + }, + "format": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "maximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "maxLength": { + "type": "integer" + }, + "minLength": { + "type": "integer" + }, + "enum": { + "items": { + "type": "string" + }, + "type": "array" + }, + "items": { + "$ref": "#/$defs/TypeSchema" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "type" + ], + "description": "TypeSchema represents a serializable object of OpenAPI schema that is used for validation" + } + } +} \ No newline at end of file diff --git a/ndc-rest-schema/openapi/internal/oas2_operation.go b/ndc-rest-schema/openapi/internal/oas2_operation.go index a798698..fb04aaa 100644 --- a/ndc-rest-schema/openapi/internal/oas2_operation.go +++ b/ndc-rest-schema/openapi/internal/oas2_operation.go @@ -255,9 +255,8 @@ func (oc *oas2OperationBuilder) convertParameters(operation *v2.Operation, apiPa Type: argument.Type, Description: argument.Description, }, + Rest: typeSchema, } - // TTODO: remove - // formData.Properties[paramName] = *typeSchema } default: argument.Rest = &rest.RequestParameter{ @@ -281,7 +280,7 @@ func (oc *oas2OperationBuilder) convertParameters(operation *v2.Operation, apiPa Description: &desc, }, Rest: &rest.RequestParameter{ - In: rest.InBody, + In: rest.InFormData, Schema: &formData, }, } diff --git a/ndc-rest-schema/openapi/internal/oas3.go b/ndc-rest-schema/openapi/internal/oas3.go index e62ad1b..65b18f4 100644 --- a/ndc-rest-schema/openapi/internal/oas3.go +++ b/ndc-rest-schema/openapi/internal/oas3.go @@ -29,8 +29,9 @@ type OAS3Builder struct { // SchemaInfoCache stores prebuilt information of component schema types. type SchemaInfoCache struct { - Name string - Schema schema.TypeEncoder + Name string + Schema schema.TypeEncoder + TypeSchema *rest.TypeSchema } // NewOAS3Builder creates an OAS3Builder instance @@ -329,6 +330,9 @@ func (oc *OAS3Builder) populateWriteSchemaType(schemaType schema.Type) (schema.T oc.schemaCache[ty.Name] = SchemaInfoCache{ Name: ty.Name, Schema: schema.NewNamedType(ty.Name), + TypeSchema: &rest.TypeSchema{ + Type: []string{"object"}, + }, } } @@ -360,6 +364,7 @@ func (oc *OAS3Builder) populateWriteSchemaType(schemaType schema.Type) (schema.T Description: field.Description, Type: ut, }, + Rest: field.Rest, } if isInput { hasWriteField = true diff --git a/ndc-rest-schema/openapi/internal/oas3_operation.go b/ndc-rest-schema/openapi/internal/oas3_operation.go index 6c3a127..d4954f6 100644 --- a/ndc-rest-schema/openapi/internal/oas3_operation.go +++ b/ndc-rest-schema/openapi/internal/oas3_operation.go @@ -248,12 +248,10 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP oc.builder.typeUsageCounter.Add(getNamedType(schemaType, true, ""), 1) bodyResult := &rest.RequestBody{ ContentType: contentType, - // TODO: move to argument - // Schema: typeSchema, } if content.Encoding != nil { - encoding := make(map[string]rest.EncodingObject) + bodyResult.Encoding = make(map[string]rest.EncodingObject) for iter := content.Encoding.First(); iter != nil; iter = iter.Next() { encodingValue := iter.Value() if encodingValue == nil { @@ -292,7 +290,6 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP headerEncoding := rest.EncodingObject{ AllowReserved: header.AllowReserved, Explode: &header.Explode, - Headers: map[string]rest.RequestParameter{}, } if header.Style != "" { @@ -325,9 +322,8 @@ func (oc *oas3OperationBuilder) convertRequestBody(reqBody *v3.RequestBody, apiP } } - encoding[iter.Key()] = item + bodyResult.Encoding[iter.Key()] = item } - bodyResult.Encoding = encoding } return bodyResult, schemaType, nil } diff --git a/ndc-rest-schema/openapi/internal/oas3_schema.go b/ndc-rest-schema/openapi/internal/oas3_schema.go index ec48b7f..4854264 100644 --- a/ndc-rest-schema/openapi/internal/oas3_schema.go +++ b/ndc-rest-schema/openapi/internal/oas3_schema.go @@ -54,10 +54,13 @@ func (oc *oas3SchemaBuilder) getSchemaTypeFromProxy(schemaProxy *base.SchemaProx } else if typeCache, ok := oc.builder.schemaCache[rawRefName]; ok { isRef = true ndcType = typeCache.Schema - typeSchema = &rest.TypeSchema{ - // TODO: remove cache - Type: []string{typeCache.Name}, - Description: innerSchema.Description, + typeSchema = typeCache.TypeSchema + if typeSchema == nil { + typeSchema = &rest.TypeSchema{ + // TODO: remove cache + Type: []string{"object"}, + Description: innerSchema.Description, + } } } else { // return early object from ref @@ -77,14 +80,15 @@ func (oc *oas3SchemaBuilder) getSchemaTypeFromProxy(schemaProxy *base.SchemaProx } typeSchema.Description = innerSchema.Description oc.builder.schemaCache[rawRefName] = SchemaInfoCache{ - Name: schemaName, - Schema: ndcType, + Name: schemaName, + Schema: ndcType, + TypeSchema: typeSchema, } } else { ndcType = schema.NewNamedType(schemaName) typeSchema = &rest.TypeSchema{ // TODO: remove cache - Type: []string{typeCache.Name}, + Type: []string{"object"}, Description: innerSchema.Description, } } @@ -167,13 +171,16 @@ func (oc *oas3SchemaBuilder) getSchemaType(typeSchema *base.Schema, fieldPaths [ switch typeName { case "object": refName := utils.StringSliceToPascalCase(fieldPaths) + objectTypeSchema := &rest.TypeSchema{ + Type: []string{"object"}, + } if typeSchema.Properties == nil || typeSchema.Properties.IsZero() { if typeSchema.AdditionalProperties != nil && (typeSchema.AdditionalProperties.A == nil || !typeSchema.AdditionalProperties.B) { return nil, nil, false, nil } // treat no-property objects as a JSON scalar - return oc.builder.buildScalarJSON(), &rest.TypeSchema{}, false, nil + return oc.builder.buildScalarJSON(), objectTypeSchema, false, nil } object := rest.ObjectType{ @@ -210,14 +217,12 @@ func (oc *oas3SchemaBuilder) getSchemaType(typeSchema *base.Schema, fieldPaths [ ObjectField: schema.ObjectField{ Type: propType.Encode(), }, + Rest: propApiSchema, } if propApiSchema.Description != "" { objField.Description = &propApiSchema.Description } - if (!propApiSchema.ReadOnly && !propApiSchema.WriteOnly) || (!oc.writeMode && propApiSchema.ReadOnly) || (oc.writeMode || propApiSchema.WriteOnly) { - objField.Rest = propApiSchema - } if !propApiSchema.ReadOnly && !propApiSchema.WriteOnly { object.Fields[propName] = objField } else if !oc.writeMode && propApiSchema.ReadOnly { @@ -375,11 +380,5 @@ func (oc *oas3SchemaBuilder) buildAllOfAnyOfSchemaType(schemaProxies []*base.Sch refName = writeRefName } - // TODO: remove type - // if len(typeSchema.Properties) == 0 { - // typeSchema = &rest.TypeSchema{ - // // Type: refName, - // } - // } return schema.NewNamedType(refName), typeSchema, false, nil } diff --git a/ndc-rest-schema/openapi/oas2_test.go b/ndc-rest-schema/openapi/oas2_test.go index b084da9..b57f5b2 100644 --- a/ndc-rest-schema/openapi/oas2_test.go +++ b/ndc-rest-schema/openapi/oas2_test.go @@ -27,13 +27,13 @@ func TestOpenAPIv2ToRESTSchema(t *testing.T) { TrimPrefix: "/v1", }, }, - // go run . convert -f ./openapi/testdata/petstore2/swagger.json -o ./openapi/testdata/petstore2/expected.json --spec oas2 + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/petstore2/swagger.json -o ./ndc-rest-schema/openapi/testdata/petstore2/expected.json --spec oas2 { Name: "petstore2", Source: "testdata/petstore2/swagger.json", Expected: "testdata/petstore2/expected.json", }, - // go run . convert -f ./openapi/testdata/prefix2/source.json -o ./openapi/testdata/prefix2/expected_single_word.json --spec oas2 --prefix hasura + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/prefix2/source.json -o ./ndc-rest-schema/openapi/testdata/prefix2/expected_single_word.json --spec oas2 --prefix hasura { Name: "prefix2_single_word", Source: "testdata/prefix2/source.json", @@ -42,7 +42,7 @@ func TestOpenAPIv2ToRESTSchema(t *testing.T) { Prefix: "hasura", }, }, - // go run . convert -f ./openapi/testdata/prefix2/source.json -o ./openapi/testdata/prefix2/expected_multi_words.json --spec oas2 --prefix hasura_mock_json + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/prefix2/source.json -o ./ndc-rest-schema/openapi/testdata/prefix2/expected_multi_words.json --spec oas2 --prefix hasura_mock_json { Name: "prefix2_single_word", Source: "testdata/prefix2/source.json", diff --git a/ndc-rest-schema/openapi/oas3_test.go b/ndc-rest-schema/openapi/oas3_test.go index a208bc0..23feb89 100644 --- a/ndc-rest-schema/openapi/oas3_test.go +++ b/ndc-rest-schema/openapi/oas3_test.go @@ -18,7 +18,7 @@ func TestOpenAPIv3ToRESTSchema(t *testing.T) { Expected string Options ConvertOptions }{ - // go run . convert -f ./openapi/testdata/petstore3/source.json -o ./openapi/testdata/petstore3/expected.json --trim-prefix /v1 --spec openapi3 --env-prefix PET_STORE + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/petstore3/source.json -o ./ndc-rest-schema/openapi/testdata/petstore3/expected.json --trim-prefix /v1 --spec openapi3 --env-prefix PET_STORE { Name: "petstore3", Source: "testdata/petstore3/source.json", @@ -28,21 +28,21 @@ func TestOpenAPIv3ToRESTSchema(t *testing.T) { EnvPrefix: "PET_STORE", }, }, - // go run . convert -f ./openapi/testdata/onesignal/source.json -o ./openapi/testdata/onesignal/expected.json --spec openapi3 + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/onesignal/source.json -o ./ndc-rest-schema/openapi/testdata/onesignal/expected.json --spec openapi3 { Name: "onesignal", Source: "testdata/onesignal/source.json", Expected: "testdata/onesignal/expected.json", Options: ConvertOptions{}, }, - // go run . convert -f ./openapi/testdata/openai/source.json -o ./openapi/testdata/openai/expected.json --spec openapi3 + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/openai/source.json -o ./ndc-rest-schema/openapi/testdata/openai/expected.json --spec openapi3 { Name: "openai", Source: "testdata/openai/source.json", Expected: "testdata/openai/expected.json", Options: ConvertOptions{}, }, - // go run . convert -f ./openapi/testdata/prefix3/source.json -o ./openapi/testdata/prefix3/expected_single_word.json --spec openapi3 --prefix hasura + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/prefix3/source.json -o ./ndc-rest-schema/openapi/testdata/prefix3/expected_single_word.json --spec openapi3 --prefix hasura { Name: "prefix3_single_word", Source: "testdata/prefix3/source.json", @@ -51,7 +51,7 @@ func TestOpenAPIv3ToRESTSchema(t *testing.T) { Prefix: "hasura", }, }, - // go run . convert -f ./openapi/testdata/prefix3/source.json -o ./openapi/testdata/prefix3/expected_multi_words.json --spec openapi3 --prefix hasura_one_signal + // go run ./ndc-rest-schema convert -f ./ndc-rest-schema/openapi/testdata/prefix3/source.json -o ./ndc-rest-schema/openapi/testdata/prefix3/expected_multi_words.json --spec openapi3 --prefix hasura_one_signal { Name: "prefix3_multi_words", Source: "testdata/prefix3/source.json", @@ -88,9 +88,13 @@ func TestOpenAPIv3ToRESTSchema(t *testing.T) { } func assertRESTSchemaEqual(t *testing.T, expected *schema.NDCRestSchema, output *schema.NDCRestSchema) { + t.Helper() assert.DeepEqual(t, expected.Settings, output.Settings) assert.DeepEqual(t, expected.ScalarTypes, output.ScalarTypes) - assert.DeepEqual(t, expected.ObjectTypes, output.ObjectTypes) + objectBs, _ := json.Marshal(output.ObjectTypes) + var objectTypes map[string]schema.ObjectType + assert.NilError(t, json.Unmarshal(objectBs, &objectTypes)) + assert.DeepEqual(t, expected.ObjectTypes, objectTypes) assert.DeepEqual(t, expected.Procedures, output.Procedures) assert.DeepEqual(t, expected.Functions, output.Functions) } diff --git a/ndc-rest-schema/openapi/testdata/onesignal/expected.json b/ndc-rest-schema/openapi/testdata/onesignal/expected.json index 6586555..98c5496 100644 --- a/ndc-rest-schema/openapi/testdata/onesignal/expected.json +++ b/ndc-rest-schema/openapi/testdata/onesignal/expected.json @@ -28,45 +28,11 @@ }, "version": "1.2.2" }, - "collections": [], "functions": [ { "request": { "url": "/notifications", "method": "get", - "parameters": [ - { - "name": "app_id", - "in": "query", - "schema": { - "type": "String" - } - }, - { - "name": "kind", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - } - ], "security": [ { "app_key": [] @@ -82,6 +48,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "app_id", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "kind": { @@ -92,6 +67,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "kind", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "limit": { @@ -102,6 +86,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "limit", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "offset": { @@ -112,6 +105,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "offset", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -126,22 +128,6 @@ "request": { "url": "/notifications/{notification_id}", "method": "get", - "parameters": [ - { - "name": "notification_id", - "in": "path", - "schema": { - "type": "String" - } - }, - { - "name": "app_id", - "in": "query", - "schema": { - "type": "String" - } - } - ], "security": [ { "app_key": [] @@ -156,12 +142,30 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "app_id", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "notification_id": { "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "notification_id", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -183,6 +187,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } } } @@ -196,6 +205,11 @@ "name": "Notification200Errors", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "external_id": { @@ -205,6 +219,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "id": { @@ -214,6 +233,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "recipients": { @@ -223,6 +247,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -237,6 +266,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "errored": { @@ -247,6 +281,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "failed": { @@ -257,6 +296,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "received": { @@ -267,6 +311,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "successful": { @@ -277,6 +326,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -288,6 +342,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "key": { @@ -298,6 +357,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "relation": { @@ -305,6 +369,11 @@ "type": { "name": "FilterRelation", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { @@ -315,6 +384,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -328,6 +402,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "email": { @@ -338,6 +417,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "events": { @@ -348,6 +432,11 @@ "name": "NotificationsEvents", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -361,6 +450,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "success": { @@ -370,6 +464,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } } } @@ -383,6 +482,12 @@ "name": "TimestampTZ", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "date-time" } } } @@ -396,6 +501,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "notifications": { @@ -408,6 +518,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "offset": { @@ -417,6 +532,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "total_count": { @@ -426,6 +546,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -440,6 +565,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "contents": { @@ -449,6 +580,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "converted": { @@ -459,6 +595,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "custom_data": { @@ -468,6 +609,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "data": { @@ -477,6 +623,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "errored": { @@ -487,6 +638,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "excluded_segments": { @@ -499,6 +655,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "failed": { @@ -509,6 +675,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "filters": { @@ -521,6 +692,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "headings": { @@ -530,6 +706,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "id": { @@ -539,6 +720,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "include_player_ids": { @@ -551,6 +737,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "included_segments": { @@ -563,6 +759,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "outcomes": { @@ -575,6 +781,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "platform_delivery_stats": { @@ -585,6 +796,11 @@ "name": "PlatformDeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "queued_at": { @@ -595,6 +811,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "received": { @@ -605,6 +827,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "remaining": { @@ -615,6 +842,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "send_after": { @@ -625,6 +857,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "subtitle": { @@ -634,6 +872,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "successful": { @@ -644,6 +887,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "target_channel": { @@ -653,6 +901,11 @@ "name": "PlayerNotificationTargetTargetChannel", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "throttle_rate_per_minute": { @@ -663,6 +916,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -673,18 +931,33 @@ "type": { "name": "OutcomeDataAggregation", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "id": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -699,6 +972,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "chrome_web_push": { @@ -708,6 +986,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "edge_web_push": { @@ -717,6 +1000,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "email": { @@ -726,6 +1014,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "firefox_web_push": { @@ -735,6 +1028,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "ios": { @@ -744,6 +1042,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "safari_web_push": { @@ -753,6 +1056,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "sms": { @@ -762,6 +1070,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -776,6 +1089,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -792,10 +1110,7 @@ } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "NotificationInput" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -807,6 +1122,9 @@ "type": { "name": "NotificationInput", "type": "named" + }, + "rest": { + "in": "body" } } }, @@ -821,22 +1139,6 @@ "request": { "url": "/notifications/{notification_id}", "method": "delete", - "parameters": [ - { - "name": "notification_id", - "in": "path", - "schema": { - "type": "String" - } - }, - { - "name": "app_id", - "in": "query", - "schema": { - "type": "String" - } - } - ], "security": [ { "app_key": [] @@ -851,12 +1153,30 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "app_id", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "notification_id": { "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "notification_id", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -871,39 +1191,13 @@ "request": { "url": "/notifications/{notification_id}/history", "method": "post", - "parameters": [ - { - "name": "notification_id", - "in": "path", - "schema": { - "type": "String" - } - } - ], "security": [ { "app_key": [] } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "object", - "properties": { - "app_id": { - "type": "String", - "nullable": true - }, - "email": { - "type": "String", - "nullable": true - }, - "events": { - "type": "NotificationsEvents", - "nullable": true - } - } - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -915,6 +1209,9 @@ "type": { "name": "GetNotificationHistoryBody", "type": "named" + }, + "rest": { + "in": "body" } }, "notification_id": { @@ -922,6 +1219,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "notification_id", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, diff --git a/ndc-rest-schema/openapi/testdata/openai/expected.json b/ndc-rest-schema/openapi/testdata/openai/expected.json index 30df2ad..998eaaf 100644 --- a/ndc-rest-schema/openapi/testdata/openai/expected.json +++ b/ndc-rest-schema/openapi/testdata/openai/expected.json @@ -30,7 +30,6 @@ ], "version": "2.1.0" }, - "collections": [], "functions": [], "object_types": { "ChatCompletionFunctions": { @@ -43,6 +42,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "name": { @@ -50,6 +54,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "parameters": { @@ -60,6 +69,11 @@ "name": "FunctionParameters", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -71,6 +85,11 @@ "type": { "name": "ChatCompletionMessageToolCallFunction", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } }, "id": { @@ -78,6 +97,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "type": { @@ -85,6 +109,11 @@ "type": { "name": "ChatCompletionMessageToolCallType", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -97,6 +126,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "name": { @@ -104,6 +138,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -116,6 +155,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "function_call": { @@ -126,6 +170,11 @@ "name": "ChatCompletionResponseMessageFunctionCall", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "role": { @@ -133,6 +182,11 @@ "type": { "name": "ChatCompletionResponseMessageRole", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "tool_calls": { @@ -146,6 +200,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } } } @@ -158,6 +217,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "name": { @@ -165,6 +229,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -180,6 +249,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } } } @@ -194,6 +268,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "integer" + ] + } } }, "logprob": { @@ -201,6 +285,11 @@ "type": { "name": "Float64", "type": "named" + }, + "rest": { + "type": [ + "number" + ] } }, "token": { @@ -208,6 +297,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "top_logprobs": { @@ -218,6 +312,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } } } @@ -232,6 +336,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "integer" + ] + } } }, "logprob": { @@ -239,6 +353,11 @@ "type": { "name": "Float64", "type": "named" + }, + "rest": { + "type": [ + "number" + ] } }, "token": { @@ -246,6 +365,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -256,6 +380,11 @@ "type": { "name": "FunctionObject", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } }, "type": { @@ -263,6 +392,11 @@ "type": { "name": "ChatCompletionToolType", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -277,6 +411,13 @@ "name": "Float64", "type": "named" } + }, + "rest": { + "type": [ + "number" + ], + "maximum": 2, + "minimum": -2 } }, "function_call": { @@ -287,6 +428,9 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": null } }, "functions": { @@ -300,6 +444,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "logit_bias": { @@ -310,6 +459,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "logprobs": { @@ -320,6 +474,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "max_tokens": { @@ -330,6 +489,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "messages": { @@ -340,6 +504,11 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ] } }, "model": { @@ -347,6 +516,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "n": { @@ -357,6 +531,13 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "maximum": 128, + "minimum": 1 } }, "parallel_tool_calls": { @@ -367,6 +548,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "presence_penalty": { @@ -377,6 +563,13 @@ "name": "Float64", "type": "named" } + }, + "rest": { + "type": [ + "number" + ], + "maximum": 2, + "minimum": -2 } }, "response_format": { @@ -387,6 +580,11 @@ "name": "CreateChatCompletionRequestResponseFormat", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "seed": { @@ -397,6 +595,13 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "maximum": 9223372036854776000, + "minimum": -9223372036854776000 } }, "service_tier": { @@ -407,6 +612,11 @@ "name": "CreateChatCompletionRequestServiceTier", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "stop": { @@ -417,6 +627,9 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": null } }, "stream": { @@ -427,6 +640,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "stream_options": { @@ -437,6 +655,11 @@ "name": "ChatCompletionStreamOptions", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "temperature": { @@ -447,6 +670,13 @@ "name": "Float64", "type": "named" } + }, + "rest": { + "type": [ + "number" + ], + "maximum": 2, + "minimum": 0 } }, "tool_choice": { @@ -457,6 +687,11 @@ "name": "ChatCompletionToolChoiceOption", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "tools": { @@ -470,6 +705,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "top_logprobs": { @@ -480,6 +720,13 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "maximum": 20, + "minimum": 0 } }, "top_p": { @@ -490,6 +737,13 @@ "name": "Float64", "type": "named" } + }, + "rest": { + "type": [ + "number" + ], + "maximum": 1, + "minimum": 0 } }, "user": { @@ -500,6 +754,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -515,6 +774,11 @@ "name": "CreateChatCompletionRequestResponseFormatType", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -530,6 +794,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } }, "created": { @@ -537,6 +811,11 @@ "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } }, "id": { @@ -544,6 +823,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "model": { @@ -551,6 +835,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "object": { @@ -558,6 +847,11 @@ "type": { "name": "CreateChatCompletionResponseObject", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "service_tier": { @@ -568,6 +862,11 @@ "name": "CreateChatCompletionResponseServiceTier", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "system_fingerprint": { @@ -578,6 +877,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -589,6 +893,11 @@ "type": { "name": "CreateChatCompletionResponseChoicesFinishReason", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "index": { @@ -596,6 +905,11 @@ "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } }, "logprobs": { @@ -603,6 +917,11 @@ "type": { "name": "CreateChatCompletionResponseChoicesLogprobs", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } }, "message": { @@ -610,6 +929,11 @@ "type": { "name": "ChatCompletionResponseMessage", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } } } @@ -625,6 +949,11 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ] } } } @@ -639,6 +968,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -653,6 +987,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "name": { @@ -660,6 +999,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "parameters": { @@ -670,6 +1014,11 @@ "name": "FunctionParameters", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -684,6 +1033,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "digest": { @@ -694,6 +1048,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "status": { @@ -704,6 +1063,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "total": { @@ -714,6 +1078,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -725,10 +1094,7 @@ "url": "/api/create", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "CreateModelRequest" - } + "contentType": "application/json" }, "response": { "contentType": "application/x-ndjson" @@ -740,6 +1106,9 @@ "type": { "name": "CreateModelRequest", "type": "named" + }, + "rest": { + "in": "body" } } }, @@ -757,10 +1126,7 @@ "url": "/chat/completions", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "CreateChatCompletionRequest" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -772,6 +1138,9 @@ "type": { "name": "CreateChatCompletionRequest", "type": "named" + }, + "rest": { + "in": "body" } } }, diff --git a/ndc-rest-schema/openapi/testdata/petstore2/expected.json b/ndc-rest-schema/openapi/testdata/petstore2/expected.json index 13f2ce2..1b134d5 100644 --- a/ndc-rest-schema/openapi/testdata/petstore2/expected.json +++ b/ndc-rest-schema/openapi/testdata/petstore2/expected.json @@ -40,21 +40,11 @@ }, "version": "1.0.6" }, - "collections": [], "functions": [ { "request": { "url": "/pet/findByStatus", "method": "get", - "parameters": [ - { - "name": "status", - "in": "query", - "schema": { - "type": "array" - } - } - ], "security": [ { "petstore_auth": [ @@ -76,6 +66,15 @@ "type": "named" }, "type": "array" + }, + "rest": { + "name": "status", + "in": "query", + "schema": { + "type": [ + "array" + ] + } } } }, @@ -93,15 +92,6 @@ "request": { "url": "/pet/findByTags", "method": "get", - "parameters": [ - { - "name": "tags", - "in": "query", - "schema": { - "type": "array" - } - } - ], "security": [ { "petstore_auth": [ @@ -123,6 +113,15 @@ "type": "named" }, "type": "array" + }, + "rest": { + "name": "tags", + "in": "query", + "schema": { + "type": [ + "array" + ] + } } } }, @@ -140,15 +139,6 @@ "request": { "url": "/pet/{petId}", "method": "get", - "parameters": [ - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "security": [ { "api_key": [] @@ -164,6 +154,15 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -178,17 +177,6 @@ "request": { "url": "/store/order/{orderId}", "method": "get", - "parameters": [ - { - "name": "orderId", - "in": "path", - "schema": { - "type": "Int64", - "maximum": 10, - "minimum": 1 - } - } - ], "response": { "contentType": "application/json" } @@ -199,6 +187,17 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "orderId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "maximum": 10, + "minimum": 1 + } } } }, @@ -234,15 +233,6 @@ "request": { "url": "/user/{username}", "method": "get", - "parameters": [ - { - "name": "username", - "in": "path", - "schema": { - "type": "String" - } - } - ], "response": { "contentType": "application/json" } @@ -253,6 +243,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "username", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -267,22 +266,6 @@ "request": { "url": "/user/login", "method": "get", - "parameters": [ - { - "name": "username", - "in": "query", - "schema": { - "type": "String" - } - }, - { - "name": "password", - "in": "query", - "schema": { - "type": "String" - } - } - ], "response": { "contentType": "application/json" } @@ -293,6 +276,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "password", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "username": { @@ -300,6 +292,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "username", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -330,40 +331,6 @@ "request": { "url": "/clients", "method": "get", - "parameters": [ - { - "name": "limit", - "in": "query", - "schema": { - "type": "Int64", - "nullable": true - } - }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "Int64", - "nullable": true - } - }, - { - "name": "client_name", - "in": "query", - "schema": { - "type": "String", - "nullable": true - } - }, - { - "name": "owner", - "in": "query", - "schema": { - "type": "String", - "nullable": true - } - } - ], "response": { "contentType": "application/json" } @@ -377,6 +344,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "client_name", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "limit": { @@ -387,6 +363,15 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "name": "limit", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "offset": { @@ -397,6 +382,15 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "name": "offset", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "owner": { @@ -407,6 +401,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "owner", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -431,6 +434,12 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int32" } }, "message": { @@ -440,6 +449,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "type": { @@ -449,6 +463,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -462,6 +481,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "name": { @@ -471,6 +496,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -485,6 +515,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "client_name": { @@ -495,6 +530,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "client_secret": { @@ -505,6 +545,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "client_secret_expires_at": { @@ -515,6 +560,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "client_uri": { @@ -525,6 +576,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -538,6 +594,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "id": { @@ -547,6 +608,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "petId": { @@ -556,6 +623,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "quantity": { @@ -565,6 +638,12 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int32" } }, "shipDate": { @@ -574,6 +653,12 @@ "name": "TimestampTZ", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "date-time" } }, "status": { @@ -584,6 +669,11 @@ "name": "OrderStatus", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -597,6 +687,9 @@ "name": "Category", "type": "named" } + }, + "rest": { + "type": null } }, "id": { @@ -606,12 +699,23 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "name": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "photoUrls": { @@ -621,6 +725,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "status": { @@ -631,6 +745,11 @@ "name": "PetStatus", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "tags": { @@ -643,6 +762,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } } } @@ -656,6 +780,9 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": null } }, "id": { @@ -665,6 +792,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "username": { @@ -674,6 +807,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -687,6 +825,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "name": { @@ -696,6 +840,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -710,6 +859,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "status": { @@ -720,6 +874,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -734,6 +893,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "file": { @@ -744,6 +908,11 @@ "name": "Binary", "type": "named" } + }, + "rest": { + "type": [ + "file" + ] } } } @@ -757,6 +926,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "firstName": { @@ -766,6 +940,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "id": { @@ -775,6 +954,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "lastName": { @@ -784,6 +969,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "password": { @@ -793,6 +983,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "phone": { @@ -802,6 +997,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "userStatus": { @@ -812,6 +1012,12 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int32" } }, "username": { @@ -821,6 +1027,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -831,15 +1042,6 @@ "request": { "url": "/pet/{petId}/uploadImage", "method": "post", - "parameters": [ - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "security": [ { "petstore_auth": [ @@ -849,20 +1051,7 @@ } ], "requestBody": { - "contentType": "multipart/form-data", - "schema": { - "type": "object", - "properties": { - "additionalMetadata": { - "type": "String", - "nullable": true - }, - "file": { - "type": "Binary", - "nullable": true - } - } - } + "contentType": "multipart/form-data" }, "response": { "contentType": "application/json" @@ -874,6 +1063,14 @@ "type": { "name": "UploadFileBody", "type": "named" + }, + "rest": { + "in": "formData", + "schema": { + "type": [ + "object" + ] + } } }, "petId": { @@ -881,6 +1078,15 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -904,10 +1110,7 @@ } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Pet" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -919,6 +1122,12 @@ "type": { "name": "Pet", "type": "named" + }, + "rest": { + "in": "body", + "schema": { + "type": null + } } } }, @@ -945,10 +1154,7 @@ } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Pet" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -960,6 +1166,12 @@ "type": { "name": "Pet", "type": "named" + }, + "rest": { + "in": "body", + "schema": { + "type": null + } } } }, @@ -977,15 +1189,6 @@ "request": { "url": "/pet/{petId}", "method": "post", - "parameters": [ - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "security": [ { "petstore_auth": [ @@ -995,20 +1198,7 @@ } ], "requestBody": { - "contentType": "application/x-www-form-urlencoded", - "schema": { - "type": "object", - "properties": { - "name": { - "type": "String", - "nullable": true - }, - "status": { - "type": "String", - "nullable": true - } - } - } + "contentType": "application/x-www-form-urlencoded" }, "response": { "contentType": "application/json" @@ -1020,6 +1210,14 @@ "type": { "name": "UpdatePetWithFormBody", "type": "named" + }, + "rest": { + "in": "formData", + "schema": { + "type": [ + "object" + ] + } } }, "petId": { @@ -1027,6 +1225,15 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -1044,23 +1251,6 @@ "request": { "url": "/pet/{petId}", "method": "delete", - "parameters": [ - { - "name": "api_key", - "in": "header", - "schema": { - "type": "String", - "nullable": true - } - }, - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "security": [ { "petstore_auth": [ @@ -1081,6 +1271,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "api_key", + "in": "header", + "schema": { + "type": [ + "string" + ] + } } }, "petId": { @@ -1088,6 +1287,15 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -1106,10 +1314,7 @@ "url": "/store/order", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Order" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -1121,6 +1326,12 @@ "type": { "name": "Order", "type": "named" + }, + "rest": { + "in": "body", + "schema": { + "type": null + } } } }, @@ -1135,16 +1346,6 @@ "request": { "url": "/store/order/{orderId}", "method": "delete", - "parameters": [ - { - "name": "orderId", - "in": "path", - "schema": { - "type": "Int64", - "minimum": 1 - } - } - ], "response": { "contentType": "application/json" } @@ -1155,6 +1356,16 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "orderId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "minimum": 1 + } } } }, @@ -1172,20 +1383,8 @@ "request": { "url": "/user/{username}", "method": "put", - "parameters": [ - { - "name": "username", - "in": "path", - "schema": { - "type": "String" - } - } - ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "User" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -1197,6 +1396,12 @@ "type": { "name": "User", "type": "named" + }, + "rest": { + "in": "body", + "schema": { + "type": null + } } }, "username": { @@ -1204,6 +1409,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "username", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -1221,15 +1435,6 @@ "request": { "url": "/user/{username}", "method": "delete", - "parameters": [ - { - "name": "username", - "in": "path", - "schema": { - "type": "String" - } - } - ], "response": { "contentType": "application/json" } @@ -1240,6 +1445,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "username", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -1274,10 +1488,7 @@ "url": "/oauth2/register", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "OAuth2Client" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -1288,6 +1499,12 @@ "type": { "name": "OAuth2Client", "type": "named" + }, + "rest": { + "in": "body", + "schema": { + "type": null + } } } }, diff --git a/ndc-rest-schema/openapi/testdata/petstore3/expected.json b/ndc-rest-schema/openapi/testdata/petstore3/expected.json index 08698da..abaa5fc 100644 --- a/ndc-rest-schema/openapi/testdata/petstore3/expected.json +++ b/ndc-rest-schema/openapi/testdata/petstore3/expected.json @@ -52,23 +52,11 @@ ], "version": "1.0.19" }, - "collections": [], "functions": [ { "request": { "url": "/pet/findByStatus", "method": "get", - "parameters": [ - { - "explode": true, - "name": "status", - "in": "query", - "schema": { - "type": "PetStatus", - "nullable": true - } - } - ], "security": [ { "petstore_auth": [ @@ -90,6 +78,16 @@ "name": "PetStatus", "type": "named" } + }, + "rest": { + "explode": true, + "name": "status", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -107,20 +105,6 @@ "request": { "url": "/pet/findByTags", "method": "get", - "parameters": [ - { - "explode": true, - "name": "tags", - "in": "query", - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "String" - } - } - } - ], "security": [ { "petstore_auth": [ @@ -145,6 +129,21 @@ }, "type": "array" } + }, + "rest": { + "explode": true, + "name": "tags", + "in": "query", + "schema": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } + } } } }, @@ -162,15 +161,6 @@ "request": { "url": "/pet/{petId}", "method": "get", - "parameters": [ - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "security": [ { "api_key": [] @@ -192,6 +182,16 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "format": "int64" + } } } }, @@ -227,15 +227,6 @@ "request": { "url": "/store/order/{orderId}", "method": "get", - "parameters": [ - { - "name": "orderId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "response": { "contentType": "application/json" } @@ -246,6 +237,16 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "orderId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "format": "int64" + } } } }, @@ -260,24 +261,6 @@ "request": { "url": "/user/login", "method": "get", - "parameters": [ - { - "name": "password", - "in": "query", - "schema": { - "type": "String", - "nullable": true - } - }, - { - "name": "username", - "in": "query", - "schema": { - "type": "String", - "nullable": true - } - } - ], "response": { "contentType": "application/json" } @@ -291,6 +274,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "password", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "username": { @@ -301,6 +293,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "username", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -315,15 +316,6 @@ "request": { "url": "/user/{username}", "method": "get", - "parameters": [ - { - "name": "username", - "in": "path", - "schema": { - "type": "String" - } - } - ], "response": { "contentType": "application/json" } @@ -334,6 +326,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "username", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -359,6 +360,233 @@ "name": "SnakeObject", "type": "named" } + }, + { + "request": { + "url": "/v1/invoices", + "method": "get", + "response": { + "contentType": "application/json" + } + }, + "arguments": { + "collection_method": { + "description": "The collection method of the invoice to retrieve. Either `charge_automatically` or `send_invoice`.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "InvoicesCollectionMethod", + "type": "named" + } + }, + "rest": { + "style": "form", + "name": "collection_method", + "in": "query", + "schema": { + "type": [ + "string" + ] + } + } + }, + "created": { + "description": "Only return invoices that were created during the given date interval.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "JSON", + "type": "named" + } + }, + "rest": { + "style": "deepObject", + "explode": true, + "name": "created", + "in": "query", + "schema": { + "type": null + } + } + }, + "customer": { + "description": "Only return invoices for the customer specified by this customer ID.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "rest": { + "style": "form", + "name": "customer", + "in": "query", + "schema": { + "type": [ + "string" + ], + "maxLength": 5000 + } + } + }, + "due_date": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "JSON", + "type": "named" + } + }, + "rest": { + "style": "deepObject", + "explode": true, + "name": "due_date", + "in": "query", + "schema": { + "type": null + } + } + }, + "ending_before": { + "description": "A cursor for use in pagination. `ending_before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with `obj_bar`, your subsequent call can include `ending_before=obj_bar` in order to fetch the previous page of the list.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "rest": { + "style": "form", + "name": "ending_before", + "in": "query", + "schema": { + "type": [ + "string" + ], + "maxLength": 5000 + } + } + }, + "expand": { + "description": "Specifies which fields in the response should be expanded.", + "type": { + "type": "nullable", + "underlying_type": { + "element_type": { + "name": "String", + "type": "named" + }, + "type": "array" + } + }, + "rest": { + "style": "deepObject", + "explode": true, + "name": "expand", + "in": "query", + "schema": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } + } + } + }, + "limit": { + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "Int32", + "type": "named" + } + }, + "rest": { + "style": "form", + "name": "limit", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } + } + }, + "starting_after": { + "description": "A cursor for use in pagination. `starting_after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with `obj_foo`, your subsequent call can include `starting_after=obj_foo` in order to fetch the next page of the list.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "rest": { + "style": "form", + "name": "starting_after", + "in": "query", + "schema": { + "type": [ + "string" + ], + "maxLength": 5000 + } + } + }, + "status": { + "description": "The status of the invoice, one of `draft`, `open`, `paid`, `uncollectible`, or `void`. [Learn more](https://stripe.com/docs/billing/invoices/workflow#workflow-overview)", + "type": { + "type": "nullable", + "underlying_type": { + "name": "InvoicesStatus", + "type": "named" + } + }, + "rest": { + "style": "form", + "name": "status", + "in": "query", + "schema": { + "type": [ + "string" + ] + } + } + }, + "subscription": { + "description": "Only return invoices for the subscription specified by this subscription ID.", + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "rest": { + "style": "form", + "name": "subscription", + "in": "query", + "schema": { + "type": [ + "string" + ], + "maxLength": 5000 + } + } + } + }, + "name": "GetInvoices", + "result_type": { + "name": "GetInvoicesResult", + "type": "named" + } } ], "object_types": { @@ -371,6 +599,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "state": { @@ -380,6 +613,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "street": { @@ -389,6 +627,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "zip": { @@ -398,6 +641,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -411,6 +659,12 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int32" } }, "message": { @@ -420,6 +674,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "type": { @@ -429,6 +688,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -442,6 +706,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "name": { @@ -451,6 +721,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -464,6 +739,53 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] + } + } + } + }, + "GetInvoicesResult": { + "fields": { + "has_more": { + "description": "True if this list has another page of items after this one that can be fetched.", + "type": { + "name": "Boolean", + "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] + } + }, + "object": { + "description": "String representing the object's type. Objects of the same type share the same value. Always has the value `list`.", + "type": { + "name": "InvoicesObject", + "type": "named" + }, + "rest": { + "type": [ + "string" + ] + } + }, + "url": { + "description": "The URL where this list can be accessed.", + "type": { + "name": "String", + "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "pattern": "^/v1/invoices", + "maxLength": 5000 } } } @@ -477,6 +799,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "id": { @@ -486,6 +813,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "petId": { @@ -495,6 +828,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "quantity": { @@ -504,6 +843,12 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int32" } }, "shipDate": { @@ -513,6 +858,12 @@ "name": "TimestampTZ", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "date-time" } }, "status": { @@ -523,6 +874,11 @@ "name": "OrderStatus", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -536,6 +892,11 @@ "name": "Category", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "id": { @@ -545,12 +906,23 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "name": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "photoUrls": { @@ -560,6 +932,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "status": { @@ -570,6 +952,11 @@ "name": "PetStatus", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "tags": { @@ -582,6 +969,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } } } @@ -596,6 +988,11 @@ "name": "PostCheckoutSessionsBodyAfterExpiration", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "allow_promotion_codes": { @@ -606,6 +1003,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "automatic_tax": { @@ -616,6 +1018,11 @@ "name": "PostCheckoutSessionsBodyAutomaticTax", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "billing_address_collection": { @@ -626,6 +1033,11 @@ "name": "CheckoutBillingAddressCollection", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "cancel_url": { @@ -636,6 +1048,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "client_reference_id": { @@ -646,6 +1064,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 200 } }, "consent_collection": { @@ -656,6 +1080,11 @@ "name": "PostCheckoutSessionsBodyConsentCollection", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "currency": { @@ -666,6 +1095,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "custom_fields": { @@ -679,6 +1113,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } }, "custom_text": { @@ -689,6 +1133,11 @@ "name": "PostCheckoutSessionsBodyCustomText", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "customer": { @@ -699,6 +1148,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "customer_creation": { @@ -709,6 +1164,11 @@ "name": "CheckoutCustomerCreation", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "customer_email": { @@ -719,6 +1179,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "customer_update": { @@ -729,6 +1194,11 @@ "name": "PostCheckoutSessionsBodyCustomerUpdate", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "discounts": { @@ -742,6 +1212,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } }, "expand": { @@ -755,6 +1235,17 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } } }, "expires_at": { @@ -765,6 +1256,12 @@ "name": "UnixTime", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } }, "invoice_creation": { @@ -775,6 +1272,11 @@ "name": "PostCheckoutSessionsBodyInvoiceCreation", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "line_items": { @@ -788,6 +1290,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } }, "locale": { @@ -798,6 +1310,11 @@ "name": "CheckoutLocale", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "metadata": { @@ -808,6 +1325,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "mode": { @@ -818,6 +1340,11 @@ "name": "CheckoutMode", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "payment_intent_data": { @@ -828,6 +1355,11 @@ "name": "PostCheckoutSessionsBodyPaymentIntentData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "payment_method_collection": { @@ -838,6 +1370,11 @@ "name": "CheckoutPaymentMethodCollection", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "payment_method_configuration": { @@ -848,6 +1385,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 100 } }, "payment_method_options": { @@ -858,6 +1401,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptions", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "payment_method_types": { @@ -871,6 +1419,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "phone_number_collection": { @@ -881,6 +1439,11 @@ "name": "PostCheckoutSessionsBodyPhoneNumberCollection", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "redirect_on_completion": { @@ -891,6 +1454,11 @@ "name": "CheckoutRedirectOnCompletion", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "return_url": { @@ -901,6 +1469,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "setup_intent_data": { @@ -911,6 +1485,11 @@ "name": "PostCheckoutSessionsBodySetupIntentData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "shipping_address_collection": { @@ -921,6 +1500,11 @@ "name": "PostCheckoutSessionsBodyShippingAddressCollection", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "shipping_options": { @@ -934,6 +1518,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } }, "submit_type": { @@ -944,6 +1538,11 @@ "name": "CheckoutSubmitType", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "subscription_data": { @@ -954,6 +1553,11 @@ "name": "PostCheckoutSessionsBodySubscriptionData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "success_url": { @@ -964,6 +1568,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "tax_id_collection": { @@ -974,6 +1584,11 @@ "name": "PostCheckoutSessionsBodyTaxIdCollection", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "ui_mode": { @@ -984,6 +1599,11 @@ "name": "CheckoutUiMode", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -998,6 +1618,11 @@ "name": "PostCheckoutSessionsBodyAfterExpirationRecovery", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -1011,12 +1636,22 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "enabled": { "type": { "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } } } @@ -1028,6 +1663,11 @@ "type": { "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } }, "liability": { @@ -1037,6 +1677,11 @@ "name": "PostCheckoutSessionsBodyAutomaticTaxLiability", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -1050,12 +1695,22 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "type": { "type": { "name": "CheckoutType", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1070,6 +1725,11 @@ "name": "PostCheckoutSessionsBodyConsentCollectionPaymentMethodReuseAgreement", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "promotions": { @@ -1079,6 +1739,11 @@ "name": "CheckoutPromotions", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "terms_of_service": { @@ -1088,6 +1753,11 @@ "name": "CheckoutTermsOfService", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1098,6 +1768,11 @@ "type": { "name": "CheckoutPosition", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1111,18 +1786,34 @@ "name": "PostCheckoutSessionsBodyCustomFieldsDropdown", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "key": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 200 } }, "label": { "type": { "name": "PostCheckoutSessionsBodyCustomFieldsLabel", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } }, "numeric": { @@ -1132,6 +1823,11 @@ "name": "PostCheckoutSessionsBodyCustomFieldsNumeric", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "optional": { @@ -1141,6 +1837,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "text": { @@ -1150,12 +1851,22 @@ "name": "PostCheckoutSessionsBodyCustomFieldsText", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "type": { "type": { "name": "PostCheckoutSessionsBodyCustomFieldsType", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1169,6 +1880,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } } } @@ -1179,12 +1900,24 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 100 } }, "value": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 100 } } } @@ -1195,12 +1928,23 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 50 } }, "type": { "type": { "name": "PostCheckoutSessionsBodyCustomFieldsLabelType", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1214,6 +1958,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "minimum_length": { @@ -1223,6 +1972,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -1236,6 +1990,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "minimum_length": { @@ -1245,6 +2004,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -1259,6 +2023,11 @@ "name": "PostCheckoutSessionsBodyCustomTextAfterSubmit", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "shipping_address": { @@ -1268,6 +2037,11 @@ "name": "PostCheckoutSessionsBodyCustomTextShippingAddress", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "submit": { @@ -1277,6 +2051,11 @@ "name": "PostCheckoutSessionsBodyCustomTextSubmit", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "terms_of_service_acceptance": { @@ -1286,6 +2065,11 @@ "name": "PostCheckoutSessionsBodyCustomTextTermsOfServiceAcceptance", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -1296,6 +2080,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 1200 } } } @@ -1306,6 +2096,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 1200 } } } @@ -1316,6 +2112,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 1200 } } } @@ -1326,6 +2128,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 1200 } } } @@ -1340,6 +2148,11 @@ "name": "CheckoutAddress", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "name": { @@ -1349,6 +2162,11 @@ "name": "CheckoutName", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "shipping": { @@ -1358,6 +2176,11 @@ "name": "CheckoutShipping", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1371,6 +2194,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "promotion_code": { @@ -1380,6 +2209,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } } } @@ -1391,6 +2226,11 @@ "type": { "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } }, "invoice_data": { @@ -1400,6 +2240,11 @@ "name": "PostCheckoutSessionsBodyInvoiceCreationInvoiceData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -1416,6 +2261,17 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } } }, "custom_fields": { @@ -1428,6 +2284,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "object" + ] + } } }, "description": { @@ -1437,6 +2303,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 1500 } }, "footer": { @@ -1446,6 +2318,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "issuer": { @@ -1455,6 +2333,11 @@ "name": "PostCheckoutSessionsBodyInvoiceCreationInvoiceDataIssuer", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "metadata": { @@ -1464,6 +2347,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "rendering_options": { @@ -1473,6 +2361,11 @@ "name": "PostCheckoutSessionsBodyInvoiceCreationInvoiceDataRenderingOptions", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -1483,12 +2376,24 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 40 } }, "value": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 140 } } } @@ -1502,12 +2407,22 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "type": { "type": { "name": "CheckoutType", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1521,6 +2436,11 @@ "name": "CheckoutAmountTaxDisplay", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1534,6 +2454,11 @@ "name": "PostCheckoutSessionsBodyLineItemsAdjustableQuantity", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "dynamic_tax_rates": { @@ -1546,6 +2471,17 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } } }, "price": { @@ -1555,6 +2491,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "price_data": { @@ -1564,6 +2506,11 @@ "name": "PostCheckoutSessionsBodyLineItemsPriceData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "quantity": { @@ -1573,6 +2520,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "tax_rates": { @@ -1585,6 +2537,17 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } } } } @@ -1595,6 +2558,11 @@ "type": { "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } }, "maximum": { @@ -1604,6 +2572,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "minimum": { @@ -1613,6 +2586,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -1623,6 +2601,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "product": { @@ -1632,6 +2615,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "product_data": { @@ -1641,6 +2630,11 @@ "name": "PostCheckoutSessionsBodyLineItemsPriceDataProductData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "recurring": { @@ -1650,6 +2644,11 @@ "name": "PostCheckoutSessionsBodyLineItemsPriceDataRecurring", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "tax_behavior": { @@ -1659,6 +2658,11 @@ "name": "CheckoutTaxBehavior", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "unit_amount": { @@ -1668,6 +2672,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "unit_amount_decimal": { @@ -1677,6 +2686,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "decimal" } } } @@ -1690,6 +2705,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 40000 } }, "images": { @@ -1702,6 +2723,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "metadata": { @@ -1711,12 +2742,23 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "name": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "tax_code": { @@ -1726,6 +2768,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } } } @@ -1736,6 +2784,11 @@ "type": { "name": "CheckoutInterval", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "interval_count": { @@ -1745,6 +2798,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -1759,6 +2817,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "capture_method": { @@ -1768,6 +2831,11 @@ "name": "CheckoutCaptureMethod", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "description": { @@ -1777,6 +2845,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 1000 } }, "metadata": { @@ -1786,6 +2860,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "on_behalf_of": { @@ -1795,6 +2874,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "receipt_email": { @@ -1804,6 +2888,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "setup_future_usage": { @@ -1813,6 +2902,11 @@ "name": "CheckoutSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "shipping": { @@ -1822,6 +2916,11 @@ "name": "PostCheckoutSessionsBodyPaymentIntentDataShipping", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "statement_descriptor": { @@ -1831,6 +2930,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 22 } }, "statement_descriptor_suffix": { @@ -1840,6 +2945,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 22 } }, "transfer_data": { @@ -1849,6 +2960,11 @@ "name": "PostCheckoutSessionsBodyPaymentIntentDataTransferData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "transfer_group": { @@ -1858,6 +2974,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1868,6 +2989,11 @@ "type": { "name": "PostCheckoutSessionsBodyPaymentIntentDataShippingAddress", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } }, "carrier": { @@ -1877,12 +3003,24 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "name": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "phone": { @@ -1892,6 +3030,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "tracking_number": { @@ -1901,6 +3045,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } } } @@ -1914,6 +3064,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "country": { @@ -1923,12 +3079,24 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "line1": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "line2": { @@ -1938,6 +3106,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "postal_code": { @@ -1947,6 +3121,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "state": { @@ -1956,6 +3136,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } } } @@ -1969,12 +3155,22 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "destination": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -1989,6 +3185,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAcssDebit", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "affirm": { @@ -1998,6 +3199,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAffirm", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "afterpay_clearpay": { @@ -2007,6 +3213,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAfterpayClearpay", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "alipay": { @@ -2016,6 +3227,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAlipay", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "au_becs_debit": { @@ -2025,6 +3241,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAuBecsDebit", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "bacs_debit": { @@ -2034,6 +3255,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsBacsDebit", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "bancontact": { @@ -2043,6 +3269,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsBancontact", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "boleto": { @@ -2052,6 +3283,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsBoleto", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "card": { @@ -2061,6 +3297,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCard", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "cashapp": { @@ -2070,6 +3311,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCashapp", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "customer_balance": { @@ -2079,6 +3325,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCustomerBalance", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "eps": { @@ -2088,6 +3339,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsEps", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "fpx": { @@ -2097,6 +3353,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsFpx", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "giropay": { @@ -2106,6 +3367,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsGiropay", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "grabpay": { @@ -2115,6 +3381,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsGrabpay", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "ideal": { @@ -2124,6 +3395,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsIdeal", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "klarna": { @@ -2133,6 +3409,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsKlarna", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "konbini": { @@ -2142,6 +3423,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsKonbini", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "link": { @@ -2151,6 +3437,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsLink", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "oxxo": { @@ -2160,6 +3451,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsOxxo", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "p24": { @@ -2169,6 +3465,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsP24", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "paynow": { @@ -2178,6 +3479,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsPaynow", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "paypal": { @@ -2187,6 +3493,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsPaypal", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "pix": { @@ -2196,6 +3507,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsPix", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "revolut_pay": { @@ -2205,6 +3521,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsRevolutPay", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "sepa_debit": { @@ -2214,6 +3535,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsSepaDebit", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "sofort": { @@ -2223,6 +3549,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsSofort", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "swish": { @@ -2232,6 +3563,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsSwish", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "us_bank_account": { @@ -2241,6 +3577,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsUsBankAccount", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "wechat_pay": { @@ -2250,6 +3591,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsWechatPay", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -2263,6 +3609,11 @@ "name": "PaymentMethodAffirm", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "currency": { @@ -2272,6 +3623,11 @@ "name": "CheckoutCurrency", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "mandate_options": { @@ -2281,6 +3637,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAcssDebitMandateOptions", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "setup_future_usage": { @@ -2290,6 +3651,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAcssDebitSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "verification_method": { @@ -2299,6 +3665,11 @@ "name": "CheckoutVerificationMethod", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2312,6 +3683,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "default_for": { @@ -2324,6 +3700,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "interval_description": { @@ -2333,6 +3719,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 500 } }, "payment_schedule": { @@ -2342,6 +3734,11 @@ "name": "CheckoutPaymentSchedule", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "transaction_type": { @@ -2351,6 +3748,11 @@ "name": "CheckoutTransactionType", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2364,6 +3766,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAffirmSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2377,6 +3784,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAfterpayClearpaySetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2390,6 +3802,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAlipaySetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2403,6 +3820,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsAuBecsDebitSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2416,6 +3838,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsBacsDebitSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2429,6 +3856,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsBancontactSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2442,6 +3874,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "setup_future_usage": { @@ -2451,6 +3888,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsBoletoSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2464,6 +3906,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCardInstallments", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "request_three_d_secure": { @@ -2473,6 +3920,11 @@ "name": "CheckoutRequestThreeDSecure", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "setup_future_usage": { @@ -2482,6 +3934,11 @@ "name": "CheckoutSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "statement_descriptor_suffix_kana": { @@ -2491,6 +3948,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 22 } }, "statement_descriptor_suffix_kanji": { @@ -2500,6 +3963,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 17 } } } @@ -2513,6 +3982,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } } } @@ -2526,6 +4000,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCashappSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2539,6 +4018,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCustomerBalanceBankTransfer", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "funding_type": { @@ -2548,6 +4032,11 @@ "name": "CheckoutFundingType", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "setup_future_usage": { @@ -2557,6 +4046,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCustomerBalanceSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2570,6 +4064,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCustomerBalanceBankTransferEuBankTransfer", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "requested_address_types": { @@ -2582,12 +4081,27 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "type": { "type": { "name": "PostCheckoutSessionsBodyPaymentMethodOptionsCustomerBalanceBankTransferType", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2598,6 +4112,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } } } @@ -2611,6 +4131,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsEpsSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2624,6 +4149,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsFpxSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2637,6 +4167,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsGiropaySetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2650,6 +4185,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsGrabpaySetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2663,6 +4203,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsIdealSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2676,6 +4221,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsKlarnaSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2689,6 +4239,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "setup_future_usage": { @@ -2698,6 +4253,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsKonbiniSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2711,6 +4271,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsLinkSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2724,6 +4289,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "setup_future_usage": { @@ -2733,6 +4303,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsOxxoSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2746,6 +4321,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsP24SetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "tos_shown_and_accepted": { @@ -2755,6 +4335,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } } } @@ -2768,6 +4353,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsPaynowSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2781,6 +4371,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsPaypalCaptureMethod", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "preferred_locale": { @@ -2790,6 +4385,11 @@ "name": "CheckoutPreferredLocale", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "reference": { @@ -2799,6 +4399,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 127 } }, "risk_correlation_id": { @@ -2808,6 +4414,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 32 } }, "setup_future_usage": { @@ -2817,6 +4429,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsPaypalSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2830,6 +4447,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -2843,6 +4465,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsRevolutPaySetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2856,6 +4483,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsSepaDebitSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2869,6 +4501,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsSofortSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2882,6 +4519,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } } } @@ -2895,6 +4538,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsUsBankAccountFinancialConnections", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "setup_future_usage": { @@ -2904,6 +4552,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsUsBankAccountSetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "verification_method": { @@ -2913,6 +4566,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsUsBankAccountVerificationMethod", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2929,6 +4587,17 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } } }, "prefetch": { @@ -2941,6 +4610,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } } } @@ -2954,12 +4633,23 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "client": { "type": { "name": "CheckoutClient", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "setup_future_usage": { @@ -2969,6 +4659,11 @@ "name": "PostCheckoutSessionsBodyPaymentMethodOptionsWechatPaySetupFutureUsage", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -2980,6 +4675,11 @@ "type": { "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } } } @@ -2994,6 +4694,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 1000 } }, "metadata": { @@ -3003,6 +4709,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "on_behalf_of": { @@ -3012,6 +4723,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -3026,6 +4742,16 @@ "type": "named" }, "type": "array" + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } } } @@ -3039,6 +4765,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "shipping_rate_data": { @@ -3048,6 +4780,11 @@ "name": "PostCheckoutSessionsBodyShippingOptionsShippingRateData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -3061,12 +4798,23 @@ "name": "PostCheckoutSessionsBodyShippingOptionsShippingRateDataDeliveryEstimate", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "display_name": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 100 } }, "fixed_amount": { @@ -3076,6 +4824,11 @@ "name": "PostCheckoutSessionsBodyShippingOptionsShippingRateDataFixedAmount", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "metadata": { @@ -3085,6 +4838,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "tax_behavior": { @@ -3094,6 +4852,11 @@ "name": "CheckoutTaxBehavior", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "tax_code": { @@ -3103,6 +4866,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "type": { @@ -3112,6 +4880,11 @@ "name": "PostCheckoutSessionsBodyShippingOptionsShippingRateDataType", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -3125,6 +4898,11 @@ "name": "PostCheckoutSessionsBodyShippingOptionsShippingRateDataDeliveryEstimateMaximum", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "minimum": { @@ -3134,6 +4912,11 @@ "name": "PostCheckoutSessionsBodyShippingOptionsShippingRateDataDeliveryEstimateMinimum", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -3144,12 +4927,22 @@ "type": { "name": "CheckoutUnit", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -3160,12 +4953,22 @@ "type": { "name": "CheckoutUnit", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -3176,12 +4979,22 @@ "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } }, "currency": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "currency_options": { @@ -3191,6 +5004,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -3205,6 +5023,11 @@ "name": "Float64", "type": "named" } + }, + "rest": { + "type": [ + "number" + ] } }, "billing_cycle_anchor": { @@ -3214,6 +5037,12 @@ "name": "UnixTime", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } }, "default_tax_rates": { @@ -3226,6 +5055,17 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } } }, "description": { @@ -3235,6 +5075,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 500 } }, "invoice_settings": { @@ -3244,6 +5090,11 @@ "name": "PostCheckoutSessionsBodySubscriptionDataInvoiceSettings", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "metadata": { @@ -3253,6 +5104,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "on_behalf_of": { @@ -3262,6 +5118,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "proration_behavior": { @@ -3271,6 +5132,11 @@ "name": "CheckoutProrationBehavior", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "transfer_data": { @@ -3280,6 +5146,11 @@ "name": "PostCheckoutSessionsBodySubscriptionDataTransferData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "trial_end": { @@ -3289,6 +5160,12 @@ "name": "UnixTime", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } }, "trial_period_days": { @@ -3298,6 +5175,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "trial_settings": { @@ -3307,6 +5189,11 @@ "name": "PostCheckoutSessionsBodySubscriptionDataTrialSettings", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -3320,75 +5207,249 @@ "name": "PostCheckoutSessionsBodySubscriptionDataInvoiceSettingsIssuer", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] + } + } + } + }, + "PostCheckoutSessionsBodySubscriptionDataInvoiceSettingsIssuer": { + "fields": { + "account": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "String", + "type": "named" + } + }, + "rest": { + "type": [ + "string" + ] + } + }, + "type": { + "type": { + "name": "CheckoutType", + "type": "named" + }, + "rest": { + "type": [ + "string" + ] + } + } + } + }, + "PostCheckoutSessionsBodySubscriptionDataTransferData": { + "fields": { + "amount_percent": { + "type": { + "type": "nullable", + "underlying_type": { + "name": "Float64", + "type": "named" + } + }, + "rest": { + "type": [ + "number" + ] + } + }, + "destination": { + "type": { + "name": "String", + "type": "named" + }, + "rest": { + "type": [ + "string" + ] + } + } + } + }, + "PostCheckoutSessionsBodySubscriptionDataTrialSettings": { + "fields": { + "end_behavior": { + "type": { + "name": "PostCheckoutSessionsBodySubscriptionDataTrialSettingsEndBehavior", + "type": "named" + }, + "rest": { + "type": [ + "object" + ] + } + } + } + }, + "PostCheckoutSessionsBodySubscriptionDataTrialSettingsEndBehavior": { + "fields": { + "missing_payment_method": { + "type": { + "name": "CheckoutMissingPaymentMethod", + "type": "named" + }, + "rest": { + "type": [ + "string" + ] + } + } + } + }, + "PostCheckoutSessionsBodyTaxIdCollection": { + "description": "Controls tax ID collection settings for the session.", + "fields": { + "enabled": { + "type": { + "name": "Boolean", + "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } } } }, - "PostCheckoutSessionsBodySubscriptionDataInvoiceSettingsIssuer": { + "PostFilesBody": { "fields": { - "account": { + "expand": { + "description": "Specifies which fields in the response should be expanded.", "type": { "type": "nullable", "underlying_type": { - "name": "String", - "type": "named" + "element_type": { + "name": "String", + "type": "named" + }, + "type": "array" + } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 } } }, - "type": { + "expand_json": { "type": { - "name": "CheckoutType", + "type": "nullable", + "underlying_type": { + "element_type": { + "name": "String", + "type": "named" + }, + "type": "array" + } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } + } + }, + "file": { + "description": "A file to upload. Make sure that the specifications follow RFC 2388, which defines file transfers for the `multipart/form-data` protocol.", + "type": { + "name": "Binary", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "format": "binary" } - } - } - }, - "PostCheckoutSessionsBodySubscriptionDataTransferData": { - "fields": { - "amount_percent": { + }, + "file_link_data": { + "description": "Optional parameters that automatically create a [file link](https://stripe.com/docs/api#file_links) for the newly created file.", "type": { "type": "nullable", "underlying_type": { - "name": "Float64", + "name": "PostFilesBodyFileLinkData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, - "destination": { + "purpose": { + "description": "The [purpose](https://stripe.com/docs/file-upload#uploading-a-file) of the uploaded file.", "type": { - "name": "String", + "name": "FilesPurpose", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } }, - "PostCheckoutSessionsBodySubscriptionDataTrialSettings": { + "PostFilesBodyFileLinkData": { + "description": "Optional parameters that automatically create a [file link](https://stripe.com/docs/api#file_links) for the newly created file.", "fields": { - "end_behavior": { + "create": { "type": { - "name": "PostCheckoutSessionsBodySubscriptionDataTrialSettingsEndBehavior", + "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } - } - } - }, - "PostCheckoutSessionsBodySubscriptionDataTrialSettingsEndBehavior": { - "fields": { - "missing_payment_method": { + }, + "expires_at": { "type": { - "name": "CheckoutMissingPaymentMethod", - "type": "named" + "type": "nullable", + "underlying_type": { + "name": "UnixTime", + "type": "named" + } + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } - } - } - }, - "PostCheckoutSessionsBodyTaxIdCollection": { - "description": "Controls tax ID collection settings for the session.", - "fields": { - "enabled": { + }, + "metadata": { "type": { - "name": "Boolean", - "type": "named" + "type": "nullable", + "underlying_type": { + "name": "JSON", + "type": "named" + } + }, + "rest": { + "type": null } } } @@ -3406,6 +5467,17 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ], + "maxLength": 5000 + } } }, "failure_details": { @@ -3416,6 +5488,11 @@ "name": "PostTestHelpersTreasuryInboundTransfersIdFailBodyFailureDetails", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -3430,6 +5507,11 @@ "name": "TestHelpersCode", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -3443,6 +5525,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "id": { @@ -3452,6 +5539,11 @@ "name": "SnakeObjectId", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -3465,6 +5557,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "id": { @@ -3474,6 +5571,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "petId": { @@ -3483,6 +5586,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "quantity": { @@ -3492,6 +5601,12 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int32" } }, "shipDate": { @@ -3501,6 +5616,12 @@ "name": "TimestampTZ", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "date-time" } }, "status": { @@ -3511,6 +5632,11 @@ "name": "SnakeObjectIdStatus", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -3524,6 +5650,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "name": { @@ -3533,6 +5665,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -3545,6 +5682,11 @@ "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } }, "cancelable": { @@ -3552,6 +5694,11 @@ "type": { "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } }, "context": { @@ -3561,6 +5708,9 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": null } }, "created": { @@ -3568,6 +5718,12 @@ "type": { "name": "UnixTime", "type": "named" + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } }, "currency": { @@ -3575,6 +5731,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "description": { @@ -3585,6 +5746,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "failure_details": { @@ -3595,6 +5762,11 @@ "name": "TreasuryInboundTransfersResourceFailureDetails", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "financial_account": { @@ -3602,6 +5774,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "hosted_regulatory_receipt_url": { @@ -3612,6 +5790,12 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "id": { @@ -3619,6 +5803,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "livemode": { @@ -3626,6 +5816,11 @@ "type": { "name": "Boolean", "type": "named" + }, + "rest": { + "type": [ + "boolean" + ] } }, "metadata": { @@ -3633,6 +5828,11 @@ "type": { "name": "JSON", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } }, "object": { @@ -3640,6 +5840,11 @@ "type": { "name": "TreasuryInboundTransferObject", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "origin_payment_method": { @@ -3647,6 +5852,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "returned": { @@ -3657,6 +5868,11 @@ "name": "Boolean", "type": "named" } + }, + "rest": { + "type": [ + "boolean" + ] } }, "statement_descriptor": { @@ -3664,6 +5880,12 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ], + "maxLength": 5000 } }, "status": { @@ -3671,12 +5893,22 @@ "type": { "name": "TreasuryInboundTransferStatus", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "status_transitions": { "type": { "name": "TreasuryInboundTransfersResourceInboundTransferResourceStatusTransitions", "type": "named" + }, + "rest": { + "type": [ + "object" + ] } }, "transaction": { @@ -3687,6 +5919,9 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": null } } } @@ -3698,6 +5933,11 @@ "type": { "name": "TreasuryInboundTransfersResourceFailureDetailsCode", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } } } @@ -3712,6 +5952,12 @@ "name": "UnixTime", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } }, "failed_at": { @@ -3722,6 +5968,12 @@ "name": "UnixTime", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } }, "succeeded_at": { @@ -3732,6 +5984,12 @@ "name": "UnixTime", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "unix-time" } } } @@ -3745,6 +6003,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "addresses": { @@ -3757,6 +6020,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "children": { @@ -3769,6 +6037,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "id": { @@ -3778,6 +6056,12 @@ "name": "UUID", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "uuid" } }, "profileImage": { @@ -3787,6 +6071,12 @@ "name": "Binary", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "binary" } } } @@ -3800,6 +6090,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "firstName": { @@ -3809,6 +6104,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "id": { @@ -3818,6 +6118,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "lastName": { @@ -3827,6 +6133,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "password": { @@ -3836,6 +6147,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "phone": { @@ -3845,6 +6161,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "userStatus": { @@ -3855,6 +6176,12 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int32" } }, "username": { @@ -3864,6 +6191,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -3883,10 +6215,7 @@ } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Pet" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -3898,6 +6227,9 @@ "type": { "name": "Pet", "type": "named" + }, + "rest": { + "in": "body" } } }, @@ -3921,10 +6253,7 @@ } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Pet" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -3936,6 +6265,9 @@ "type": { "name": "Pet", "type": "named" + }, + "rest": { + "in": "body" } } }, @@ -3950,31 +6282,6 @@ "request": { "url": "/pet/{petId}", "method": "post", - "parameters": [ - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - }, - { - "name": "name", - "in": "query", - "schema": { - "type": "String", - "nullable": true - } - }, - { - "name": "status", - "in": "query", - "schema": { - "type": "String", - "nullable": true - } - } - ], "security": [ { "petstore_auth": [ @@ -3996,6 +6303,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "name", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "petId": { @@ -4003,6 +6319,16 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "format": "int64" + } } }, "status": { @@ -4013,6 +6339,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "status", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -4030,23 +6365,6 @@ "request": { "url": "/pet/{petId}", "method": "delete", - "parameters": [ - { - "name": "api_key", - "in": "header", - "schema": { - "type": "String", - "nullable": true - } - }, - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "security": [ { "petstore_auth": [ @@ -4067,6 +6385,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "api_key", + "in": "header", + "schema": { + "type": [ + "string" + ] + } } }, "petId": { @@ -4074,6 +6401,16 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "format": "int64" + } } } }, @@ -4091,23 +6428,6 @@ "request": { "url": "/pet/{petId}/uploadImage", "method": "post", - "parameters": [ - { - "name": "petId", - "in": "path", - "schema": { - "type": "Int64" - } - }, - { - "name": "additionalMetadata", - "in": "query", - "schema": { - "type": "String", - "nullable": true - } - } - ], "security": [ { "petstore_auth": [ @@ -4117,11 +6437,7 @@ } ], "requestBody": { - "contentType": "application/octet-stream", - "schema": { - "type": "Binary", - "nullable": true - } + "contentType": "application/octet-stream" }, "response": { "contentType": "application/json" @@ -4136,6 +6452,15 @@ "name": "String", "type": "named" } + }, + "rest": { + "name": "additionalMetadata", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "body": { @@ -4146,6 +6471,9 @@ "name": "Binary", "type": "named" } + }, + "rest": { + "in": "body" } }, "petId": { @@ -4153,6 +6481,16 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "petId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "format": "int64" + } } } }, @@ -4169,35 +6507,6 @@ "method": "post", "requestBody": { "contentType": "multipart/form-data", - "schema": { - "type": "object", - "nullable": true, - "properties": { - "address": { - "type": "JSON", - "nullable": true - }, - "addresses": { - "type": "array", - "nullable": true - }, - "children": { - "type": "array", - "nullable": true, - "items": { - "type": "String" - } - }, - "id": { - "type": "UUID", - "nullable": true - }, - "profileImage": { - "type": "Binary", - "nullable": true - } - } - }, "encoding": { "profileImage": { "contentType": [ @@ -4209,7 +6518,9 @@ "explode": false, "argumentName": "headerXRateLimitLimit", "schema": { - "type": "Int32" + "type": [ + "integer" + ] } } } @@ -4229,6 +6540,9 @@ "name": "UploadPetMultipartBody", "type": "named" } + }, + "rest": { + "in": "body" } }, "headerXRateLimitLimit": { @@ -4236,6 +6550,15 @@ "type": { "name": "Int32", "type": "named" + }, + "rest": { + "explode": false, + "argumentName": "headerXRateLimitLimit", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -4250,11 +6573,7 @@ "url": "/store/order", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Order", - "nullable": true - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -4269,6 +6588,9 @@ "name": "Order", "type": "named" } + }, + "rest": { + "in": "body" } } }, @@ -4283,15 +6605,6 @@ "request": { "url": "/store/order/{orderId}", "method": "delete", - "parameters": [ - { - "name": "orderId", - "in": "path", - "schema": { - "type": "Int64" - } - } - ], "response": { "contentType": "application/json" } @@ -4302,6 +6615,16 @@ "type": { "name": "Int64", "type": "named" + }, + "rest": { + "name": "orderId", + "in": "path", + "schema": { + "type": [ + "integer" + ], + "format": "int64" + } } } }, @@ -4320,11 +6643,7 @@ "url": "/user/createWithList", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "array", - "nullable": true - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -4342,6 +6661,9 @@ }, "type": "array" } + }, + "rest": { + "in": "body" } } }, @@ -4356,15 +6678,6 @@ "request": { "url": "/user/{username}", "method": "delete", - "parameters": [ - { - "name": "username", - "in": "path", - "schema": { - "type": "String" - } - } - ], "response": { "contentType": "application/json" } @@ -4375,6 +6688,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "username", + "in": "path", + "schema": { + "type": [ + "string" + ] + } } } }, @@ -4408,43 +6730,8 @@ "request": { "url": "/test_helpers/treasury/inbound_transfers/{id}/fail", "method": "post", - "parameters": [ - { - "style": "simple", - "name": "id", - "in": "path", - "schema": { - "type": "String", - "maxLength": 5000 - } - } - ], "requestBody": { "contentType": "application/x-www-form-urlencoded", - "schema": { - "type": "object", - "nullable": true, - "properties": { - "expand": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "failure_details": { - "type": "object", - "nullable": true, - "properties": { - "code": { - "type": "TestHelpersCode", - "nullable": true - } - } - } - } - }, "encoding": { "expand": { "style": "deepObject", @@ -4469,12 +6756,26 @@ "name": "PostTestHelpersTreasuryInboundTransfersIdFailBody", "type": "named" } + }, + "rest": { + "in": "body" } }, "id": { "type": { "name": "String", "type": "named" + }, + "rest": { + "style": "simple", + "name": "id", + "in": "path", + "schema": { + "type": [ + "string" + ], + "maxLength": 5000 + } } } }, @@ -4488,17 +6789,6 @@ "request": { "url": "/v1/accounts/{account}", "method": "delete", - "parameters": [ - { - "style": "simple", - "name": "account", - "in": "path", - "schema": { - "type": "String", - "maxLength": 5000 - } - } - ], "response": { "contentType": "application/json" } @@ -4508,6 +6798,17 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "style": "simple", + "name": "account", + "in": "path", + "schema": { + "type": [ + "string" + ], + "maxLength": 5000 + } } } }, @@ -4523,1355 +6824,6 @@ "method": "post", "requestBody": { "contentType": "application/x-www-form-urlencoded", - "schema": { - "type": "object", - "nullable": true, - "properties": { - "after_expiration": { - "type": "object", - "nullable": true, - "properties": { - "recovery": { - "type": "object", - "nullable": true, - "properties": { - "allow_promotion_codes": { - "type": "Boolean", - "nullable": true - }, - "enabled": { - "type": "Boolean" - } - } - } - } - }, - "allow_promotion_codes": { - "type": "Boolean", - "nullable": true - }, - "automatic_tax": { - "type": "object", - "nullable": true, - "properties": { - "enabled": { - "type": "Boolean" - }, - "liability": { - "type": "object", - "nullable": true, - "properties": { - "account": { - "type": "String", - "nullable": true - }, - "type": { - "type": "CheckoutType" - } - } - } - } - }, - "billing_address_collection": { - "type": "CheckoutBillingAddressCollection", - "nullable": true - }, - "cancel_url": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "client_reference_id": { - "type": "String", - "nullable": true, - "maxLength": 200 - }, - "consent_collection": { - "type": "object", - "nullable": true, - "properties": { - "payment_method_reuse_agreement": { - "type": "object", - "nullable": true, - "properties": { - "position": { - "type": "CheckoutPosition" - } - } - }, - "promotions": { - "type": "CheckoutPromotions", - "nullable": true - }, - "terms_of_service": { - "type": "CheckoutTermsOfService", - "nullable": true - } - } - }, - "currency": { - "type": "String", - "nullable": true - }, - "custom_fields": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "dropdown": { - "type": "object", - "nullable": true, - "properties": { - "options": { - "type": "array", - "items": { - "type": "object", - "properties": { - "label": { - "type": "String", - "maxLength": 100 - }, - "value": { - "type": "String", - "maxLength": 100 - } - } - } - } - } - }, - "key": { - "type": "String", - "maxLength": 200 - }, - "label": { - "type": "object", - "properties": { - "custom": { - "type": "String", - "maxLength": 50 - }, - "type": { - "type": "PostCheckoutSessionsBodyCustomFieldsLabelType" - } - } - }, - "numeric": { - "type": "object", - "nullable": true, - "properties": { - "maximum_length": { - "type": "Int32", - "nullable": true - }, - "minimum_length": { - "type": "Int32", - "nullable": true - } - } - }, - "optional": { - "type": "Boolean", - "nullable": true - }, - "text": { - "type": "object", - "nullable": true, - "properties": { - "maximum_length": { - "type": "Int32", - "nullable": true - }, - "minimum_length": { - "type": "Int32", - "nullable": true - } - } - }, - "type": { - "type": "PostCheckoutSessionsBodyCustomFieldsType" - } - } - } - }, - "custom_text": { - "type": "object", - "nullable": true, - "properties": { - "after_submit": { - "type": "object", - "nullable": true, - "properties": { - "message": { - "type": "String", - "maxLength": 1200 - } - } - }, - "shipping_address": { - "type": "object", - "nullable": true, - "properties": { - "message": { - "type": "String", - "maxLength": 1200 - } - } - }, - "submit": { - "type": "object", - "nullable": true, - "properties": { - "message": { - "type": "String", - "maxLength": 1200 - } - } - }, - "terms_of_service_acceptance": { - "type": "object", - "nullable": true, - "properties": { - "message": { - "type": "String", - "maxLength": 1200 - } - } - } - } - }, - "customer": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "customer_creation": { - "type": "CheckoutCustomerCreation", - "nullable": true - }, - "customer_email": { - "type": "String", - "nullable": true - }, - "customer_update": { - "type": "object", - "nullable": true, - "properties": { - "address": { - "type": "CheckoutAddress", - "nullable": true - }, - "name": { - "type": "CheckoutName", - "nullable": true - }, - "shipping": { - "type": "CheckoutShipping", - "nullable": true - } - } - }, - "discounts": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "coupon": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "promotion_code": { - "type": "String", - "nullable": true, - "maxLength": 5000 - } - } - } - }, - "expand": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "expires_at": { - "type": "UnixTime", - "nullable": true - }, - "invoice_creation": { - "type": "object", - "nullable": true, - "properties": { - "enabled": { - "type": "Boolean" - }, - "invoice_data": { - "type": "object", - "nullable": true, - "properties": { - "account_tax_ids": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "custom_fields": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "name": { - "type": "String", - "maxLength": 40 - }, - "value": { - "type": "String", - "maxLength": 140 - } - } - } - }, - "description": { - "type": "String", - "nullable": true, - "maxLength": 1500 - }, - "footer": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "issuer": { - "type": "object", - "nullable": true, - "properties": { - "account": { - "type": "String", - "nullable": true - }, - "type": { - "type": "CheckoutType" - } - } - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "rendering_options": { - "type": "object", - "nullable": true, - "properties": { - "amount_tax_display": { - "type": "CheckoutAmountTaxDisplay", - "nullable": true - } - } - } - } - } - } - }, - "line_items": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "adjustable_quantity": { - "type": "object", - "nullable": true, - "properties": { - "enabled": { - "type": "Boolean" - }, - "maximum": { - "type": "Int32", - "nullable": true - }, - "minimum": { - "type": "Int32", - "nullable": true - } - } - }, - "dynamic_tax_rates": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "price": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "price_data": { - "type": "object", - "nullable": true, - "properties": { - "currency": { - "type": "String" - }, - "product": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "product_data": { - "type": "object", - "nullable": true, - "properties": { - "description": { - "type": "String", - "nullable": true, - "maxLength": 40000 - }, - "images": { - "type": "array", - "nullable": true, - "items": { - "type": "String" - } - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "name": { - "type": "String", - "maxLength": 5000 - }, - "tax_code": { - "type": "String", - "nullable": true, - "maxLength": 5000 - } - } - }, - "recurring": { - "type": "object", - "nullable": true, - "properties": { - "interval": { - "type": "CheckoutInterval" - }, - "interval_count": { - "type": "Int32", - "nullable": true - } - } - }, - "tax_behavior": { - "type": "CheckoutTaxBehavior", - "nullable": true - }, - "unit_amount": { - "type": "Int32", - "nullable": true - }, - "unit_amount_decimal": { - "type": "String", - "nullable": true - } - } - }, - "quantity": { - "type": "Int32", - "nullable": true - }, - "tax_rates": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - } - } - } - }, - "locale": { - "type": "CheckoutLocale", - "nullable": true - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "mode": { - "type": "CheckoutMode", - "nullable": true - }, - "payment_intent_data": { - "type": "object", - "nullable": true, - "properties": { - "application_fee_amount": { - "type": "Int32", - "nullable": true - }, - "capture_method": { - "type": "CheckoutCaptureMethod", - "nullable": true - }, - "description": { - "type": "String", - "nullable": true, - "maxLength": 1000 - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "on_behalf_of": { - "type": "String", - "nullable": true - }, - "receipt_email": { - "type": "String", - "nullable": true - }, - "setup_future_usage": { - "type": "CheckoutSetupFutureUsage", - "nullable": true - }, - "shipping": { - "type": "object", - "nullable": true, - "properties": { - "address": { - "type": "object", - "properties": { - "city": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "country": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "line1": { - "type": "String", - "maxLength": 5000 - }, - "line2": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "postal_code": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "state": { - "type": "String", - "nullable": true, - "maxLength": 5000 - } - } - }, - "carrier": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "name": { - "type": "String", - "maxLength": 5000 - }, - "phone": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "tracking_number": { - "type": "String", - "nullable": true, - "maxLength": 5000 - } - } - }, - "statement_descriptor": { - "type": "String", - "nullable": true, - "maxLength": 22 - }, - "statement_descriptor_suffix": { - "type": "String", - "nullable": true, - "maxLength": 22 - }, - "transfer_data": { - "type": "object", - "nullable": true, - "properties": { - "amount": { - "type": "Int32", - "nullable": true - }, - "destination": { - "type": "String" - } - } - }, - "transfer_group": { - "type": "String", - "nullable": true - } - } - }, - "payment_method_collection": { - "type": "CheckoutPaymentMethodCollection", - "nullable": true - }, - "payment_method_configuration": { - "type": "String", - "nullable": true, - "maxLength": 100 - }, - "payment_method_options": { - "type": "object", - "nullable": true, - "properties": { - "acss_debit": { - "type": "object", - "nullable": true, - "properties": { - "affirm": { - "type": "PaymentMethodAffirm", - "nullable": true - }, - "currency": { - "type": "CheckoutCurrency", - "nullable": true - }, - "mandate_options": { - "type": "object", - "nullable": true, - "properties": { - "custom_mandate_url": { - "type": "String", - "nullable": true - }, - "default_for": { - "type": "array", - "nullable": true, - "items": { - "type": "CheckoutDefaultFor" - } - }, - "interval_description": { - "type": "String", - "nullable": true, - "maxLength": 500 - }, - "payment_schedule": { - "type": "CheckoutPaymentSchedule", - "nullable": true - }, - "transaction_type": { - "type": "CheckoutTransactionType", - "nullable": true - } - } - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsAcssDebitSetupFutureUsage", - "nullable": true - }, - "verification_method": { - "type": "CheckoutVerificationMethod", - "nullable": true - } - } - }, - "affirm": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsAffirmSetupFutureUsage", - "nullable": true - } - } - }, - "afterpay_clearpay": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsAfterpayClearpaySetupFutureUsage", - "nullable": true - } - } - }, - "alipay": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsAlipaySetupFutureUsage", - "nullable": true - } - } - }, - "au_becs_debit": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsAuBecsDebitSetupFutureUsage", - "nullable": true - } - } - }, - "bacs_debit": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsBacsDebitSetupFutureUsage", - "nullable": true - } - } - }, - "bancontact": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsBancontactSetupFutureUsage", - "nullable": true - } - } - }, - "boleto": { - "type": "object", - "nullable": true, - "properties": { - "expires_after_days": { - "type": "Int32", - "nullable": true - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsBoletoSetupFutureUsage", - "nullable": true - } - } - }, - "card": { - "type": "object", - "nullable": true, - "properties": { - "installments": { - "type": "object", - "nullable": true, - "properties": { - "enabled": { - "type": "Boolean", - "nullable": true - } - } - }, - "request_three_d_secure": { - "type": "CheckoutRequestThreeDSecure", - "nullable": true - }, - "setup_future_usage": { - "type": "CheckoutSetupFutureUsage", - "nullable": true - }, - "statement_descriptor_suffix_kana": { - "type": "String", - "nullable": true, - "maxLength": 22 - }, - "statement_descriptor_suffix_kanji": { - "type": "String", - "nullable": true, - "maxLength": 17 - } - } - }, - "cashapp": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsCashappSetupFutureUsage", - "nullable": true - } - } - }, - "customer_balance": { - "type": "object", - "nullable": true, - "properties": { - "bank_transfer": { - "type": "object", - "nullable": true, - "properties": { - "eu_bank_transfer": { - "type": "object", - "nullable": true, - "properties": { - "country": { - "type": "String", - "maxLength": 5000 - } - } - }, - "requested_address_types": { - "type": "array", - "nullable": true, - "items": { - "type": "CheckoutRequestedAddressTypes" - } - }, - "type": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsCustomerBalanceBankTransferType" - } - } - }, - "funding_type": { - "type": "CheckoutFundingType", - "nullable": true - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsCustomerBalanceSetupFutureUsage", - "nullable": true - } - } - }, - "eps": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsEpsSetupFutureUsage", - "nullable": true - } - } - }, - "fpx": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsFpxSetupFutureUsage", - "nullable": true - } - } - }, - "giropay": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsGiropaySetupFutureUsage", - "nullable": true - } - } - }, - "grabpay": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsGrabpaySetupFutureUsage", - "nullable": true - } - } - }, - "ideal": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsIdealSetupFutureUsage", - "nullable": true - } - } - }, - "klarna": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsKlarnaSetupFutureUsage", - "nullable": true - } - } - }, - "konbini": { - "type": "object", - "nullable": true, - "properties": { - "expires_after_days": { - "type": "Int32", - "nullable": true - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsKonbiniSetupFutureUsage", - "nullable": true - } - } - }, - "link": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsLinkSetupFutureUsage", - "nullable": true - } - } - }, - "oxxo": { - "type": "object", - "nullable": true, - "properties": { - "expires_after_days": { - "type": "Int32", - "nullable": true - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsOxxoSetupFutureUsage", - "nullable": true - } - } - }, - "p24": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsP24SetupFutureUsage", - "nullable": true - }, - "tos_shown_and_accepted": { - "type": "Boolean", - "nullable": true - } - } - }, - "paynow": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsPaynowSetupFutureUsage", - "nullable": true - } - } - }, - "paypal": { - "type": "object", - "nullable": true, - "properties": { - "capture_method": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsPaypalCaptureMethod", - "nullable": true - }, - "preferred_locale": { - "type": "CheckoutPreferredLocale", - "nullable": true - }, - "reference": { - "type": "String", - "nullable": true, - "maxLength": 127 - }, - "risk_correlation_id": { - "type": "String", - "nullable": true, - "maxLength": 32 - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsPaypalSetupFutureUsage", - "nullable": true - } - } - }, - "pix": { - "type": "object", - "nullable": true, - "properties": { - "expires_after_seconds": { - "type": "Int32", - "nullable": true - } - } - }, - "revolut_pay": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsRevolutPaySetupFutureUsage", - "nullable": true - } - } - }, - "sepa_debit": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsSepaDebitSetupFutureUsage", - "nullable": true - } - } - }, - "sofort": { - "type": "object", - "nullable": true, - "properties": { - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsSofortSetupFutureUsage", - "nullable": true - } - } - }, - "swish": { - "type": "object", - "nullable": true, - "properties": { - "reference": { - "type": "String", - "nullable": true, - "maxLength": 5000 - } - } - }, - "us_bank_account": { - "type": "object", - "nullable": true, - "properties": { - "financial_connections": { - "type": "object", - "nullable": true, - "properties": { - "permissions": { - "type": "array", - "nullable": true, - "items": { - "type": "CheckoutPermissions", - "maxLength": 5000 - } - }, - "prefetch": { - "type": "array", - "nullable": true, - "items": { - "type": "CheckoutPrefetch" - } - } - } - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsUsBankAccountSetupFutureUsage", - "nullable": true - }, - "verification_method": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsUsBankAccountVerificationMethod", - "nullable": true - } - } - }, - "wechat_pay": { - "type": "object", - "nullable": true, - "properties": { - "app_id": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "client": { - "type": "CheckoutClient" - }, - "setup_future_usage": { - "type": "PostCheckoutSessionsBodyPaymentMethodOptionsWechatPaySetupFutureUsage", - "nullable": true - } - } - } - } - }, - "payment_method_types": { - "type": "array", - "nullable": true, - "items": { - "type": "CheckoutPaymentMethodTypes" - } - }, - "phone_number_collection": { - "type": "object", - "nullable": true, - "properties": { - "enabled": { - "type": "Boolean" - } - } - }, - "redirect_on_completion": { - "type": "CheckoutRedirectOnCompletion", - "nullable": true - }, - "return_url": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "setup_intent_data": { - "type": "object", - "nullable": true, - "properties": { - "description": { - "type": "String", - "nullable": true, - "maxLength": 1000 - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "on_behalf_of": { - "type": "String", - "nullable": true - } - } - }, - "shipping_address_collection": { - "type": "object", - "nullable": true, - "properties": { - "allowed_countries": { - "type": "array", - "items": { - "type": "CheckoutAllowedCountries" - } - } - } - }, - "shipping_options": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "shipping_rate": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "shipping_rate_data": { - "type": "object", - "nullable": true, - "properties": { - "delivery_estimate": { - "type": "object", - "nullable": true, - "properties": { - "maximum": { - "type": "object", - "nullable": true, - "properties": { - "unit": { - "type": "CheckoutUnit" - }, - "value": { - "type": "Int32" - } - } - }, - "minimum": { - "type": "object", - "nullable": true, - "properties": { - "unit": { - "type": "CheckoutUnit" - }, - "value": { - "type": "Int32" - } - } - } - } - }, - "display_name": { - "type": "String", - "maxLength": 100 - }, - "fixed_amount": { - "type": "object", - "nullable": true, - "properties": { - "amount": { - "type": "Int32" - }, - "currency": { - "type": "String" - }, - "currency_options": { - "type": "JSON", - "nullable": true - } - } - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "tax_behavior": { - "type": "CheckoutTaxBehavior", - "nullable": true - }, - "tax_code": { - "type": "String", - "nullable": true - }, - "type": { - "type": "PostCheckoutSessionsBodyShippingOptionsShippingRateDataType", - "nullable": true - } - } - } - } - } - }, - "submit_type": { - "type": "CheckoutSubmitType", - "nullable": true - }, - "subscription_data": { - "type": "object", - "nullable": true, - "properties": { - "application_fee_percent": { - "type": "Float64", - "nullable": true - }, - "billing_cycle_anchor": { - "type": "UnixTime", - "nullable": true - }, - "default_tax_rates": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "description": { - "type": "String", - "nullable": true, - "maxLength": 500 - }, - "invoice_settings": { - "type": "object", - "nullable": true, - "properties": { - "issuer": { - "type": "object", - "nullable": true, - "properties": { - "account": { - "type": "String", - "nullable": true - }, - "type": { - "type": "CheckoutType" - } - } - } - } - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "on_behalf_of": { - "type": "String", - "nullable": true - }, - "proration_behavior": { - "type": "CheckoutProrationBehavior", - "nullable": true - }, - "transfer_data": { - "type": "object", - "nullable": true, - "properties": { - "amount_percent": { - "type": "Float64", - "nullable": true - }, - "destination": { - "type": "String" - } - } - }, - "trial_end": { - "type": "UnixTime", - "nullable": true - }, - "trial_period_days": { - "type": "Int32", - "nullable": true - }, - "trial_settings": { - "type": "object", - "nullable": true, - "properties": { - "end_behavior": { - "type": "object", - "properties": { - "missing_payment_method": { - "type": "CheckoutMissingPaymentMethod" - } - } - } - } - } - } - }, - "success_url": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "tax_id_collection": { - "type": "object", - "nullable": true, - "properties": { - "enabled": { - "type": "Boolean" - } - } - }, - "ui_mode": { - "type": "CheckoutUiMode", - "nullable": true - } - } - }, "encoding": { "after_expiration": { "style": "deepObject", @@ -5968,6 +6920,9 @@ "name": "PostCheckoutSessionsBody", "type": "named" } + }, + "rest": { + "in": "body" } } }, @@ -5979,13 +6934,87 @@ }, { "request": { - "url": "/fine_tuning/jobs", + "url": "/v1/files", "method": "post", + "servers": [ + { + "url": "{{PET_STORE_SERVER_URL:-https://files.stripe.com/}}" + } + ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "CreateFineTuningJobRequest" + "contentType": "multipart/form-data", + "encoding": { + "expand": { + "style": "deepObject", + "explode": true + }, + "expand_json": { + "contentType": [ + "application/json" + ] + }, + "file": { + "headers": { + "X-Rate-Limit-Limit": { + "explode": false, + "argumentName": "headerXRateLimitLimit", + "schema": { + "type": [ + "integer" + ] + } + } + } + }, + "file_link_data": { + "style": "deepObject", + "explode": true + } + } + }, + "response": { + "contentType": "application/json" + } + }, + "arguments": { + "body": { + "description": "Request body of POST /v1/files", + "type": { + "name": "PostFilesBody", + "type": "named" + }, + "rest": { + "in": "body" + } + }, + "headerXRateLimitLimit": { + "type": { + "name": "Int32", + "type": "named" + }, + "rest": { + "explode": false, + "argumentName": "headerXRateLimitLimit", + "schema": { + "type": [ + "integer" + ] + } } + } + }, + "name": "PostFiles", + "result_type": { + "name": "ApiResponse", + "type": "named" + } + }, + { + "request": { + "url": "/fine_tuning/jobs", + "method": "post", + "requestBody": { + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -5997,6 +7026,9 @@ "type": { "name": "CreateFineTuningJobRequest", "type": "named" + }, + "rest": { + "in": "body" } } }, @@ -6448,6 +7480,26 @@ "type": "enum" } }, + "FilesPurpose": { + "aggregate_functions": {}, + "comparison_operators": {}, + "representation": { + "one_of": [ + "account_requirement", + "additional_verification", + "business_icon", + "business_logo", + "customer_signature", + "dispute_evidence", + "identity_document", + "issuing_regulatory_reporting", + "pci_document", + "tax_document_user_upload", + "terminal_reader_splashscreen" + ], + "type": "enum" + } + }, "Float64": { "aggregate_functions": {}, "comparison_operators": {}, @@ -6469,6 +7521,41 @@ "type": "int64" } }, + "InvoicesCollectionMethod": { + "aggregate_functions": {}, + "comparison_operators": {}, + "representation": { + "one_of": [ + "charge_automatically", + "send_invoice" + ], + "type": "enum" + } + }, + "InvoicesObject": { + "aggregate_functions": {}, + "comparison_operators": {}, + "representation": { + "one_of": [ + "list" + ], + "type": "enum" + } + }, + "InvoicesStatus": { + "aggregate_functions": {}, + "comparison_operators": {}, + "representation": { + "one_of": [ + "draft", + "open", + "paid", + "uncollectible", + "void" + ], + "type": "enum" + } + }, "JSON": { "aggregate_functions": {}, "comparison_operators": {}, diff --git a/ndc-rest-schema/openapi/testdata/petstore3/source.json b/ndc-rest-schema/openapi/testdata/petstore3/source.json index 41280c3..5eb400d 100644 --- a/ndc-rest-schema/openapi/testdata/petstore3/source.json +++ b/ndc-rest-schema/openapi/testdata/petstore3/source.json @@ -2730,6 +2730,364 @@ } } }, + "/v1/invoices": { + "get": { + "description": "

You can list all invoices, or list the invoices for a specific customer. The invoices are returned sorted by creation date, with the most recently created invoices appearing first.

", + "operationId": "GetInvoices", + "parameters": [ + { + "description": "The collection method of the invoice to retrieve. Either `charge_automatically` or `send_invoice`.", + "in": "query", + "name": "collection_method", + "required": false, + "schema": { + "enum": ["charge_automatically", "send_invoice"], + "type": "string" + }, + "style": "form" + }, + { + "description": "Only return invoices that were created during the given date interval.", + "explode": true, + "in": "query", + "name": "created", + "required": false, + "schema": { + "anyOf": [ + { + "properties": { + "gt": { + "type": "integer" + }, + "gte": { + "type": "integer" + }, + "lt": { + "type": "integer" + }, + "lte": { + "type": "integer" + } + }, + "title": "range_query_specs", + "type": "object" + }, + { + "type": "integer" + } + ] + }, + "style": "deepObject" + }, + { + "description": "Only return invoices for the customer specified by this customer ID.", + "in": "query", + "name": "customer", + "required": false, + "schema": { + "maxLength": 5000, + "type": "string" + }, + "style": "form" + }, + { + "explode": true, + "in": "query", + "name": "due_date", + "required": false, + "schema": { + "anyOf": [ + { + "properties": { + "gt": { + "type": "integer" + }, + "gte": { + "type": "integer" + }, + "lt": { + "type": "integer" + }, + "lte": { + "type": "integer" + } + }, + "title": "range_query_specs", + "type": "object" + }, + { + "type": "integer" + } + ] + }, + "style": "deepObject" + }, + { + "description": "A cursor for use in pagination. `ending_before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with `obj_bar`, your subsequent call can include `ending_before=obj_bar` in order to fetch the previous page of the list.", + "in": "query", + "name": "ending_before", + "required": false, + "schema": { + "maxLength": 5000, + "type": "string" + }, + "style": "form" + }, + { + "description": "Specifies which fields in the response should be expanded.", + "explode": true, + "in": "query", + "name": "expand", + "required": false, + "schema": { + "items": { + "maxLength": 5000, + "type": "string" + }, + "type": "array" + }, + "style": "deepObject" + }, + { + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "type": "integer" + }, + "style": "form" + }, + { + "description": "A cursor for use in pagination. `starting_after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with `obj_foo`, your subsequent call can include `starting_after=obj_foo` in order to fetch the next page of the list.", + "in": "query", + "name": "starting_after", + "required": false, + "schema": { + "maxLength": 5000, + "type": "string" + }, + "style": "form" + }, + { + "description": "The status of the invoice, one of `draft`, `open`, `paid`, `uncollectible`, or `void`. [Learn more](https://stripe.com/docs/billing/invoices/workflow#workflow-overview)", + "in": "query", + "name": "status", + "required": false, + "schema": { + "enum": ["draft", "open", "paid", "uncollectible", "void"], + "type": "string", + "x-stripeBypassValidation": true + }, + "style": "form" + }, + { + "description": "Only return invoices for the subscription specified by this subscription ID.", + "in": "query", + "name": "subscription", + "required": false, + "schema": { + "maxLength": 5000, + "type": "string" + }, + "style": "form" + } + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "encoding": {}, + "schema": { + "additionalProperties": false, + "properties": {}, + "type": "object" + } + } + }, + "required": false + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "description": "", + "properties": { + "has_more": { + "description": "True if this list has another page of items after this one that can be fetched.", + "type": "boolean" + }, + "object": { + "description": "String representing the object's type. Objects of the same type share the same value. Always has the value `list`.", + "enum": ["list"], + "type": "string" + }, + "url": { + "description": "The URL where this list can be accessed.", + "maxLength": 5000, + "pattern": "^/v1/invoices", + "type": "string" + } + }, + "required": ["has_more", "object", "url"], + "title": "InvoicesResourceList", + "type": "object", + "x-expandableFields": ["data"] + } + } + }, + "description": "Successful response." + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + }, + "description": "Error response." + } + } + } + }, + "/v1/files": { + "post": { + "operationId": "PostFiles", + "requestBody": { + "content": { + "multipart/form-data": { + "encoding": { + "expand": { + "explode": true, + "style": "deepObject" + }, + "expand_json": { + "contentType": "application/json" + }, + "file": { + "headers": { + "X-Rate-Limit-Limit": { + "explode": false, + "argumentName": "headerXRateLimitLimit", + "schema": { + "type": ["integer"] + } + } + } + }, + "file_link_data": { + "explode": true, + "style": "deepObject" + } + }, + "schema": { + "additionalProperties": false, + "properties": { + "expand": { + "description": "Specifies which fields in the response should be expanded.", + "items": { + "maxLength": 5000, + "type": "string" + }, + "type": "array" + }, + "expand_json": { + "items": { + "maxLength": 5000, + "type": "string" + }, + "type": "array" + }, + "file": { + "description": "A file to upload. Make sure that the specifications follow RFC 2388, which defines file transfers for the `multipart/form-data` protocol.", + "format": "binary", + "type": "string" + }, + "file_link_data": { + "description": "Optional parameters that automatically create a [file link](https://stripe.com/docs/api#file_links) for the newly created file.", + "properties": { + "create": { + "type": "boolean" + }, + "expires_at": { + "format": "unix-time", + "type": "integer" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "enum": [""], + "type": "string" + } + ] + } + }, + "required": ["create"], + "title": "file_link_creation_params", + "type": "object" + }, + "purpose": { + "description": "The [purpose](https://stripe.com/docs/file-upload#uploading-a-file) of the uploaded file.", + "enum": [ + "account_requirement", + "additional_verification", + "business_icon", + "business_logo", + "customer_signature", + "dispute_evidence", + "identity_document", + "issuing_regulatory_reporting", + "pci_document", + "tax_document_user_upload", + "terminal_reader_splashscreen" + ], + "type": "string", + "x-stripeBypassValidation": true + } + }, + "required": ["file", "purpose"], + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + }, + "description": "Successful response." + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + }, + "description": "Error response." + } + }, + "servers": [ + { + "url": "https://files.stripe.com/" + } + ] + } + }, "/fine_tuning/jobs": { "post": { "operationId": "createFineTuningJob", diff --git a/ndc-rest-schema/openapi/testdata/prefix2/expected_multi_words.json b/ndc-rest-schema/openapi/testdata/prefix2/expected_multi_words.json index 19d8168..d1bed30 100644 --- a/ndc-rest-schema/openapi/testdata/prefix2/expected_multi_words.json +++ b/ndc-rest-schema/openapi/testdata/prefix2/expected_multi_words.json @@ -14,30 +14,11 @@ }, "version": "1.0.0" }, - "collections": [], "functions": [ { "request": { "url": "/posts", "method": "get", - "parameters": [ - { - "name": "id", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "userId", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - } - ], "response": { "contentType": "application/json" } @@ -51,6 +32,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "id", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "userId": { @@ -61,6 +51,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "userId", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -85,6 +84,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "id": { @@ -94,6 +98,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "title": { @@ -103,6 +113,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "userId": { @@ -112,6 +127,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } } } @@ -123,10 +144,7 @@ "url": "/posts", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Post" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -138,6 +156,12 @@ "type": { "name": "Post", "type": "named" + }, + "rest": { + "in": "body", + "schema": { + "type": null + } } } }, diff --git a/ndc-rest-schema/openapi/testdata/prefix2/expected_single_word.json b/ndc-rest-schema/openapi/testdata/prefix2/expected_single_word.json index a3ef003..6002d55 100644 --- a/ndc-rest-schema/openapi/testdata/prefix2/expected_single_word.json +++ b/ndc-rest-schema/openapi/testdata/prefix2/expected_single_word.json @@ -14,30 +14,11 @@ }, "version": "1.0.0" }, - "collections": [], "functions": [ { "request": { "url": "/posts", "method": "get", - "parameters": [ - { - "name": "id", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "userId", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - } - ], "response": { "contentType": "application/json" } @@ -51,6 +32,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "id", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "userId": { @@ -61,6 +51,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "userId", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -85,6 +84,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "id": { @@ -94,6 +98,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "title": { @@ -103,6 +113,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "userId": { @@ -112,6 +127,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } } } @@ -123,10 +144,7 @@ "url": "/posts", "method": "post", "requestBody": { - "contentType": "application/json", - "schema": { - "type": "Post" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -138,6 +156,12 @@ "type": { "name": "Post", "type": "named" + }, + "rest": { + "in": "body", + "schema": { + "type": null + } } } }, diff --git a/ndc-rest-schema/openapi/testdata/prefix3/expected_multi_words.json b/ndc-rest-schema/openapi/testdata/prefix3/expected_multi_words.json index 434cc73..4e85e2c 100644 --- a/ndc-rest-schema/openapi/testdata/prefix3/expected_multi_words.json +++ b/ndc-rest-schema/openapi/testdata/prefix3/expected_multi_words.json @@ -28,45 +28,11 @@ }, "version": "1.2.2" }, - "collections": [], "functions": [ { "request": { "url": "/notifications", "method": "get", - "parameters": [ - { - "name": "app_id", - "in": "query", - "schema": { - "type": "String" - } - }, - { - "name": "kind", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - } - ], "security": [ { "app_key": [] @@ -82,6 +48,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "app_id", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "kind": { @@ -92,6 +67,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "kind", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "limit": { @@ -102,6 +86,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "limit", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "offset": { @@ -112,6 +105,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "offset", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -133,6 +135,11 @@ "name": "Notification200Errors", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "external_id": { @@ -142,6 +149,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "id": { @@ -151,6 +163,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "recipients": { @@ -160,6 +177,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -174,6 +196,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "errored": { @@ -184,6 +211,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "failed": { @@ -194,6 +226,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "received": { @@ -204,6 +241,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "successful": { @@ -214,6 +256,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -225,6 +272,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "key": { @@ -235,6 +287,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "relation": { @@ -242,6 +299,11 @@ "type": { "name": "FilterRelation", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { @@ -252,6 +314,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -265,6 +332,12 @@ "name": "TimestampTZ", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "date-time" } } } @@ -278,6 +351,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "notifications": { @@ -290,6 +368,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "offset": { @@ -299,6 +382,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "total_count": { @@ -308,6 +396,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -322,6 +415,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "contents": { @@ -331,6 +430,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "converted": { @@ -341,6 +445,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "custom_data": { @@ -350,6 +459,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "data": { @@ -359,6 +473,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "errored": { @@ -369,6 +488,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "excluded_segments": { @@ -381,6 +505,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "failed": { @@ -391,6 +525,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "filters": { @@ -403,6 +542,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "headings": { @@ -412,6 +556,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "id": { @@ -421,6 +570,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "include_player_ids": { @@ -433,6 +587,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "included_segments": { @@ -445,6 +609,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "outcomes": { @@ -457,6 +631,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "platform_delivery_stats": { @@ -467,6 +646,11 @@ "name": "PlatformDeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "queued_at": { @@ -477,6 +661,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "received": { @@ -487,6 +677,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "remaining": { @@ -497,6 +692,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "send_after": { @@ -507,6 +707,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "subtitle": { @@ -516,6 +722,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "successful": { @@ -526,6 +737,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "target_channel": { @@ -535,6 +751,11 @@ "name": "PlayerNotificationTargetTargetChannel", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "throttle_rate_per_minute": { @@ -545,6 +766,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -555,18 +781,33 @@ "type": { "name": "OutcomeDataAggregation", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "id": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -581,6 +822,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "chrome_web_push": { @@ -590,6 +836,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "edge_web_push": { @@ -599,6 +850,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "email": { @@ -608,6 +864,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "firefox_web_push": { @@ -617,6 +878,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "ios": { @@ -626,6 +892,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "safari_web_push": { @@ -635,6 +906,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "sms": { @@ -644,6 +920,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -658,6 +939,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -674,10 +960,7 @@ } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "NotificationInput" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -689,6 +972,9 @@ "type": { "name": "NotificationInput", "type": "named" + }, + "rest": { + "in": "body" } } }, diff --git a/ndc-rest-schema/openapi/testdata/prefix3/expected_single_word.json b/ndc-rest-schema/openapi/testdata/prefix3/expected_single_word.json index c3be9d5..cc0784f 100644 --- a/ndc-rest-schema/openapi/testdata/prefix3/expected_single_word.json +++ b/ndc-rest-schema/openapi/testdata/prefix3/expected_single_word.json @@ -28,45 +28,11 @@ }, "version": "1.2.2" }, - "collections": [], "functions": [ { "request": { "url": "/notifications", "method": "get", - "parameters": [ - { - "name": "app_id", - "in": "query", - "schema": { - "type": "String" - } - }, - { - "name": "kind", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "limit", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - }, - { - "name": "offset", - "in": "query", - "schema": { - "type": "Int32", - "nullable": true - } - } - ], "security": [ { "app_key": [] @@ -82,6 +48,15 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "name": "app_id", + "in": "query", + "schema": { + "type": [ + "string" + ] + } } }, "kind": { @@ -92,6 +67,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "kind", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "limit": { @@ -102,6 +86,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "limit", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } }, "offset": { @@ -112,6 +105,15 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "name": "offset", + "in": "query", + "schema": { + "type": [ + "integer" + ] + } } } }, @@ -133,6 +135,11 @@ "name": "Notification200Errors", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "external_id": { @@ -142,6 +149,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "id": { @@ -151,6 +163,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "recipients": { @@ -160,6 +177,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -174,6 +196,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "errored": { @@ -184,6 +211,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "failed": { @@ -194,6 +226,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "received": { @@ -204,6 +241,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "successful": { @@ -214,6 +256,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -225,6 +272,11 @@ "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "key": { @@ -235,6 +287,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "relation": { @@ -242,6 +299,11 @@ "type": { "name": "FilterRelation", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { @@ -252,6 +314,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -265,6 +332,12 @@ "name": "TimestampTZ", "type": "named" } + }, + "rest": { + "type": [ + "string" + ], + "format": "date-time" } } } @@ -278,6 +351,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "notifications": { @@ -290,6 +368,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "offset": { @@ -299,6 +382,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "total_count": { @@ -308,6 +396,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -322,6 +415,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "contents": { @@ -331,6 +430,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "converted": { @@ -341,6 +445,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "custom_data": { @@ -350,6 +459,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "data": { @@ -359,6 +473,11 @@ "name": "JSON", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "errored": { @@ -369,6 +488,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "excluded_segments": { @@ -381,6 +505,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "failed": { @@ -391,6 +525,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "filters": { @@ -403,6 +542,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "headings": { @@ -412,6 +556,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "id": { @@ -421,6 +570,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "include_player_ids": { @@ -433,6 +587,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "included_segments": { @@ -445,6 +609,16 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ], + "items": { + "type": [ + "string" + ] + } } }, "outcomes": { @@ -457,6 +631,11 @@ }, "type": "array" } + }, + "rest": { + "type": [ + "array" + ] } }, "platform_delivery_stats": { @@ -467,6 +646,11 @@ "name": "PlatformDeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "queued_at": { @@ -477,6 +661,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "received": { @@ -487,6 +677,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "remaining": { @@ -497,6 +692,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "send_after": { @@ -507,6 +707,12 @@ "name": "Int64", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ], + "format": "int64" } }, "subtitle": { @@ -516,6 +722,11 @@ "name": "StringMap", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "successful": { @@ -526,6 +737,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } }, "target_channel": { @@ -535,6 +751,11 @@ "name": "PlayerNotificationTargetTargetChannel", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } }, "throttle_rate_per_minute": { @@ -545,6 +766,11 @@ "name": "Int32", "type": "named" } + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -555,18 +781,33 @@ "type": { "name": "OutcomeDataAggregation", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "id": { "type": { "name": "String", "type": "named" + }, + "rest": { + "type": [ + "string" + ] } }, "value": { "type": { "name": "Int32", "type": "named" + }, + "rest": { + "type": [ + "integer" + ] } } } @@ -581,6 +822,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "chrome_web_push": { @@ -590,6 +836,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "edge_web_push": { @@ -599,6 +850,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "email": { @@ -608,6 +864,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "firefox_web_push": { @@ -617,6 +878,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "ios": { @@ -626,6 +892,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "safari_web_push": { @@ -635,6 +906,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } }, "sms": { @@ -644,6 +920,11 @@ "name": "DeliveryData", "type": "named" } + }, + "rest": { + "type": [ + "object" + ] } } } @@ -658,6 +939,11 @@ "name": "String", "type": "named" } + }, + "rest": { + "type": [ + "string" + ] } } } @@ -674,10 +960,7 @@ } ], "requestBody": { - "contentType": "application/json", - "schema": { - "type": "NotificationInput" - } + "contentType": "application/json" }, "response": { "contentType": "application/json" @@ -689,6 +972,9 @@ "type": { "name": "NotificationInput", "type": "named" + }, + "rest": { + "in": "body" } } }, diff --git a/ndc-rest-schema/schema/schema.go b/ndc-rest-schema/schema/schema.go index df6377d..ba6e49e 100644 --- a/ndc-rest-schema/schema/schema.go +++ b/ndc-rest-schema/schema/schema.go @@ -160,6 +160,26 @@ type EncodingObject struct { Headers map[string]RequestParameter `json:"headers,omitempty" mapstructure:"headers" yaml:"headers,omitempty"` } +// SetHeader sets the encoding header +func (eo *EncodingObject) SetHeader(key string, param RequestParameter) { + if eo.Headers == nil { + eo.Headers = make(map[string]RequestParameter) + } + eo.Headers[key] = param +} + +// GetHeader gets the encoding header by key +func (eo *EncodingObject) GetHeader(key string) *RequestParameter { + if len(eo.Headers) == 0 { + return nil + } + result, ok := eo.Headers[key] + if !ok { + return nil + } + return &result +} + // RequestBody defines flexible request body with content types type RequestBody struct { ContentType string `json:"contentType,omitempty" mapstructure:"contentType" yaml:"contentType,omitempty"` @@ -270,7 +290,7 @@ func (of ObjectType) Schema() schema.ObjectType { type ObjectField struct { schema.ObjectField `yaml:",inline"` - // The request parameter information of the REST request + // The field schema information of the REST request Rest *TypeSchema `json:"rest,omitempty" mapstructure:"rest" yaml:"rest,omitempty"` } @@ -287,24 +307,24 @@ func (j *ObjectField) UnmarshalJSON(b []byte) error { } rawType, ok := raw["type"] if !ok || len(rawType) == 0 { - return fmt.Errorf("field type in ArgumentInfo: required") + return fmt.Errorf("field type in ObjectField: required") } if err := json.Unmarshal(rawType, &j.Type); err != nil { - return fmt.Errorf("field type in ArgumentInfo: %w", err) + return fmt.Errorf("field type in ObjectField: %w", err) } if rawDesc, ok := raw["description"]; ok { var desc string if err := json.Unmarshal(rawDesc, &desc); err != nil { - return fmt.Errorf("field description in ArgumentInfo: %w", err) + return fmt.Errorf("field description in ObjectField: %w", err) } j.Description = &desc } if rawArguments, ok := raw["arguments"]; ok { var arguments schema.ObjectFieldArguments if err := json.Unmarshal(rawArguments, &arguments); err != nil { - return fmt.Errorf("field arguments in ArgumentInfo: %w", err) + return fmt.Errorf("field arguments in ObjectField: %w", err) } j.Arguments = arguments } @@ -312,7 +332,7 @@ func (j *ObjectField) UnmarshalJSON(b []byte) error { if rawType, ok := raw["rest"]; ok { var ty TypeSchema if err := json.Unmarshal(rawType, &ty); err != nil { - return fmt.Errorf("field parameter in ArgumentInfo: %w", err) + return fmt.Errorf("field rest in ObjectField: %w", err) } j.Rest = &ty } diff --git a/rest/internal/request_builder.go b/rest/internal/request_builder.go new file mode 100644 index 0000000..798c265 --- /dev/null +++ b/rest/internal/request_builder.go @@ -0,0 +1,394 @@ +package internal + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "net/http" + "net/url" + "reflect" + "slices" + "strings" + + rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" + "github.com/hasura/ndc-sdk-go/schema" + "github.com/hasura/ndc-sdk-go/utils" +) + +// RequestBuilder builds requests to the remote service +type RequestBuilder struct { + Schema *rest.NDCRestSchema + Operation *rest.OperationInfo + Arguments map[string]any +} + +// NewRequestBuilder creates a new RequestBuilder instance +func NewRequestBuilder(restSchema *rest.NDCRestSchema, operation *rest.OperationInfo, arguments map[string]any) *RequestBuilder { + return &RequestBuilder{ + Schema: restSchema, + Operation: operation, + Arguments: arguments, + } +} + +// Build evaluates and builds a RetryableRequest +func (c *RequestBuilder) Build() (*RetryableRequest, error) { + endpoint, headers, err := c.evalURLAndHeaderParameters() + if err != nil { + return nil, schema.UnprocessableContentError("failed to evaluate URL and Headers from parameters", map[string]any{ + "cause": err.Error(), + }) + } + + var buffer io.ReadSeeker + + rawRequest := c.Operation.Request + contentType := rest.ContentTypeJSON + + if rawRequest.RequestBody != nil { + contentType = rawRequest.RequestBody.ContentType + bodyInfo, infoOk := c.Operation.Arguments[rest.BodyKey] + bodyData, ok := c.Arguments[rest.BodyKey] + if ok && bodyData != nil { + var err error + binaryBody := getRequestUploadBody(rawRequest) + if binaryBody != nil { + b64, err := utils.DecodeString(bodyData) + if err != nil { + return nil, err + } + dataURI, err := DecodeDataURI(b64) + if err != nil { + return nil, err + } + buffer = bytes.NewReader([]byte(dataURI.Data)) + } else if strings.HasPrefix(contentType, "text/") { + buffer = bytes.NewReader([]byte(fmt.Sprint(bodyData))) + } else if strings.HasPrefix(contentType, "multipart/") { + buffer, contentType, err = c.createMultipartForm(bodyData) + if err != nil { + return nil, err + } + } else { + switch contentType { + case rest.ContentTypeFormURLEncoded: + buffer, err = c.createFormURLEncoded(&bodyInfo, bodyData) + if err != nil { + return nil, err + } + case rest.ContentTypeJSON, "": + bodyBytes, err := json.Marshal(bodyData) + if err != nil { + return nil, err + } + + buffer = bytes.NewReader(bodyBytes) + default: + return nil, fmt.Errorf("unsupported content type %s", contentType) + } + } + } else if infoOk { + ty, err := bodyInfo.Type.Type() + if err != nil { + return nil, err + } + if ty != schema.TypeNullable { + return nil, errors.New("request body is required") + } + } + } + + request := &RetryableRequest{ + URL: endpoint, + RawRequest: rawRequest, + ContentType: contentType, + Headers: headers, + Body: buffer, + Timeout: rawRequest.Timeout, + Retry: rawRequest.Retry, + } + + return request, nil +} + +func (c *RequestBuilder) createFormURLEncoded(bodyInfo *rest.ArgumentInfo, bodyData any) (io.ReadSeeker, error) { + queryParams, err := c.encodeParameterValues(&rest.ObjectField{ + ObjectField: schema.ObjectField{ + Type: bodyInfo.Type, + }, + Rest: bodyInfo.Rest.Schema, + }, reflect.ValueOf(bodyData), []string{"body"}) + if err != nil { + return nil, err + } + + if len(queryParams) == 0 { + return nil, nil + } + q := url.Values{} + for _, qp := range queryParams { + keys := qp.Keys() + evalQueryParameterURL(&q, "", bodyInfo.Rest.EncodingObject, keys, qp.Values()) + } + rawQuery := encodeQueryValues(q, true) + + return bytes.NewReader([]byte(rawQuery)), nil +} + +func (c *RequestBuilder) createMultipartForm(bodyData any) (io.ReadSeeker, string, error) { + bodyInfo, ok := c.Operation.Arguments[rest.BodyKey] + if !ok { + return nil, "", errRequestBodyTypeRequired + } + + buffer := new(bytes.Buffer) + writer := NewMultipartWriter(buffer) + + if err := c.evalMultipartForm(writer, &bodyInfo, reflect.ValueOf(bodyData)); err != nil { + return nil, "", err + } + if err := writer.Close(); err != nil { + return nil, "", err + } + + reader := bytes.NewReader(buffer.Bytes()) + buffer.Reset() + + return reader, writer.FormDataContentType(), nil +} + +func (c *RequestBuilder) evalMultipartForm(w *MultipartWriter, bodyInfo *rest.ArgumentInfo, bodyData reflect.Value) error { + bodyData, ok := utils.UnwrapPointerFromReflectValue(bodyData) + if !ok { + return nil + } + switch bodyType := bodyInfo.Type.Interface().(type) { + case *schema.NullableType: + return c.evalMultipartForm(w, &rest.ArgumentInfo{ + ArgumentInfo: schema.ArgumentInfo{ + Type: bodyType.UnderlyingType, + }, + Rest: bodyInfo.Rest, + }, bodyData) + case *schema.NamedType: + if !ok { + return fmt.Errorf("%s: %w", rest.BodyKey, errArgumentRequired) + } + bodyObject, ok := c.Schema.ObjectTypes[bodyType.Name] + if !ok { + break + } + kind := bodyData.Kind() + switch kind { + case reflect.Map, reflect.Interface: + bi := bodyData.Interface() + bodyMap, ok := bi.(map[string]any) + if !ok { + return fmt.Errorf("invalid multipart form body, expected object, got %v", bi) + } + + for key, fieldInfo := range bodyObject.Fields { + fieldValue := bodyMap[key] + var enc *rest.EncodingObject + if len(c.Operation.Request.RequestBody.Encoding) > 0 { + en, ok := c.Operation.Request.RequestBody.Encoding[key] + if ok { + enc = &en + } + } + log.Println("fields", key, fieldInfo) + + if err := c.evalMultipartFieldValueRecursive(w, key, reflect.ValueOf(fieldValue), &fieldInfo, enc); err != nil { + return err + } + } + return nil + case reflect.Struct: + reflectType := bodyData.Type() + for fieldIndex := range bodyData.NumField() { + fieldValue := bodyData.Field(fieldIndex) + fieldType := reflectType.Field(fieldIndex) + fieldInfo, ok := bodyObject.Fields[fieldType.Name] + if !ok { + continue + } + + var enc *rest.EncodingObject + if len(c.Operation.Request.RequestBody.Encoding) > 0 { + en, ok := c.Operation.Request.RequestBody.Encoding[fieldType.Name] + if ok { + enc = &en + } + } + + if err := c.evalMultipartFieldValueRecursive(w, fieldType.Name, fieldValue, &fieldInfo, enc); err != nil { + return err + } + } + return nil + } + } + + return fmt.Errorf("invalid multipart form body, expected object, got %v", bodyInfo.Type) +} + +func (c *RequestBuilder) evalMultipartFieldValueRecursive(w *MultipartWriter, name string, value reflect.Value, fieldInfo *rest.ObjectField, enc *rest.EncodingObject) error { + underlyingValue, notNull := utils.UnwrapPointerFromReflectValue(value) + argTypeT, err := fieldInfo.Type.InterfaceT() + switch argType := argTypeT.(type) { + case *schema.ArrayType: + if !notNull { + return fmt.Errorf("%s: %w", name, errArgumentRequired) + } + if fieldInfo.Rest == nil || (enc != nil && slices.Contains(enc.ContentType, rest.ContentTypeJSON)) { + var headers http.Header + var err error + if enc != nil && len(enc.Headers) > 0 { + headers, err = c.evalEncodingHeaders(enc.Headers) + if err != nil { + return err + } + } + return w.WriteJSON(name, value.Interface(), headers) + } + if !slices.Contains([]reflect.Kind{reflect.Slice, reflect.Array}, value.Kind()) { + return fmt.Errorf("%s: expected array type, got %v", name, value.Interface()) + } + + for i := range value.Len() { + elem := value.Index(i) + err := c.evalMultipartFieldValueRecursive(w, name+"[]", elem, &rest.ObjectField{ + ObjectField: schema.ObjectField{ + Type: argType.ElementType, + }, + Rest: fieldInfo.Rest.Items, + }, enc) + if err != nil { + return err + } + } + return nil + case *schema.NullableType: + if !notNull { + return nil + } + return c.evalMultipartFieldValueRecursive(w, name, underlyingValue, &rest.ObjectField{ + ObjectField: schema.ObjectField{ + Type: argType.UnderlyingType, + }, + Rest: fieldInfo.Rest, + }, enc) + case *schema.NamedType: + if !notNull { + return fmt.Errorf("%s: %w", name, errArgumentRequired) + } + var headers http.Header + var err error + if enc != nil && len(enc.Headers) > 0 { + headers, err = c.evalEncodingHeaders(enc.Headers) + if err != nil { + return err + } + } + if iScalar, ok := c.Schema.ScalarTypes[argType.Name]; ok { + switch iScalar.Representation.Interface().(type) { + case *schema.TypeRepresentationBytes: + return w.WriteDataURI(name, value.Interface(), headers) + } + } + + if enc != nil && slices.Contains(enc.ContentType, rest.ContentTypeJSON) { + return w.WriteJSON(name, value, headers) + } + + params, err := c.encodeParameterValues(fieldInfo, value, []string{}) + if err != nil { + return err + } + + if len(params) == 0 { + return nil + } + + for _, p := range params { + keys := p.Keys() + values := p.Values() + fieldName := name + + if len(keys) > 0 { + keys = append([]Key{NewKey(name)}, keys...) + fieldName = keys.String() + } + + if len(values) > 1 { + fieldName += "[]" + for _, v := range values { + if err = w.WriteField(fieldName, v, headers); err != nil { + return err + } + } + } else if len(values) == 1 { + if err = w.WriteField(fieldName, values[0], headers); err != nil { + return err + } + } + } + + return nil + case *schema.PredicateType: + return fmt.Errorf("%s: predicate type is not supported", name) + default: + return fmt.Errorf("%s: %w", name, err) + } +} + +func (c *RequestBuilder) evalEncodingHeaders(encHeaders map[string]rest.RequestParameter) (http.Header, error) { + results := http.Header{} + for key, param := range encHeaders { + argumentName := param.ArgumentName + if argumentName == "" { + argumentName = key + } + argumentInfo, ok := c.Operation.Arguments[argumentName] + if !ok { + continue + } + rawHeaderValue, ok := c.Arguments[argumentName] + if !ok { + continue + } + + headerParams, err := c.encodeParameterValues(&rest.ObjectField{ + ObjectField: schema.ObjectField{ + Type: argumentInfo.Type, + }, + Rest: param.Schema, + }, reflect.ValueOf(rawHeaderValue), []string{}) + if err != nil { + return nil, err + } + + param.Name = key + setHeaderParameters(&results, ¶m, headerParams) + } + + return results, nil +} + +func getRequestUploadBody(rawRequest *rest.Request) *rest.RequestBody { + if rawRequest.RequestBody == nil { + return nil + } + if rawRequest.RequestBody.ContentType == "application/octet-stream" { + return rawRequest.RequestBody + } + + // TODO + // if rawRequest.RequestBody.Schema != nil && slices.Contains(rawRequest.RequestBody.Schema.Type, string(rest.ScalarBinary)) { + // return rawRequest.RequestBody + // } + return nil +} diff --git a/rest/internal/request_builder_test.go b/rest/internal/request_builder_test.go new file mode 100644 index 0000000..8e1c41c --- /dev/null +++ b/rest/internal/request_builder_test.go @@ -0,0 +1,580 @@ +package internal + +import ( + "encoding/json" + "io" + "mime" + "mime/multipart" + "net/http" + "os" + "slices" + "strings" + "testing" + + rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" + "gotest.tools/v3/assert" +) + +func TestCreateMultipartForm(t *testing.T) { + testCases := []struct { + Name string + RawArguments string + Expected map[string]string + ExpectedHeaders map[string]http.Header + }{ + { + Name: "PostFiles", + RawArguments: `{ + "body": { + "expand": ["foo"], + "expand_json": ["foo","bar"], + "file": "aGVsbG8gd29ybGQ=", + "file_link_data": { + "create": true, + "expires_at": 181320689 + }, + "purpose": "business_icon" + }, + "headerXRateLimitLimit": 10 + }`, + Expected: map[string]string{ + "expand[]": `foo`, + "expand_json": `["foo","bar"]`, + "file": "hello world", + "file_link_data.create": "true", + "file_link_data.expires_at": "181320689", + "purpose": "business_icon", + }, + ExpectedHeaders: map[string]http.Header{ + "expand": { + "Content-Type": []string{"application/json"}, + }, + "file": { + "X-Rate-Limit-Limit": []string{"10"}, + }, + }, + }, + } + + ndcSchema := createMockSchema(t) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + var info *rest.OperationInfo + for _, f := range ndcSchema.Procedures { + if f.Name == tc.Name { + info = f + break + } + } + assert.Assert(t, info != nil) + + var arguments map[string]any + assert.NilError(t, json.Unmarshal([]byte(tc.RawArguments), &arguments)) + builder := RequestBuilder{ + Schema: ndcSchema, + Operation: info, + Arguments: arguments, + } + buf, mediaType, err := builder.createMultipartForm(arguments["body"]) + assert.NilError(t, err) + + _, params, err := mime.ParseMediaType(mediaType) + assert.NilError(t, err) + + reader := multipart.NewReader(buf, params["boundary"]) + var count int + results := make(map[string]string) + for { + form, err := reader.NextPart() + if err != nil && strings.Contains(err.Error(), io.EOF.Error()) { + break + } + assert.NilError(t, err) + count++ + name := form.FormName() + + expected, ok := tc.Expected[name] + if !ok { + t.Fatalf("field %s does not exist", name) + } else { + result, err := io.ReadAll(form) + assert.NilError(t, err) + assert.Equal(t, expected, string(result)) + results[name] = string(result) + expectedHeader := tc.ExpectedHeaders[name] + + for key, value := range expectedHeader { + assert.DeepEqual(t, value, form.Header[key]) + } + } + } + if len(tc.Expected) != count { + assert.DeepEqual(t, tc.Expected, results) + } + }) + } +} + +func TestCreateFormURLEncoded(t *testing.T) { + testCases := []struct { + Name string + RawArguments string + Expected string + }{ + { + Name: "PostCheckoutSessions", + RawArguments: `{ + "body": { + "after_expiration": { + "recovery": { + "allow_promotion_codes": true, + "enabled": true + } + }, + "allow_promotion_codes": true, + "automatic_tax": { + "enabled": false, + "liability": { + "account": "gW7D0WhP9C", + "type": "self" + } + }, + "billing_address_collection": "auto", + "cancel_url": "qpmWppPyIv", + "client_reference_id": "ZcJeCf6JAa", + "consent_collection": { + "payment_method_reuse_agreement": { + "position": "auto" + }, + "promotions": "auto", + "terms_of_service": "required" + }, + "currency": "oVljMB8lon", + "custom_fields": [ + { + "dropdown": { + "options": [ + { + "label": "W3oysCi31d", + "value": "hXN8MppU0k" + } + ] + }, + "key": "5ZeyjIHLn8", + "label": { + "custom": "uabTz3xzdn", + "type": "custom" + }, + "numeric": { + "maximum_length": 678468035, + "minimum_length": 2134997439 + }, + "optional": false, + "text": { + "maximum_length": 331815114, + "minimum_length": 1689246767 + }, + "type": "dropdown" + } + ], + "custom_text": { + "after_submit": { + "message": "b7ifuedi9S" + }, + "shipping_address": { + "message": "XeD5TkmC8k" + }, + "submit": { + "message": "vGcSz5eSlo" + }, + "terms_of_service_acceptance": { + "message": "zGLTTZItPl" + } + }, + "customer": "mT4BKOSu9s", + "customer_creation": "always", + "customer_email": "1xiCJ8M7Pr", + "customer_update": { + "address": "never", + "name": "never", + "shipping": "auto" + }, + "discounts": [ + { + "coupon": "tOlEXiZKv9", + "promotion_code": "Xknj8juRnm" + } + ], + "expand": ["ZBxEXz7SN0"], + "expires_at": 1756067225, + "invoice_creation": { + "enabled": true, + "invoice_data": { + "account_tax_ids": ["dev8vFF6xG"], + "custom_fields": [ + { + "name": "LBlZjJ4gEy", + "value": "EWoKgkV3fg" + } + ], + "description": "MiePp9LfkQ", + "footer": "OAELqbYbKV", + "issuer": { + "account": "aqOwDzxnyg", + "type": "account" + }, + "metadata": null, + "rendering_options": { + "amount_tax_display": "exclude_tax" + } + } + }, + "line_items": [ + { + "adjustable_quantity": { + "enabled": false, + "maximum": 1665059759, + "minimum": 905088217 + }, + "dynamic_tax_rates": ["jMMvH8TmQD"], + "price": "fR6vnvprv8", + "price_data": { + "currency": "euIDO8C4A7", + "product": "xilQ2QDVdA", + "product_data": { + "description": "DQECtJEsLI", + "images": ["gE5K8MOzRc"], + "metadata": null, + "name": "ak6UVjXl1B", + "tax_code": "PzbIHvqWJp" + }, + "recurring": { + "interval": "day", + "interval_count": 592739346 + }, + "tax_behavior": "inclusive", + "unit_amount": 945322526, + "unit_amount_decimal": "vkJPCvrn9Q" + }, + "quantity": 968305911, + "tax_rates": ["Ts1bPAoT0T"] + } + ], + "locale": "auto", + "metadata": null, + "mode": "payment", + "payment_intent_data": { + "application_fee_amount": 2033958571, + "capture_method": "manual", + "description": "yoalRHw9ZG", + "metadata": null, + "on_behalf_of": "mpkGzXu3st", + "receipt_email": "LxJLYGjJ4r", + "setup_future_usage": "off_session", + "shipping": { + "address": { + "city": "v6nZI33cUt", + "country": "O8MBVcia7c", + "line1": "3YghEmysVn", + "line2": "CM9x9Jizzu", + "postal_code": "1aAilmcYiq", + "state": "ILODDWP1IP" + }, + "carrier": "P8mCJlEq1J", + "name": "mJYqgRIh3S", + "phone": "CWAbvZM4Kw", + "tracking_number": "XGOZIrLZf0" + }, + "statement_descriptor": "JCOo6lU8Fy", + "statement_descriptor_suffix": "dtPJwyuc4i", + "transfer_data": { + "amount": 94957585, + "destination": "LrcNMrJPkO" + }, + "transfer_group": "XKfPQPVhOT" + }, + "payment_method_collection": "always", + "payment_method_configuration": "uwYSwIZP4V", + "payment_method_options": { + "acss_debit": { + "currency": "usd", + "mandate_options": { + "custom_mandate_url": "FZwPtJKktL", + "default_for": ["invoice"], + "interval_description": "iMgay8S9If", + "payment_schedule": "sporadic", + "transaction_type": "business" + }, + "setup_future_usage": "off_session", + "verification_method": "instant" + }, + "affirm": { + "setup_future_usage": "none" + }, + "afterpay_clearpay": { + "setup_future_usage": "none" + }, + "alipay": { + "setup_future_usage": "none" + }, + "au_becs_debit": { + "setup_future_usage": "none" + }, + "bacs_debit": { + "setup_future_usage": "off_session" + }, + "bancontact": { + "setup_future_usage": "none" + }, + "boleto": { + "expires_after_days": 953467886, + "setup_future_usage": "none" + }, + "card": { + "installments": { + "enabled": true + }, + "request_three_d_secure": "any", + "setup_future_usage": "on_session", + "statement_descriptor_suffix_kana": "ZvJtIONyDK", + "statement_descriptor_suffix_kanji": "Y57zexRcIH" + }, + "cashapp": { + "setup_future_usage": "off_session" + }, + "customer_balance": { + "bank_transfer": { + "eu_bank_transfer": { + "country": "mzrVWAjBTc" + }, + "requested_address_types": ["iban"], + "type": "gb_bank_transfer" + }, + "funding_type": "bank_transfer", + "setup_future_usage": "none" + }, + "eps": { + "setup_future_usage": "none" + }, + "fpx": { + "setup_future_usage": "none" + }, + "giropay": { + "setup_future_usage": "none" + }, + "grabpay": { + "setup_future_usage": "none" + }, + "ideal": { + "setup_future_usage": "none" + }, + "klarna": { + "setup_future_usage": "none" + }, + "konbini": { + "expires_after_days": 664583520, + "setup_future_usage": "none" + }, + "link": { + "setup_future_usage": "none" + }, + "mobilepay": { + "setup_future_usage": "none" + }, + "oxxo": { + "expires_after_days": 1925345768, + "setup_future_usage": "none" + }, + "p24": { + "setup_future_usage": "none", + "tos_shown_and_accepted": true + }, + "paynow": { + "setup_future_usage": "none" + }, + "paypal": { + "capture_method": "manual", + "preferred_locale": "cs-CZ", + "reference": "ulLn2NXA1P", + "risk_correlation_id": "fj1J6Nux6P", + "setup_future_usage": "none" + }, + "pix": { + "expires_after_seconds": 191312234 + }, + "revolut_pay": { + "setup_future_usage": "off_session" + }, + "sepa_debit": { + "setup_future_usage": "none" + }, + "sofort": { + "setup_future_usage": "none" + }, + "swish": { + "reference": "rXJq1EX4rc" + }, + "us_bank_account": { + "financial_connections": { + "permissions": ["ownership"], + "prefetch": ["transactions"] + }, + "setup_future_usage": "none", + "verification_method": "automatic" + }, + "wechat_pay": { + "app_id": "9Pu0d1pZ2r", + "client": "ios", + "setup_future_usage": "none" + } + }, + "payment_method_types": ["acss_debit"], + "phone_number_collection": { + "enabled": true + }, + "redirect_on_completion": "never", + "return_url": "YgIdKykEHC", + "setup_intent_data": { + "description": "U9qFTQnt1W", + "metadata": null, + "on_behalf_of": "165u5Fvodj" + }, + "shipping_address_collection": { + "allowed_countries": ["AC"] + }, + "shipping_options": [ + { + "shipping_rate": "5PAjqTpMjw", + "shipping_rate_data": { + "delivery_estimate": { + "maximum": { + "unit": "week", + "value": 479399576 + }, + "minimum": { + "unit": "day", + "value": 1640284987 + } + }, + "display_name": "PXozGQQnBA", + "fixed_amount": { + "amount": 2040036333, + "currency": "KkRL3jvZMO", + "currency_options": null + }, + "metadata": null, + "tax_behavior": "exclusive", + "tax_code": "NKSQxYdCfO", + "type": "fixed_amount" + } + } + ], + "submit_type": "donate", + "subscription_data": { + "application_fee_percent": 1.7020678102144877, + "billing_cycle_anchor": 1981798554, + "default_tax_rates": ["b3jgFBJq4f"], + "description": "7mpaD2E0jf", + "invoice_settings": { + "issuer": { + "account": "axhiYamJKY", + "type": "account" + } + }, + "metadata": null, + "on_behalf_of": "oGsMnSifXV", + "proration_behavior": "create_prorations", + "transfer_data": { + "amount_percent": 1.5805719275050356, + "destination": "wzJ3U1Tyhd" + }, + "trial_end": 606476058, + "trial_period_days": 1684102049, + "trial_settings": { + "end_behavior": { + "missing_payment_method": "create_invoice" + } + } + }, + "success_url": "hDTwi34TAz", + "tax_id_collection": { + "enabled": true + }, + "ui_mode": "hosted" + } + }`, + Expected: "payment_method_options[bacs_debit][setup_future_usage]=off_session&payment_intent_data[shipping][address][line1]=3YghEmysVn&consent_collection[payment_method_reuse_agreement][position]=auto&payment_method_options[au_becs_debit][setup_future_usage]=none&payment_method_options[customer_balance][bank_transfer][eu_bank_transfer][country]=mzrVWAjBTc&payment_method_options[acss_debit][verification_method]=instant&payment_intent_data[transfer_group]=XKfPQPVhOT&line_items[0][price_data][currency]=euIDO8C4A7&invoice_creation[invoice_data][footer]=OAELqbYbKV&payment_method_options[us_bank_account][verification_method]=automatic&payment_method_options[cashapp][setup_future_usage]=off_session&payment_method_options[konbini][expires_after_days]=664583520&subscription_data[default_tax_rates][]=b3jgFBJq4f&line_items[0][price_data][product_data][tax_code]=PzbIHvqWJp&line_items[0][adjustable_quantity][minimum]=905088217¤cy=oVljMB8lon&invoice_creation[invoice_data][issuer][account]=aqOwDzxnyg&success_url=hDTwi34TAz&payment_method_options[giropay][setup_future_usage]=none&payment_method_options[oxxo][setup_future_usage]=none&payment_method_options[acss_debit][currency]=usd&payment_method_options[acss_debit][mandate_options][payment_schedule]=sporadic&payment_intent_data[shipping][name]=mJYqgRIh3S&subscription_data[transfer_data][amount_percent]=1.5805719275050356&custom_fields[0][label][custom]=uabTz3xzdn&customer_update[name]=never&payment_method_options[acss_debit][mandate_options][interval_description]=iMgay8S9If&automatic_tax[liability][type]=self&payment_intent_data[capture_method]=manual&custom_fields[0][dropdown][options][0][label]=W3oysCi31d&custom_fields[0][text][maximum_length]=331815114&custom_text[terms_of_service_acceptance][message]=zGLTTZItPl&invoice_creation[enabled]=true&shipping_options[0][shipping_rate]=5PAjqTpMjw&shipping_options[0][shipping_rate_data][delivery_estimate][minimum][unit]=day&payment_method_types[]=acss_debit&payment_intent_data[application_fee_amount]=2033958571&custom_text[after_submit][message]=b7ifuedi9S&shipping_options[0][shipping_rate_data][delivery_estimate][minimum][value]=1640284987&payment_method_options[afterpay_clearpay][setup_future_usage]=none&payment_method_options[alipay][setup_future_usage]=none&payment_method_options[us_bank_account][financial_connections][permissions][]=ownership&mode=payment&line_items[0][quantity]=968305911&return_url=YgIdKykEHC&shipping_options[0][shipping_rate_data][delivery_estimate][maximum][value]=479399576&payment_method_options[paypal][reference]=ulLn2NXA1P&subscription_data[transfer_data][destination]=wzJ3U1Tyhd&customer=mT4BKOSu9s&submit_type=donate&payment_method_options[boleto][setup_future_usage]=none&payment_method_options[acss_debit][mandate_options][transaction_type]=business&payment_intent_data[shipping][address][city]=v6nZI33cUt&custom_fields[0][type]=dropdown&invoice_creation[invoice_data][custom_fields][0][name]=LBlZjJ4gEy&shipping_options[0][shipping_rate_data][tax_behavior]=exclusive&customer_email=1xiCJ8M7Pr&invoice_creation[invoice_data][issuer][type]=account&payment_method_options[customer_balance][funding_type]=bank_transfer&payment_intent_data[shipping][address][state]=ILODDWP1IP&subscription_data[proration_behavior]=create_prorations&line_items[0][price_data][product_data][name]=ak6UVjXl1B&invoice_creation[invoice_data][custom_fields][0][value]=EWoKgkV3fg&shipping_options[0][shipping_rate_data][type]=fixed_amount&payment_method_options[link][setup_future_usage]=none&expand[]=ZBxEXz7SN0&subscription_data[invoice_settings][issuer][type]=account&payment_method_collection=always&customer_update[address]=never&payment_method_options[wechat_pay][setup_future_usage]=none&customer_creation=always&payment_method_options[card][statement_descriptor_suffix_kanji]=Y57zexRcIH&payment_method_options[p24][setup_future_usage]=none&locale=auto&line_items[0][price_data][product_data][images][]=gE5K8MOzRc&payment_method_options[us_bank_account][setup_future_usage]=none&payment_intent_data[on_behalf_of]=mpkGzXu3st&custom_fields[0][label][type]=custom&custom_fields[0][optional]=false&line_items[0][price_data][tax_behavior]=inclusive&billing_address_collection=auto&invoice_creation[invoice_data][rendering_options][amount_tax_display]=exclude_tax&shipping_options[0][shipping_rate_data][fixed_amount][currency]=KkRL3jvZMO&payment_method_options[grabpay][setup_future_usage]=none&ui_mode=hosted&payment_intent_data[transfer_data][destination]=LrcNMrJPkO&shipping_options[0][shipping_rate_data][tax_code]=NKSQxYdCfO&payment_method_options[affirm][setup_future_usage]=none&payment_method_options[paypal][setup_future_usage]=none&payment_method_options[acss_debit][mandate_options][custom_mandate_url]=FZwPtJKktL&automatic_tax[liability][account]=gW7D0WhP9C&custom_fields[0][numeric][maximum_length]=678468035&custom_fields[0][text][minimum_length]=1689246767&line_items[0][price_data][recurring][interval_count]=592739346&client_reference_id=ZcJeCf6JAa&line_items[0][price_data][unit_amount]=945322526&line_items[0][adjustable_quantity][maximum]=1665059759&discounts[0][coupon]=tOlEXiZKv9&shipping_address_collection[allowed_countries][]=AC&payment_method_options[paypal][risk_correlation_id]=fj1J6Nux6P&payment_method_options[acss_debit][setup_future_usage]=off_session&payment_method_options[konbini][setup_future_usage]=none&payment_intent_data[statement_descriptor_suffix]=dtPJwyuc4i&payment_intent_data[setup_future_usage]=off_session&subscription_data[on_behalf_of]=oGsMnSifXV&allow_promotion_codes=true&custom_fields[0][key]=5ZeyjIHLn8&custom_text[submit][message]=vGcSz5eSlo&setup_intent_data[on_behalf_of]=165u5Fvodj&discounts[0][promotion_code]=Xknj8juRnm&customer_update[shipping]=auto&shipping_options[0][shipping_rate_data][delivery_estimate][maximum][unit]=week&payment_method_options[oxxo][expires_after_days]=1925345768&payment_intent_data[receipt_email]=LxJLYGjJ4r&subscription_data[trial_settings][end_behavior][missing_payment_method]=create_invoice&after_expiration[recovery][enabled]=true&payment_method_configuration=uwYSwIZP4V&invoice_creation[invoice_data][account_tax_ids][]=dev8vFF6xG&shipping_options[0][shipping_rate_data][fixed_amount][amount]=2040036333&payment_method_options[paypal][capture_method]=manual&payment_method_options[paypal][preferred_locale]=cs-CZ&payment_intent_data[shipping][address][country]=O8MBVcia7c&after_expiration[recovery][allow_promotion_codes]=true&custom_text[shipping_address][message]=XeD5TkmC8k&line_items[0][price_data][recurring][interval]=day&line_items[0][price_data][product]=xilQ2QDVdA&line_items[0][dynamic_tax_rates][]=jMMvH8TmQD&payment_method_options[card][setup_future_usage]=on_session&payment_method_options[customer_balance][bank_transfer][type]=gb_bank_transfer&payment_method_options[sepa_debit][setup_future_usage]=none&automatic_tax[enabled]=false&consent_collection[terms_of_service]=required&payment_method_options[fpx][setup_future_usage]=none&payment_method_options[us_bank_account][financial_connections][prefetch][]=transactions&payment_intent_data[transfer_data][amount]=94957585&payment_method_options[bancontact][setup_future_usage]=none&payment_intent_data[statement_descriptor]=JCOo6lU8Fy&line_items[0][tax_rates][]=Ts1bPAoT0T&line_items[0][price]=fR6vnvprv8&setup_intent_data[description]=U9qFTQnt1W&redirect_on_completion=never&shipping_options[0][shipping_rate_data][display_name]=PXozGQQnBA&payment_method_options[card][installments][enabled]=true&payment_method_options[p24][tos_shown_and_accepted]=true&payment_method_options[wechat_pay][app_id]=9Pu0d1pZ2r&payment_method_options[wechat_pay][client]=ios&payment_method_options[boleto][expires_after_days]=953467886&payment_method_options[eps][setup_future_usage]=none&payment_method_options[acss_debit][mandate_options][default_for][]=invoice&subscription_data[trial_end]=606476058&custom_fields[0][numeric][minimum_length]=2134997439&line_items[0][price_data][product_data][description]=DQECtJEsLI&consent_collection[promotions]=auto&payment_method_options[swish][reference]=rXJq1EX4rc&payment_intent_data[shipping][carrier]=P8mCJlEq1J&payment_intent_data[shipping][tracking_number]=XGOZIrLZf0&payment_method_options[paynow][setup_future_usage]=none&payment_method_options[revolut_pay][setup_future_usage]=off_session&payment_method_options[klarna][setup_future_usage]=none&payment_intent_data[shipping][address][postal_code]=1aAilmcYiq&subscription_data[invoice_settings][issuer][account]=axhiYamJKY&subscription_data[trial_period_days]=1684102049&subscription_data[description]=7mpaD2E0jf&cancel_url=qpmWppPyIv&payment_method_options[card][statement_descriptor_suffix_kana]=ZvJtIONyDK&payment_method_options[pix][expires_after_seconds]=191312234&custom_fields[0][dropdown][options][0][value]=hXN8MppU0k&tax_id_collection[enabled]=true&payment_method_options[sofort][setup_future_usage]=none&payment_method_options[customer_balance][setup_future_usage]=none&payment_method_options[ideal][setup_future_usage]=none&payment_intent_data[description]=yoalRHw9ZG&payment_intent_data[shipping][phone]=CWAbvZM4Kw&expires_at=1756067225&line_items[0][adjustable_quantity][enabled]=false&invoice_creation[invoice_data][description]=MiePp9LfkQ&payment_method_options[card][request_three_d_secure]=any&payment_method_options[customer_balance][bank_transfer][requested_address_types][]=iban&line_items[0][price_data][unit_amount_decimal]=vkJPCvrn9Q&phone_number_collection[enabled]=true&payment_intent_data[shipping][address][line2]=CM9x9Jizzu&subscription_data[billing_cycle_anchor]=1981798554&subscription_data[application_fee_percent]=1.7020678102144877", + }, + { + Name: "PostCheckoutSessions", + RawArguments: `{ + "body": { + "automatic_tax": { + "enabled": false, + "liability": { + "type": "self", + "country": "DE" + } + }, + "subscription_data": { + "description": "nyxWwjZ0JY", + "invoice_settings": { + "issuer": { + "type": "self" + } + }, + "metadata": null, + "trial_period_days": 27623, + "trial_settings": { + "end_behavior": { + "missing_payment_method": "cancel" + } + } + } + } + }`, + Expected: "automatic_tax[enabled]=false&automatic_tax[liability][type]=self&subscription_data[description]=nyxWwjZ0JY&subscription_data[invoice_settings][issuer][type]=self&subscription_data[trial_period_days]=27623&subscription_data[trial_settings][end_behavior][missing_payment_method]=cancel", + }, + } + + ndcSchema := createMockSchema(t) + parseQueryAndSort := func(input string) []string { + items := strings.Split(input, "&") + slices.Sort(items) + return items + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + var info *rest.OperationInfo + for _, f := range ndcSchema.Procedures { + if f.Name == tc.Name { + info = f + break + } + } + assert.Assert(t, info != nil) + var arguments map[string]any + assert.NilError(t, json.Unmarshal([]byte(tc.RawArguments), &arguments)) + argumentInfo := info.Arguments["body"] + builder := RequestBuilder{ + Schema: ndcSchema, + Operation: info, + Arguments: arguments, + } + buf, err := builder.createFormURLEncoded(&argumentInfo, arguments["body"]) + assert.NilError(t, err) + result, err := io.ReadAll(buf) + assert.NilError(t, err) + assert.DeepEqual(t, parseQueryAndSort(tc.Expected), parseQueryAndSort(string(result))) + }) + } +} + +func createMockSchema(t *testing.T) *rest.NDCRestSchema { + var ndcSchema rest.NDCRestSchema + rawSchemaBytes, err := os.ReadFile("../../ndc-rest-schema/openapi/testdata/petstore3/expected.json") + assert.NilError(t, err) + assert.NilError(t, json.Unmarshal(rawSchemaBytes, &ndcSchema)) + return &ndcSchema +} diff --git a/rest/parameter.go b/rest/internal/request_parameter.go similarity index 52% rename from rest/parameter.go rename to rest/internal/request_parameter.go index 97da06d..1516aab 100644 --- a/rest/parameter.go +++ b/rest/internal/request_parameter.go @@ -1,4 +1,4 @@ -package rest +package internal import ( "encoding/json" @@ -9,9 +9,10 @@ import ( "slices" "strconv" "strings" + "time" + "github.com/google/uuid" rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" - "github.com/hasura/ndc-rest/rest/internal" "github.com/hasura/ndc-sdk-go/schema" "github.com/hasura/ndc-sdk-go/utils" sdkUtils "github.com/hasura/ndc-sdk-go/utils" @@ -19,24 +20,25 @@ import ( var urlAndHeaderLocations = []rest.ParameterLocation{rest.InPath, rest.InQuery, rest.InPath} -func (c *RESTConnector) evalURLAndHeaderParameters(request *rest.Request, argumentsSchema map[string]rest.ArgumentInfo, arguments map[string]any) (string, http.Header, error) { - endpoint, err := url.Parse(request.URL) +// evaluate URL and header parameters +func (c *RequestBuilder) evalURLAndHeaderParameters() (string, http.Header, error) { + endpoint, err := url.Parse(c.Operation.Request.URL) if err != nil { return "", nil, err } headers := http.Header{} - for k, h := range request.Headers { + for k, h := range c.Operation.Request.Headers { v := h.Value() if v != nil && *v != "" { headers.Add(k, *v) } } - for argumentKey, argumentInfo := range argumentsSchema { + for argumentKey, argumentInfo := range c.Operation.Arguments { if argumentInfo.Rest == nil || !slices.Contains(urlAndHeaderLocations, argumentInfo.Rest.In) { continue } - if err := c.evalURLAndHeaderParameterBySchema(endpoint, &headers, &argumentInfo, arguments[argumentKey]); err != nil { + if err := c.evalURLAndHeaderParameterBySchema(endpoint, &headers, &argumentInfo, c.Operation.Arguments[argumentKey]); err != nil { return "", nil, fmt.Errorf("%s: %w", argumentKey, err) } } @@ -46,7 +48,7 @@ func (c *RESTConnector) evalURLAndHeaderParameters(request *rest.Request, argume // the query parameters serialization follows [OAS 3.1 spec] // // [OAS 3.1 spec]: https://swagger.io/docs/specification/serialization/ -func (c *RESTConnector) evalURLAndHeaderParameterBySchema(endpoint *url.URL, header *http.Header, argumentInfo *rest.ArgumentInfo, value any) error { +func (c *RequestBuilder) evalURLAndHeaderParameterBySchema(endpoint *url.URL, header *http.Header, argumentInfo *rest.ArgumentInfo, value any) error { queryParams, err := c.encodeParameterValues(&rest.ObjectField{ ObjectField: schema.ObjectField{ Type: argumentInfo.Type, @@ -82,17 +84,13 @@ func (c *RESTConnector) evalURLAndHeaderParameterBySchema(endpoint *url.URL, hea return nil } -func (c *RESTConnector) encodeParameterValues(objectField *rest.ObjectField, reflectValue reflect.Value, fieldPaths []string) (internal.ParameterItems, error) { - results := internal.ParameterItems{} +func (c *RequestBuilder) encodeParameterValues(objectField *rest.ObjectField, reflectValue reflect.Value, fieldPaths []string) (ParameterItems, error) { + results := ParameterItems{} if reflectValue.Kind() == reflect.Invalid { return results, nil } typeSchema := objectField.Rest - var typeName string - if len(typeSchema.Type) > 0 { - typeName = strings.ToLower(typeSchema.Type[0]) - } reflectValue, nonNull := sdkUtils.UnwrapPointerFromReflectValue(reflectValue) switch ty := objectField.Type.Interface().(type) { @@ -107,10 +105,14 @@ func (c *RESTConnector) encodeParameterValues(objectField *rest.ObjectField, ref Rest: typeSchema, }, reflectValue, fieldPaths) case *schema.ArrayType: - if !slices.Contains([]reflect.Kind{reflect.Slice, reflect.Array}, reflectValue.Kind()) { - return nil, fmt.Errorf("%s: expected array, got %v", strings.Join(fieldPaths, ""), reflectValue.Interface()) + if !nonNull { + return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), errArgumentRequired) } - for i := range reflectValue.Len() { + elements, ok := reflectValue.Interface().([]any) + if !ok { + return nil, fmt.Errorf("%s: expected array, got <%s> %v", strings.Join(fieldPaths, ""), reflectValue.Kind(), reflectValue.Interface()) + } + for i, elem := range elements { propPaths := append(fieldPaths, "["+strconv.Itoa(i)+"]") outputs, err := c.encodeParameterValues(&rest.ObjectField{ @@ -118,98 +120,108 @@ func (c *RESTConnector) encodeParameterValues(objectField *rest.ObjectField, ref Type: ty.ElementType, }, Rest: typeSchema.Items, - }, reflectValue.Index(i), propPaths) + }, reflect.ValueOf(elem), propPaths) if err != nil { return nil, err } for _, output := range outputs { - keys := output.Keys() - if len(keys) == 0 { - results.Add([]internal.Key{internal.NewKey("")}, output.Values()) - } else { - results.Add(append([]internal.Key{internal.NewIndexKey(i)}, output.Keys()...), output.Values()) - } + results.Add(append([]Key{NewIndexKey(i)}, output.Keys()...), output.Values()) } } return results, nil case *schema.NamedType: - iScalar, ok := c.schema.ScalarTypes[typeName] + if !nonNull { + return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), errArgumentRequired) + } + iScalar, ok := c.Schema.ScalarTypes[ty.Name] if ok { - return encodeParameterReflectionValues(reflectValue, &iScalar, fieldPaths) + return encodeScalarParameterReflectionValues(reflectValue, &iScalar, fieldPaths) } - default: - return nil, fmt.Errorf("invalid type %v", objectField.Type) + kind := reflectValue.Kind() + objectInfo, ok := c.Schema.ObjectTypes[ty.Name] + if !ok { + return nil, fmt.Errorf("%s: invalid type %s", strings.Join(fieldPaths, ""), ty.Name) + } + + switch kind { + case reflect.Map, reflect.Interface: + anyValue := reflectValue.Interface() + object, ok := anyValue.(map[string]any) + if !ok { + return nil, fmt.Errorf("%s: failed to evaluate object, got <%s> %v", strings.Join(fieldPaths, ""), kind, anyValue) + } + for key, fieldInfo := range objectInfo.Fields { + propPaths := append(fieldPaths, "."+key) + fieldVal := object[key] + output, err := c.encodeParameterValues(&fieldInfo, reflect.ValueOf(fieldVal), propPaths) + if err != nil { + return nil, err + } + + for _, pair := range output { + results.Add(append([]Key{NewKey(key)}, pair.Keys()...), pair.Values()) + } + } + case reflect.Struct: + reflectType := reflectValue.Type() + for fieldIndex := range reflectValue.NumField() { + fieldVal := reflectValue.Field(fieldIndex) + fieldType := reflectType.Field(fieldIndex) + fieldInfo, ok := objectInfo.Fields[fieldType.Name] + if !ok { + continue + } + propPaths := append(fieldPaths, "."+fieldType.Name) + output, err := c.encodeParameterValues(&fieldInfo, fieldVal, propPaths) + if err != nil { + return nil, err + } + + for _, pair := range output { + results.Add(append([]Key{NewKey(fieldType.Name)}, pair.Keys()...), pair.Values()) + } + } + default: + return nil, fmt.Errorf("%s: failed to evaluate object, got %s", strings.Join(fieldPaths, ""), kind) + } + return results, nil } - // switch typeName { - // case "object": - // objectInfo, ok := c.schema.ObjectTypes - // if len(typeSchema.Properties) == 0 { - // return results, nil - // } - // mapValue, ok := value.(map[string]any) - // if !ok { - // return nil, fmt.Errorf("%s: expected object, got %v", strings.Join(fieldPaths, ""), value) - // } - - // for k, prop := range typeSchema.Properties { - // propPaths := append(fieldPaths, "."+k) - // fieldVal, ok := mapValue[k] - // if !ok { - // if !prop.Nullable { - // return nil, fmt.Errorf("parameter %s is required", strings.Join(propPaths, "")) - // } - // continue - // } - - // output, err := c.encodeParameterValues(&prop, fieldVal, propPaths) - // if err != nil { - // return nil, err - // } - - // for _, pair := range output { - // results.Add(append([]internal.Key{internal.NewKey(k)}, pair.Keys()...), pair.Values()) - // } - // } - - // return results, nil - // } + return nil, fmt.Errorf("%s: invalid type %v", strings.Join(fieldPaths, ""), objectField.Type) } -func encodeParameterReflectionValues(reflectValue reflect.Value, scalar *schema.ScalarType, fieldPaths []string) (internal.ParameterItems, error) { - results := internal.ParameterItems{} - +func encodeScalarParameterReflectionValues(reflectValue reflect.Value, scalar *schema.ScalarType, fieldPaths []string) (ParameterItems, error) { switch sl := scalar.Representation.Interface().(type) { case *schema.TypeRepresentationBoolean: value, err := utils.DecodeBooleanReflection(reflectValue) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{strconv.FormatBool(value)}), + return []ParameterItem{ + NewParameterItem([]Key{}, []string{strconv.FormatBool(value)}), }, nil - case *schema.TypeRepresentationString: + case *schema.TypeRepresentationString, *schema.TypeRepresentationBytes: value, err := utils.DecodeStringReflection(reflectValue) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, []string{value})}, nil + return []ParameterItem{NewParameterItem([]Key{}, []string{value})}, nil case *schema.TypeRepresentationInteger, *schema.TypeRepresentationInt8, *schema.TypeRepresentationInt16, *schema.TypeRepresentationInt32, *schema.TypeRepresentationInt64, *schema.TypeRepresentationBigInteger: value, err := utils.DecodeIntReflection[int64](reflectValue) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{strconv.FormatInt(value, 10)}), + return []ParameterItem{ + NewParameterItem([]Key{}, []string{strconv.FormatInt(value, 10)}), }, nil case *schema.TypeRepresentationNumber, *schema.TypeRepresentationFloat32, *schema.TypeRepresentationFloat64, *schema.TypeRepresentationBigDecimal: value, err := utils.DecodeFloatReflection[float64](reflectValue) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{fmt.Sprint(value)}), + return []ParameterItem{ + NewParameterItem([]Key{}, []string{fmt.Sprint(value)}), }, nil case *schema.TypeRepresentationEnum: value, err := utils.DecodeStringReflection(reflectValue) @@ -219,121 +231,80 @@ func encodeParameterReflectionValues(reflectValue reflect.Value, scalar *schema. if !slices.Contains(sl.OneOf, value) { return nil, fmt.Errorf("%s: the value must be one of %v, got %s", strings.Join(fieldPaths, ""), sl.OneOf, value) } - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, []string{value})}, nil - default: - b, err := json.Marshal(reflectValue.Interface()) + return []ParameterItem{NewParameterItem([]Key{}, []string{value})}, nil + case *schema.TypeRepresentationDate: + value, err := utils.DecodeDateReflection(reflectValue) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - values := []string{strings.Trim(string(b), `"`)} - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, values)}, nil - } - - kind := reflectValue.Kind() - switch kind { - case reflect.Bool: - // if strings.Contains([]schema.ScalarType{schema.TypeRepresentationBoolean, schema.TypeRepresentationJSON}, scalar.Representation.Type()) == - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{strconv.FormatBool(reflectValue.Bool())}), - }, nil - case reflect.String: - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, []string{reflectValue.String()})}, nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{strconv.FormatInt(reflectValue.Int(), 10)}), + return []ParameterItem{ + NewParameterItem([]Key{}, []string{value.Format(time.DateOnly)}), }, nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{strconv.FormatUint(reflectValue.Uint(), 10)}), - }, nil - case reflect.Float32, reflect.Float64: - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{fmt.Sprintf("%f", reflectValue.Float())}), - }, nil - case reflect.Slice: - valueLen := reflectValue.Len() - for i := range valueLen { - propPaths := append(fieldPaths, fmt.Sprintf("[%d]", i)) - elem := reflectValue.Index(i) - - outputs, err := encodeParameterReflectionValues(elem, propPaths) - if err != nil { - return nil, err - } - - for _, output := range outputs { - keys := output.Keys() - if len(keys) == 0 { - results.Add([]internal.Key{internal.NewKey("")}, output.Values()) - } else { - results.Add(append([]internal.Key{internal.NewIndexKey(i)}, output.Keys()...), output.Values()) - } - } + case *schema.TypeRepresentationTimestamp, *schema.TypeRepresentationTimestampTZ: + value, err := utils.DecodeDateTimeReflection(reflectValue) + if err != nil { + return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return results, nil - case reflect.Map: - keys := reflectValue.MapKeys() - if len(keys) == 0 { - return results, nil + return []ParameterItem{ + NewParameterItem([]Key{}, []string{value.Format(time.RFC3339)}), + }, nil + case *schema.TypeRepresentationUUID: + rawValue, err := utils.DecodeStringReflection(reflectValue) + if err != nil { + return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - - for _, k := range keys { - key := fmt.Sprint(k.Interface()) - propPaths := append(fieldPaths, "."+key) - fieldVal := reflectValue.MapIndex(k) - - output, err := encodeParameterReflectionValues(fieldVal, propPaths) - if err != nil { - return nil, err - } - - for _, pair := range output { - results.Add(append([]internal.Key{internal.NewKey(key)}, pair.Keys()...), pair.Values()) - } + _, err = uuid.Parse(rawValue) + if err != nil { + return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - - return results, nil - case reflect.Struct, reflect.Interface: + return []ParameterItem{NewParameterItem([]Key{}, []string{rawValue})}, nil + default: b, err := json.Marshal(reflectValue.Interface()) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } values := []string{strings.Trim(string(b), `"`)} - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, values)}, nil - default: - return nil, fmt.Errorf("%s: failed to encode parameter, got %s", strings.Join(fieldPaths, ""), kind) + return []ParameterItem{NewParameterItem([]Key{}, values)}, nil } } -func buildParamQueryKey(name string, encObject rest.EncodingObject, keys internal.Keys, values []string) string { +func buildParamQueryKey(name string, encObject rest.EncodingObject, keys Keys, values []string) string { resultKeys := []string{} if name != "" { resultKeys = append(resultKeys, name) } + keysLength := len(keys) // non-explode or explode form object does not require param name // /users?role=admin&firstName=Alex if (encObject.Explode != nil && !*encObject.Explode) || - (len(values) == 1 && encObject.Style == rest.EncodingStyleForm && (len(keys) > 1 || (len(keys) == 1 && !keys[0].IsEmpty()))) { + (len(values) == 1 && encObject.Style == rest.EncodingStyleForm && (keysLength > 1 || (keysLength == 1 && !keys[0].IsEmpty()))) { resultKeys = []string{} } - if len(keys) > 0 { - if encObject.Style != rest.EncodingStyleDeepObject && keys[len(keys)-1].IsEmpty() { - keys = keys[:len(keys)-1] + if keysLength > 0 { + if encObject.Style != rest.EncodingStyleDeepObject && keys[keysLength-1].IsEmpty() { + keys = keys[:keysLength-1] } - for _, k := range keys { + + for i, key := range keys { if len(resultKeys) == 0 { - resultKeys = append(resultKeys, k.String()) - } else { - resultKeys = append(resultKeys, fmt.Sprintf("[%s]", k)) + resultKeys = append(resultKeys, key.String()) + continue + } + if i == len(keys)-1 && key.Index() != nil { + // the last element of array in the deepObject style doesn't have index + resultKeys = append(resultKeys, "[]") + continue } + + resultKeys = append(resultKeys, "["+key.String()+"]") } } return strings.Join(resultKeys, "") } -func evalQueryParameterURL(q *url.Values, name string, encObject rest.EncodingObject, keys internal.Keys, values []string) { +func evalQueryParameterURL(q *url.Values, name string, encObject rest.EncodingObject, keys Keys, values []string) { if len(values) == 0 { return } @@ -383,42 +354,42 @@ func encodeQueryValues(qValues url.Values, allowReserved bool) string { return builder.String() } -func encodeParameterBool(value any, fieldPaths []string) (internal.ParameterItems, error) { +func encodeParameterBool(value any, fieldPaths []string) (ParameterItems, error) { result, err := sdkUtils.DecodeBoolean(value) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{ - internal.NewParameterItem([]internal.Key{}, []string{strconv.FormatBool(result)}), + return []ParameterItem{ + NewParameterItem([]Key{}, []string{strconv.FormatBool(result)}), }, nil } -func encodeParameterString(value any, fieldPaths []string) (internal.ParameterItems, error) { +func encodeParameterString(value any, fieldPaths []string) (ParameterItems, error) { result, err := sdkUtils.DecodeString(value) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, []string{result})}, nil + return []ParameterItem{NewParameterItem([]Key{}, []string{result})}, nil } -func encodeParameterInt(value any, fieldPaths []string) (internal.ParameterItems, error) { +func encodeParameterInt(value any, fieldPaths []string) (ParameterItems, error) { intValue, err := sdkUtils.DecodeInt[int64](value) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, []string{strconv.FormatInt(intValue, 10)})}, nil + return []ParameterItem{NewParameterItem([]Key{}, []string{strconv.FormatInt(intValue, 10)})}, nil } -func encodeParameterFloat(value any, fieldPaths []string) (internal.ParameterItems, error) { +func encodeParameterFloat(value any, fieldPaths []string) (ParameterItems, error) { floatValue, err := sdkUtils.DecodeFloat[float64](value) if err != nil { return nil, fmt.Errorf("%s: %w", strings.Join(fieldPaths, ""), err) } - return []internal.ParameterItem{internal.NewParameterItem([]internal.Key{}, []string{fmt.Sprintf("%f", floatValue)})}, nil + return []ParameterItem{NewParameterItem([]Key{}, []string{fmt.Sprintf("%f", floatValue)})}, nil } -func setHeaderParameters(header *http.Header, param *rest.RequestParameter, queryParams internal.ParameterItems) { +func setHeaderParameters(header *http.Header, param *rest.RequestParameter, queryParams ParameterItems) { defaultParam := queryParams.FindDefault() // the param is an array if defaultParam != nil { diff --git a/rest/internal/request_parameter_test.go b/rest/internal/request_parameter_test.go new file mode 100644 index 0000000..1d7f761 --- /dev/null +++ b/rest/internal/request_parameter_test.go @@ -0,0 +1,306 @@ +package internal + +import ( + "encoding/json" + "net/url" + "testing" + + rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" + "github.com/hasura/ndc-sdk-go/utils" + "gotest.tools/v3/assert" +) + +func TestEvalQueryParameterURL(t *testing.T) { + testCases := []struct { + name string + param *rest.RequestParameter + keys []Key + values []string + expected string + }{ + { + name: "empty", + param: &rest.RequestParameter{}, + keys: []Key{NewKey("")}, + values: []string{}, + expected: "", + }, + { + name: "form_explode_single", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{}, + values: []string{"3"}, + expected: "id=3", + }, + { + name: "form_single", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(false), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{NewKey("")}, + values: []string{"3"}, + expected: "id=3", + }, + { + name: "form_explode_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{NewKey("")}, + values: []string{"3", "4", "5"}, + expected: "id=3&id=4&id=5", + }, + { + name: "spaceDelimited_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(false), + Style: rest.EncodingStyleSpaceDelimited, + }, + }, + keys: []Key{NewKey("")}, + values: []string{"3", "4", "5"}, + expected: "id=3 4 5", + }, + { + name: "spaceDelimited_explode_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleSpaceDelimited, + }, + }, + keys: []Key{NewKey("")}, + values: []string{"3", "4", "5"}, + expected: "id=3&id=4&id=5", + }, + + { + name: "pipeDelimited_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(false), + Style: rest.EncodingStylePipeDelimited, + }, + }, + keys: []Key{NewKey("")}, + values: []string{"3", "4", "5"}, + expected: "id=3|4|5", + }, + { + name: "pipeDelimited_explode_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStylePipeDelimited, + }, + }, + keys: []Key{NewKey("")}, + values: []string{"3", "4", "5"}, + expected: "id=3&id=4&id=5", + }, + { + name: "deepObject_explode_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleDeepObject, + }, + }, + keys: []Key{NewKey("")}, + values: []string{"3", "4", "5"}, + expected: "id[]=3&id[]=4&id[]=5", + }, + { + name: "form_object", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(false), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{NewKey("role")}, + values: []string{"admin"}, + expected: "id=role,admin", + }, + { + name: "form_explode_object", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{NewKey("role")}, + values: []string{"admin"}, + expected: "role=admin", + }, + { + name: "deepObject_explode_object", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleDeepObject, + }, + }, + keys: []Key{NewKey("role")}, + values: []string{"admin"}, + expected: "id[role]=admin", + }, + { + name: "form_array_object", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(false), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{NewKey("role"), NewKey(""), NewKey("user"), NewKey("")}, + values: []string{"admin"}, + expected: "id=role[][user],admin", + }, + { + name: "form_explode_array_object", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{NewKey("role"), NewKey(""), NewKey("user"), NewKey("")}, + values: []string{"admin"}, + expected: "role[][user]=admin", + }, + { + name: "form_explode_array_object_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleForm, + }, + }, + keys: []Key{NewKey("role"), NewKey(""), NewKey("user"), NewKey("")}, + values: []string{"admin", "anonymous"}, + expected: "id[role][][user]=admin&id[role][][user]=anonymous", + }, + { + name: "deepObject_explode_array_object", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleDeepObject, + }, + }, + keys: []Key{NewKey("role"), NewKey(""), NewKey("user"), NewKey("")}, + values: []string{"admin"}, + expected: "id[role][][user][]=admin", + }, + { + name: "deepObject_explode_array_object_multiple", + param: &rest.RequestParameter{ + Name: "id", + EncodingObject: rest.EncodingObject{ + Explode: utils.ToPtr(true), + Style: rest.EncodingStyleDeepObject, + }, + }, + keys: []Key{NewKey("role"), NewKey(""), NewKey("user"), NewKey("")}, + values: []string{"admin", "anonymous"}, + expected: "id[role][][user][]=admin&id[role][][user][]=anonymous", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + qValues := make(url.Values) + evalQueryParameterURL(&qValues, tc.param.Name, tc.param.EncodingObject, tc.keys, tc.values) + assert.Equal(t, tc.expected, encodeQueryValues(qValues, true)) + }) + } +} + +func TestEvalURLAndHeaderParameters(t *testing.T) { + testCases := []struct { + name string + rawArguments string + expectedURL string + errorMsg string + headers map[string]string + }{ + { + name: "GetInvoices", + rawArguments: `{ + "collection_method": "charge_automatically", + "created": null, + "customer": "UFGkQ6qKPc", + "due_date": null, + "ending_before": "bAOW2sHpAG", + "expand": ["HbZr0T5gf8"], + "limit": 19522, + "starting_after": "McghIoX8E7", + "status": "draft", + "subscription": "UpqQmfokoF" + }`, + expectedURL: "/v1/invoices?collection_method=charge_automatically&customer=UFGkQ6qKPc&ending_before=bAOW2sHpAG&expand[]=HbZr0T5gf8&limit=19522&starting_after=McghIoX8E7&status=draft&subscription=UpqQmfokoF", + }, + } + + ndcSchema := createMockSchema(t) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var info *rest.OperationInfo + for _, f := range ndcSchema.Functions { + if f.Name == tc.name { + info = f + break + } + } + var arguments map[string]any + assert.NilError(t, json.Unmarshal([]byte(tc.rawArguments), &arguments)) + + builder := RequestBuilder{ + Schema: ndcSchema, + Operation: info, + Arguments: arguments, + } + result, headers, err := builder.evalURLAndHeaderParameters() + if tc.errorMsg != "" { + assert.ErrorContains(t, err, tc.errorMsg) + } else { + assert.NilError(t, err) + decodedValue, err := url.QueryUnescape(result) + assert.NilError(t, err) + assert.Equal(t, tc.expectedURL, decodedValue) + for k, v := range tc.headers { + assert.Equal(t, v, headers.Get(k)) + } + } + }) + } +} diff --git a/rest/internal/types.go b/rest/internal/types.go index 5f0d4c5..29515fe 100644 --- a/rest/internal/types.go +++ b/rest/internal/types.go @@ -2,6 +2,7 @@ package internal import ( "encoding/json" + "errors" "fmt" rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" @@ -15,6 +16,11 @@ const ( defaultRetryDelays uint = 1000 ) +var ( + errArgumentRequired = errors.New("argument is required") + errRequestBodyTypeRequired = errors.New("failed to decode request body, empty body type") +) + var defaultRetryHTTPStatus = []int64{429, 500, 502, 503} const ( diff --git a/rest/mutation.go b/rest/mutation.go index a175072..9abe838 100644 --- a/rest/mutation.go +++ b/rest/mutation.go @@ -45,11 +45,11 @@ func (c *RESTConnector) execProcedure(ctx context.Context, operation *schema.Mut }) } - endpoint, headers, err := c.evalURLAndHeaderParameters(procedure.Request, procedure.Arguments, rawArgs) + // 2. build the request + builder := internal.NewRequestBuilder(c.schema, procedure, rawArgs) + httpRequest, err := builder.Build() if err != nil { - return nil, schema.BadRequestError("failed to evaluate URL and Headers from parameters", map[string]any{ - "cause": err.Error(), - }) + return nil, err } restOptions, err := parseRESTOptionsFromArguments(procedure.Arguments, rawArgs[internal.RESTOptionsArgumentName]) @@ -59,14 +59,9 @@ func (c *RESTConnector) execProcedure(ctx context.Context, operation *schema.Mut }) } - // 2. create and execute request - // 3. evaluate response selection restOptions.Settings = settings - httpRequest, err := c.createRequest(procedure, endpoint, headers, rawArgs) - if err != nil { - return nil, err - } + // 3. execute the request and evaluate response selection result, err := c.client.Send(ctx, httpRequest, operation.Fields, procedure.ResultType, restOptions) if err != nil { return nil, err diff --git a/rest/parameter_test.go b/rest/parameter_test.go deleted file mode 100644 index a05f02c..0000000 --- a/rest/parameter_test.go +++ /dev/null @@ -1,896 +0,0 @@ -package rest - -import ( - "encoding/json" - "net/url" - "testing" - - rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" - "github.com/hasura/ndc-rest/rest/internal" - "github.com/hasura/ndc-sdk-go/schema" - "github.com/hasura/ndc-sdk-go/utils" - "gotest.tools/v3/assert" -) - -func TestEvalQueryParameterURL(t *testing.T) { - testCases := []struct { - name string - param *rest.RequestParameter - keys []internal.Key - values []string - expected string - }{ - { - name: "empty", - param: &rest.RequestParameter{}, - keys: []internal.Key{internal.NewKey("")}, - values: []string{}, - expected: "", - }, - { - name: "form_explode_single", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{}, - values: []string{"3"}, - expected: "id=3", - }, - { - name: "form_single", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(false), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{internal.NewKey("")}, - values: []string{"3"}, - expected: "id=3", - }, - { - name: "form_explode_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{internal.NewKey("")}, - values: []string{"3", "4", "5"}, - expected: "id=3&id=4&id=5", - }, - { - name: "spaceDelimited_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(false), - Style: rest.EncodingStyleSpaceDelimited, - }, - }, - keys: []internal.Key{internal.NewKey("")}, - values: []string{"3", "4", "5"}, - expected: "id=3 4 5", - }, - { - name: "spaceDelimited_explode_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleSpaceDelimited, - }, - }, - keys: []internal.Key{internal.NewKey("")}, - values: []string{"3", "4", "5"}, - expected: "id=3&id=4&id=5", - }, - - { - name: "pipeDelimited_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(false), - Style: rest.EncodingStylePipeDelimited, - }, - }, - keys: []internal.Key{internal.NewKey("")}, - values: []string{"3", "4", "5"}, - expected: "id=3|4|5", - }, - { - name: "pipeDelimited_explode_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStylePipeDelimited, - }, - }, - keys: []internal.Key{internal.NewKey("")}, - values: []string{"3", "4", "5"}, - expected: "id=3&id=4&id=5", - }, - { - name: "deepObject_explode_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleDeepObject, - }, - }, - keys: []internal.Key{internal.NewKey("")}, - values: []string{"3", "4", "5"}, - expected: "id[]=3&id[]=4&id[]=5", - }, - { - name: "form_object", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(false), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{internal.NewKey("role")}, - values: []string{"admin"}, - expected: "id=role,admin", - }, - { - name: "form_explode_object", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{internal.NewKey("role")}, - values: []string{"admin"}, - expected: "role=admin", - }, - { - name: "deepObject_explode_object", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleDeepObject, - }, - }, - keys: []internal.Key{internal.NewKey("role")}, - values: []string{"admin"}, - expected: "id[role]=admin", - }, - { - name: "form_array_object", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(false), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{internal.NewKey("role"), internal.NewKey(""), internal.NewKey("user"), internal.NewKey("")}, - values: []string{"admin"}, - expected: "id=role[][user],admin", - }, - { - name: "form_explode_array_object", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{internal.NewKey("role"), internal.NewKey(""), internal.NewKey("user"), internal.NewKey("")}, - values: []string{"admin"}, - expected: "role[][user]=admin", - }, - { - name: "form_explode_array_object_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleForm, - }, - }, - keys: []internal.Key{internal.NewKey("role"), internal.NewKey(""), internal.NewKey("user"), internal.NewKey("")}, - values: []string{"admin", "anonymous"}, - expected: "id[role][][user]=admin&id[role][][user]=anonymous", - }, - { - name: "deepObject_explode_array_object", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleDeepObject, - }, - }, - keys: []internal.Key{internal.NewKey("role"), internal.NewKey(""), internal.NewKey("user"), internal.NewKey("")}, - values: []string{"admin"}, - expected: "id[role][][user][]=admin", - }, - { - name: "deepObject_explode_array_object_multiple", - param: &rest.RequestParameter{ - Name: "id", - EncodingObject: rest.EncodingObject{ - Explode: utils.ToPtr(true), - Style: rest.EncodingStyleDeepObject, - }, - }, - keys: []internal.Key{internal.NewKey("role"), internal.NewKey(""), internal.NewKey("user"), internal.NewKey("")}, - values: []string{"admin", "anonymous"}, - expected: "id[role][][user][]=admin&id[role][][user][]=anonymous", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - qValues := make(url.Values) - evalQueryParameterURL(&qValues, tc.param.Name, tc.param.EncodingObject, tc.keys, tc.values) - assert.Equal(t, tc.expected, encodeQueryValues(qValues, true)) - }) - } -} - -func TestEncodeParameterValues(t *testing.T) { - testCases := []struct { - name string - rawProcedureSchema string - rawArguments string - expectedURL string - errorMsg string - headers map[string]string - }{ - - { - name: "/param-array", - rawProcedureSchema: `{ - "request": { - "url": "/param-array", - "method": "post", - "parameters": [ - { - "style": "deepObject", - "explode": true, - "name": "expand", - "in": "query", - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - } - } - ], - "requestBody": { - "contentType": "application/x-www-form-urlencoded" - } - }, - "arguments": { - "expand": { - "type": { - "type": "nullable", - "underlying_type": { - "type": "array", - "element_type": { - "name": "String", - "type": "named" - } - } - } - } - }, - "name": "PostCheckoutSessions", - "result_type": { - "name": "Order", - "type": "named" - } - }`, - rawArguments: `{ - "expand": ["foo"] - }`, - expectedURL: "/param-array?expand[]=foo", - }, - { - name: "/v1/checkout/sessions", - headers: map[string]string{ - "foo": "bar", - }, - rawProcedureSchema: `{ - "request": { - "url": "/v1/checkout/sessions", - "method": "post", - "headers": { - "foo": "bar" - }, - "parameters": [ - { - "style": "deepObject", - "explode": true, - "name": "automatic_tax", - "in": "query", - "schema": { - "type": "object", - "nullable": true, - "properties": { - "enabled": { - "type": "boolean" - }, - "liability": { - "type": "object", - "nullable": true, - "properties": { - "account": { - "type": "string", - "nullable": true - }, - "type": { - "type": "string", - "enum": [ - "account", - "self" - ] - } - } - } - } - } - }, - { - "style": "deepObject", - "explode": true, - "name": "subscription_data", - "in": "query", - "schema": { - "type": "object", - "nullable": true, - "properties": { - "application_fee_percent": { - "type": "number", - "nullable": true - }, - "billing_cycle_anchor": { - "type": "integer", - "format": "unix-time", - "nullable": true - }, - "default_tax_rates": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "maxLength": 5000 - } - }, - "description": { - "type": "string", - "nullable": true, - "maxLength": 500 - }, - "invoice_settings": { - "type": "object", - "nullable": true, - "properties": { - "issuer": { - "type": "object", - "nullable": true, - "properties": { - "account": { - "type": "string", - "nullable": true - }, - "type": { - "type": "string", - "enum": [ - "account", - "self" - ] - } - } - } - } - }, - "metadata": { - "type": "JSON", - "nullable": true - }, - "on_behalf_of": { - "type": "string", - "nullable": true - }, - "proration_behavior": { - "type": "string", - "nullable": true, - "enum": [ - "create_prorations", - "none" - ] - }, - "transfer_data": { - "type": "object", - "nullable": true, - "properties": { - "amount_percent": { - "type": "number", - "nullable": true - }, - "destination": { - "type": "string" - } - } - }, - "trial_end": { - "type": "integer", - "format": "unix-time", - "nullable": true - }, - "trial_period_days": { - "type": "integer", - "nullable": true - }, - "trial_settings": { - "type": "object", - "nullable": true, - "properties": { - "end_behavior": { - "type": "object", - "properties": { - "missing_payment_method": { - "type": "string", - "enum": [ - "cancel", - "create_invoice", - "pause" - ] - } - } - } - } - } - } - } - } - ], - "requestBody": { - "contentType": "application/x-www-form-urlencoded" - } - }, - "arguments": { - "automatic_tax": { - "description": "Settings for automatic tax lookup for this session and resulting payments, invoices, and subscriptions.", - "type": { - "type": "nullable", - "underlying_type": { - "name": "AutomaticTaxParams", - "type": "named" - } - } - }, - "subscription_data": { - "type": { - "type": "nullable", - "underlying_type": { - "name": "SubscriptionDataParams", - "type": "named" - } - } - } - }, - "name": "PostCheckoutSessions", - "result_type": { - "name": "Order", - "type": "named" - } - }`, - rawArguments: `{ - "automatic_tax": { - "enabled": false, - "liability": { - "type": "self", - "country": "DE" - } - }, - "subscription_data": { - "description": "nyxWwjZ0JY", - "invoice_settings": { - "issuer": { - "type": "self", - "country": "IT" - } - }, - "metadata": null, - "trial_period_days": 27623, - "trial_settings": { - "end_behavior": { - "missing_payment_method": "cancel" - } - } - } - }`, - expectedURL: "/v1/checkout/sessions?automatic_tax[enabled]=false&automatic_tax[liability][type]=self&subscription_data[description]=nyxWwjZ0JY&subscription_data[invoice_settings][issuer][type]=self&subscription_data[trial_period_days]=27623&subscription_data[trial_settings][end_behavior][missing_payment_method]=cancel", - }, - { - name: "/v1/subscription_schedules", - rawProcedureSchema: `{ - "request": { - "url": "/v1/subscription_schedules", - "method": "post", - "parameters": [ - { - "style": "deepObject", - "explode": true, - "name": "phases", - "in": "query", - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "add_invoice_items": { - "type": "array", - "nullable": true, - "items": { - "type": "object", - "properties": { - "price": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "price_data": { - "type": "object", - "nullable": true, - "properties": { - "currency": { - "type": "String" - }, - "product": { - "type": "String", - "maxLength": 5000 - }, - "tax_behavior": { - "type": "SubscriptionSchedulesTaxBehavior", - "nullable": true - }, - "unit_amount": { - "type": "Int32", - "nullable": true - }, - "unit_amount_decimal": { - "type": "String", - "nullable": true - } - } - }, - "quantity": { - "type": "Int32", - "nullable": true - } - } - } - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "price": { - "type": "String", - "nullable": true, - "maxLength": 5000 - }, - "price_data": { - "type": "object", - "nullable": true, - "properties": { - "currency": { - "type": "String" - }, - "product": { - "type": "String", - "maxLength": 5000 - }, - "recurring": { - "type": "object", - "properties": { - "interval": { - "type": "SubscriptionSchedulesInterval" - }, - "interval_count": { - "type": "Int32", - "nullable": true - } - } - }, - "tax_behavior": { - "type": "SubscriptionSchedulesTaxBehavior", - "nullable": true - }, - "unit_amount": { - "type": "Int32", - "nullable": true - }, - "unit_amount_decimal": { - "type": "String", - "nullable": true - } - } - }, - "quantity": { - "type": "Int32", - "nullable": true - } - } - } - } - } - } - } - } - ], - "requestBody": { - "contentType": "application/x-www-form-urlencoded" - } - }, - "arguments": { - "phases": { - "type": { - "type": "nullable", - "underlying_type": { - "element_type": { - "name": "PostSubscriptionSchedulesScheduleBodyPhases", - "type": "named" - }, - "type": "array" - } - } - } - }, - "name": "PostSubscriptionSchedulesSchedule", - "result_type": { - "name": "SubscriptionSchedule", - "type": "named" - } - }`, - rawArguments: `{ - "phases": [ - { - "add_invoice_items": [ - { - "price": "Brnx6F2SW3", - "price_data": { - "currency": "KutyN1a7f7", - "product": "TS7Fs9Hy8B", - "tax_behavior": "exclusive", - "unit_amount": 2120841752, - "unit_amount_decimal": "GxHaMm19uk" - }, - "quantity": 1365407829 - } - ], - "items": [ - { - "price": "wsJvXbiSV8", - "price_data": { - "currency": "Se3SC2fQcl", - "product": "W9pqDICERA", - "recurring": { - "interval": "month", - "interval_count": 97217333 - }, - "tax_behavior": "inclusive", - "unit_amount": 1972558655, - "unit_amount_decimal": "uu9JdD5mJ0" - }, - "quantity": 1961565488 - } - ] - } - ] - }`, - expectedURL: "/v1/subscription_schedules?phases[0][add_invoice_items][0][price]=Brnx6F2SW3&phases[0][add_invoice_items][0][price_data][currency]=KutyN1a7f7&phases[0][add_invoice_items][0][price_data][product]=TS7Fs9Hy8B&phases[0][add_invoice_items][0][price_data][tax_behavior]=exclusive&phases[0][add_invoice_items][0][price_data][unit_amount]=2120841752&phases[0][add_invoice_items][0][price_data][unit_amount_decimal]=GxHaMm19uk&phases[0][add_invoice_items][0][quantity]=1365407829&phases[0][items][0][price]=wsJvXbiSV8&phases[0][items][0][price_data][currency]=Se3SC2fQcl&phases[0][items][0][price_data][product]=W9pqDICERA&phases[0][items][0][price_data][recurring][interval]=month&phases[0][items][0][price_data][recurring][interval_count]=97217333&phases[0][items][0][price_data][tax_behavior]=inclusive&phases[0][items][0][price_data][unit_amount]=1972558655&phases[0][items][0][price_data][unit_amount_decimal]=uu9JdD5mJ0&phases[0][items][0][quantity]=1961565488", - }, - } - - connector := RESTConnector{ - schema: &schema.SchemaResponse{ - ScalarTypes: schema.SchemaResponseScalarTypes{ - "V1Type": schema.ScalarType{ - AggregateFunctions: schema.ScalarTypeAggregateFunctions{}, - ComparisonOperators: make(map[string]schema.ComparisonOperatorDefinition), - Representation: schema.NewTypeRepresentationEnum([]string{ - "account", - "self", - }).Encode(), - }, - "String": schema.ScalarType{ - AggregateFunctions: schema.ScalarTypeAggregateFunctions{}, - ComparisonOperators: make(map[string]schema.ComparisonOperatorDefinition), - Representation: schema.NewTypeRepresentationString().Encode(), - }, - "JSON": schema.ScalarType{ - AggregateFunctions: schema.ScalarTypeAggregateFunctions{}, - ComparisonOperators: make(map[string]schema.ComparisonOperatorDefinition), - Representation: schema.NewTypeRepresentationJSON().Encode(), - }, - "Int32": schema.ScalarType{ - AggregateFunctions: schema.ScalarTypeAggregateFunctions{}, - ComparisonOperators: make(map[string]schema.ComparisonOperatorDefinition), - Representation: schema.NewTypeRepresentationInt32().Encode(), - }, - }, - ObjectTypes: schema.SchemaResponseObjectTypes{ - "AutomaticTaxParams": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "enabled": schema.ObjectField{ - Type: schema.NewNamedType("Boolean").Encode(), - }, - "liability": schema.ObjectField{ - Type: schema.NewNullableNamedType("Param").Encode(), - }, - }, - }, - "Param": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "account": schema.ObjectField{ - Type: schema.NewNullableNamedType("String").Encode(), - }, - "type": schema.ObjectField{ - Type: schema.NewNamedType("V1Type").Encode(), - }, - }, - }, - "SubscriptionDataParams": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "description": { - Type: schema.NewNullableNamedType("String").Encode(), - }, - "invoice_settings": { - Type: schema.NewNullableNamedType("SubscriptionDataInvoiceSettingsParams").Encode(), - }, - "metadata": { - Type: schema.NewNullableNamedType("JSON").Encode(), - }, - "trial_period_days": { - Type: schema.NewNullableNamedType("Int32").Encode(), - }, - }, - }, - "SubscriptionDataInvoiceSettingsParams": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "issuer": schema.ObjectField{ - Type: schema.NewNullableNamedType("Param").Encode(), - }, - }, - }, - "PostSubscriptionSchedulesScheduleBodyPhases": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "add_invoice_items": schema.ObjectField{ - Type: schema.NewNullableType(schema.NewArrayType(schema.NewNamedType("PostSubscriptionSchedulesScheduleBodyPhasesAddInvoiceItems"))).Encode(), - }, - "items": schema.ObjectField{ - Type: schema.NewArrayType(schema.NewNamedType("PostSubscriptionSchedulesScheduleBodyPhasesItems")).Encode(), - }, - }, - }, - "PostSubscriptionSchedulesScheduleBodyPhasesAddInvoiceItems": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "price": schema.ObjectField{ - Type: schema.NewNullableNamedType("String").Encode(), - }, - "price_data": schema.ObjectField{ - Type: schema.NewNullableNamedType("PostSubscriptionSchedulesScheduleBodyPhasesAddInvoiceItemsPriceData").Encode(), - }, - "quantity": schema.ObjectField{ - Type: schema.NewNullableNamedType("Int32").Encode(), - }, - }, - }, - "PostSubscriptionSchedulesScheduleBodyPhasesAddInvoiceItemsPriceData": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "currency": schema.ObjectField{ - Type: schema.NewNamedType("String").Encode(), - }, - "product": schema.ObjectField{ - Type: schema.NewNamedType("String").Encode(), - }, - "tax_behavior": schema.ObjectField{ - Type: schema.NewNullableNamedType("SubscriptionSchedulesTaxBehavior").Encode(), - }, - "unit_amount": schema.ObjectField{ - Type: schema.NewNullableNamedType("Int32").Encode(), - }, - "unit_amount_decimal": schema.ObjectField{ - Type: schema.NewNullableNamedType("String").Encode(), - }, - }, - }, - "PostSubscriptionSchedulesScheduleBodyPhasesItems": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "price": schema.ObjectField{ - Type: schema.NewNullableNamedType("String").Encode(), - }, - "price_data": schema.ObjectField{ - Type: schema.NewNullableNamedType("PostSubscriptionSchedulesScheduleBodyPhasesItemsPriceData").Encode(), - }, - "quantity": schema.ObjectField{ - Type: schema.NewNullableNamedType("Int32").Encode(), - }, - }, - }, - "PostSubscriptionSchedulesScheduleBodyPhasesItemsPriceData": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "currency": schema.ObjectField{ - Type: schema.NewNamedType("String").Encode(), - }, - "product": schema.ObjectField{ - Type: schema.NewNamedType("String").Encode(), - }, - "recurring": schema.ObjectField{ - Type: schema.NewNamedType("PostSubscriptionSchedulesScheduleBodyPhasesItemsPriceDataRecurring").Encode(), - }, - "tax_behavior": schema.ObjectField{ - Type: schema.NewNullableNamedType("SubscriptionSchedulesTaxBehavior").Encode(), - }, - "unit_amount": schema.ObjectField{ - Type: schema.NewNullableNamedType("Int32").Encode(), - }, - "unit_amount_decimal": schema.ObjectField{ - Type: schema.NewNullableNamedType("String").Encode(), - }, - }, - }, - "PostSubscriptionSchedulesScheduleBodyPhasesItemsPriceDataRecurring": schema.ObjectType{ - Fields: schema.ObjectTypeFields{ - "interval": schema.ObjectField{ - Type: schema.NewNamedType("SubscriptionSchedulesInterval").Encode(), - }, - "interval_count": schema.ObjectField{ - Type: schema.NewNullableNamedType("Int32").Encode(), - }, - }, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - var info rest.OperationInfo - assert.NilError(t, json.Unmarshal([]byte(tc.rawProcedureSchema), &info)) - - var arguments map[string]any - assert.NilError(t, json.Unmarshal([]byte(tc.rawArguments), &arguments)) - - result, headers, err := connector.evalURLAndHeaderParameters(info.Request, info.Arguments, arguments) - if tc.errorMsg != "" { - assert.ErrorContains(t, err, tc.errorMsg) - } else { - assert.NilError(t, err) - decodedValue, err := url.QueryUnescape(result) - assert.NilError(t, err) - assert.Equal(t, tc.expectedURL, decodedValue) - for k, v := range tc.headers { - assert.Equal(t, v, headers.Get(k)) - } - } - }) - } -} diff --git a/rest/query.go b/rest/query.go index 729d02f..7c830ed 100644 --- a/rest/query.go +++ b/rest/query.go @@ -52,11 +52,11 @@ func (c *RESTConnector) execQuery(ctx context.Context, request *schema.QueryRequ }) } - endpoint, headers, err := c.evalURLAndHeaderParameters(function.Request, function.Arguments, rawArgs) + // 2. build the request + builder := internal.NewRequestBuilder(c.schema, function, rawArgs) + httpRequest, err := builder.Build() if err != nil { - return nil, schema.UnprocessableContentError("failed to evaluate URL and Headers from parameters", map[string]any{ - "cause": err.Error(), - }) + return nil, err } restOptions, err := parseRESTOptionsFromArguments(function.Arguments, rawArgs[internal.RESTOptionsArgumentName]) @@ -66,13 +66,8 @@ func (c *RESTConnector) execQuery(ctx context.Context, request *schema.QueryRequ }) } - // 2. create and execute request - // 3. evaluate response selection restOptions.Settings = settings - httpRequest, err := c.createRequest(function, endpoint, headers, nil) - if err != nil { - return nil, err - } + // 3. execute request and evaluate response selection return c.client.Send(ctx, httpRequest, queryFields, function.ResultType, restOptions) } diff --git a/rest/request.go b/rest/request.go deleted file mode 100644 index f754e09..0000000 --- a/rest/request.go +++ /dev/null @@ -1,251 +0,0 @@ -package rest - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "slices" - "strings" - - rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" - "github.com/hasura/ndc-rest/rest/internal" - "github.com/hasura/ndc-sdk-go/utils" -) - -func (c *RESTConnector) createRequest(operation *rest.OperationInfo, endpoint string, headers http.Header, arguments map[string]any) (*internal.RetryableRequest, error) { - var buffer io.ReadSeeker - - rawRequest := operation.Request - contentType := contentTypeJSON - bodyInfo, infoOk := operation.Arguments[rest.BodyKey] - bodyData, ok := arguments[rest.BodyKey] - - if rawRequest.RequestBody != nil { - contentType = rawRequest.RequestBody.ContentType - if ok && bodyData != nil { - var err error - binaryBody := getRequestUploadBody(rawRequest) - if binaryBody != nil { - b64, err := utils.DecodeString(bodyData) - if err != nil { - return nil, err - } - dataURI, err := internal.DecodeDataURI(b64) - if err != nil { - return nil, err - } - buffer = bytes.NewReader([]byte(dataURI.Data)) - } else if strings.HasPrefix(contentType, "text/") { - buffer = bytes.NewReader([]byte(fmt.Sprint(bodyData))) - } else if strings.HasPrefix(contentType, "multipart/") { - buffer, contentType, err = c.createMultipartForm(rawRequest.RequestBody, &bodyInfo, arguments) - if err != nil { - return nil, err - } - } else { - switch contentType { - case rest.ContentTypeFormURLEncoded: - buffer, err = c.createFormURLEncoded(rawRequest.RequestBody, &bodyInfo, bodyData) - if err != nil { - return nil, err - } - case rest.ContentTypeJSON, "": - bodyBytes, err := json.Marshal(bodyData) - if err != nil { - return nil, err - } - - buffer = bytes.NewReader(bodyBytes) - default: - return nil, fmt.Errorf("unsupported content type %s", contentType) - } - } - } else if infoOk && bodyInfo.Rest != nil && bodyInfo.Rest.Schema != nil && !bodyInfo.Rest.Schema.Nullable { - return nil, errors.New("request body is required") - } - } - - request := &internal.RetryableRequest{ - URL: endpoint, - RawRequest: rawRequest, - ContentType: contentType, - Headers: headers, - Body: buffer, - Timeout: rawRequest.Timeout, - Retry: rawRequest.Retry, - } - - return request, nil -} - -func (c *RESTConnector) createFormURLEncoded(reqBody *rest.RequestBody, bodyInfo *rest.ArgumentInfo, bodyData any) (io.ReadSeeker, error) { - queryParams, err := c.encodeParameterValues(bodyInfo.Rest.Schema, bodyData, []string{"body"}) - if err != nil { - return nil, err - } - - if len(queryParams) == 0 { - return nil, nil - } - q := url.Values{} - for _, qp := range queryParams { - keys := qp.Keys() - encoding := rest.EncodingObject{} - if len(keys) > 0 && len(reqBody.Encoding) > 0 { - enc, ok := reqBody.Encoding[keys[0].String()] - if ok { - encoding = enc - } - } - evalQueryParameterURL(&q, "", encoding, qp.Keys(), qp.Values()) - } - rawQuery := encodeQueryValues(q, true) - - return bytes.NewReader([]byte(rawQuery)), nil -} - -func (c *RESTConnector) createMultipartForm(reqBody *rest.RequestBody, bodyInfo *rest.ArgumentInfo, arguments map[string]any) (io.ReadSeeker, string, error) { - bodyData := arguments["body"] - - buffer := new(bytes.Buffer) - writer := internal.NewMultipartWriter(buffer) - dataMap, ok := bodyData.(map[string]any) - if !ok { - return nil, "", fmt.Errorf("failed to decode request body, expect object, got: %v", bodyData) - } - if slices.Contains(bodyInfo.Rest.Schema.Type, "object") || len(bodyInfo.Rest.Schema.Properties) == 0 { - return nil, "", errors.New("invalid object schema for multipart") - } - - for key, value := range dataMap { - prop, ok := bodyInfo.Rest.Schema.Properties[key] - if !ok { - continue - } - var enc *rest.EncodingObject - if len(reqBody.Encoding) > 0 { - en, ok := reqBody.Encoding[key] - if ok { - enc = &en - } - } - err := c.evalMultipartFieldValue(writer, arguments, key, value, &prop, enc) - if err != nil { - return nil, "", fmt.Errorf("%s: %w", key, err) - } - } - if err := writer.Close(); err != nil { - return nil, "", err - } - - reader := bytes.NewReader(buffer.Bytes()) - buffer.Reset() - - return reader, writer.FormDataContentType(), nil -} - -func (c *RESTConnector) evalMultipartFieldValue(w *internal.MultipartWriter, arguments map[string]any, name string, value any, typeSchema *rest.TypeSchema, encObject *rest.EncodingObject) error { - if utils.IsNil(value) { - return nil - } - - var headers http.Header - var err error - if encObject != nil && len(encObject.Headers) > 0 { - headers, err = c.evalEncodingHeaders(encObject.Headers, arguments) - if err != nil { - return err - } - } - - var typeName string - if len(typeSchema.Type) > 0 { - typeName = typeSchema.Type[0] - } - if slices.Contains([]string{"object", "array"}, typeName) && (encObject == nil || slices.Contains(encObject.ContentType, rest.ContentTypeJSON)) { - return w.WriteJSON(name, value, headers) - } - - switch typeName { - case "file", string(rest.ScalarBinary): - return w.WriteDataURI(name, value, headers) - default: - params, err := c.encodeParameterValues(typeSchema, value, []string{}) - if err != nil { - return err - } - - if len(params) == 0 { - return nil - } - - for _, p := range params { - keys := p.Keys() - values := p.Values() - fieldName := name - - if len(keys) > 0 { - keys = append([]internal.Key{internal.NewKey(name)}, keys...) - fieldName = keys.String() - } - - if typeName == "array" || len(values) > 1 { - fieldName += "[]" - for _, v := range values { - if err = w.WriteField(fieldName, v, headers); err != nil { - return err - } - } - } else if len(values) == 1 { - if err = w.WriteField(fieldName, values[0], headers); err != nil { - return err - } - } - } - } - - return nil -} - -func (c *RESTConnector) evalEncodingHeaders(encHeaders map[string]rest.RequestParameter, arguments map[string]any) (http.Header, error) { - results := http.Header{} - for key, param := range encHeaders { - argumentName := param.ArgumentName - if argumentName == "" { - argumentName = key - } - rawHeaderValue, ok := arguments[argumentName] - if !ok { - continue - } - - headerParams, err := c.encodeParameterValues(param.Schema, rawHeaderValue, []string{}) - if err != nil { - return nil, err - } - - param.Name = key - setHeaderParameters(&results, ¶m, headerParams) - } - - return results, nil -} - -func getRequestUploadBody(rawRequest *rest.Request) *rest.RequestBody { - if rawRequest.RequestBody == nil { - return nil - } - if rawRequest.RequestBody.ContentType == "application/octet-stream" { - return rawRequest.RequestBody - } - - // TODO - // if rawRequest.RequestBody.Schema != nil && slices.Contains(rawRequest.RequestBody.Schema.Type, string(rest.ScalarBinary)) { - // return rawRequest.RequestBody - // } - return nil -} diff --git a/rest/request_test.go b/rest/request_test.go deleted file mode 100644 index aae95a7..0000000 --- a/rest/request_test.go +++ /dev/null @@ -1,325 +0,0 @@ -package rest - -import ( - "encoding/json" - "io" - "mime" - "mime/multipart" - "net/http" - "slices" - "strings" - "testing" - - rest "github.com/hasura/ndc-rest/ndc-rest-schema/schema" - "github.com/hasura/ndc-sdk-go/schema" - "gotest.tools/v3/assert" -) - -func TestCreateMultipartForm(t *testing.T) { - testCases := []struct { - Name string - RawBody string - RawArguments string - Expected map[string]string - ExpectedHeaders map[string]http.Header - }{ - { - Name: "multiple_fields", - RawBody: `{ - "contentType": "multipart/form-data", - "schema": { - "type": "object", - "properties": { - "expand": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "expand_expose": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "file": { - "type": "Binary" - }, - "file_link_data": { - "type": "object", - "nullable": true, - "properties": { - "create": { - "type": "Boolean" - }, - "expires_at": { - "type": "UnixTime", - "nullable": true - } - } - }, - "purpose": { - "type": "PostFilesBodyPurpose" - } - } - }, - "encoding": { - "expand_expose": { - "style": "deepObject", - "explode": true - }, - "file_link_data": { - "style": "deepObject", - "explode": true - }, - "file": { - "headers": { - "X-Rate-Limit-Limit": { - "argumentName": "headerXRateLimitLimit", - "schema": { - "type": "integer" - } - } - } - } - } - }`, - RawArguments: `{ - "body": { - "expand": ["foo", "bar"], - "expand_expose": ["foo"], - "file": "aGVsbG8gd29ybGQ=", - "file_link_data": { - "create": true, - "expires_at": 181320689 - }, - "purpose": "business_icon" - }, - "headerXRateLimitLimit": 10 - }`, - Expected: map[string]string{ - "expand": `["foo","bar"]`, - "expand_expose[]": `foo`, - "file": "hello world", - "file_link_data.create": "true", - "file_link_data.expires_at": "181320689", - "purpose": "business_icon", - }, - ExpectedHeaders: map[string]http.Header{ - "expand": { - "Content-Type": []string{"application/json"}, - }, - "file": { - "X-Rate-Limit-Limit": []string{"10"}, - }, - }, - }, - } - - rc := &RESTConnector{ - schema: &schema.SchemaResponse{ - ScalarTypes: schema.SchemaResponseScalarTypes{ - "PostFilesBodyPurpose": schema.ScalarType{ - AggregateFunctions: schema.ScalarTypeAggregateFunctions{}, - ComparisonOperators: map[string]schema.ComparisonOperatorDefinition{}, - Representation: schema.NewTypeRepresentationEnum([]string{ - "account_requirement", - "additional_verification", - "business_icon", - "business_logo", - "customer_signature", - "dispute_evidence", - "identity_document", - "pci_document", - "tax_document_user_upload", - "terminal_reader_splashscreen", - }).Encode(), - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - var reqBody rest.RequestBody - var arguments map[string]any - assert.NilError(t, json.Unmarshal([]byte(tc.RawBody), &reqBody)) - assert.NilError(t, json.Unmarshal([]byte(tc.RawArguments), &arguments)) - - buf, mediaType, err := rc.createMultipartForm(&reqBody, &rest.ArgumentInfo{}, arguments) - assert.NilError(t, err) - - // log.Println("form data:", string(buf.String())) - _, params, err := mime.ParseMediaType(mediaType) - assert.NilError(t, err) - - reader := multipart.NewReader(buf, params["boundary"]) - var count int - results := make(map[string]string) - for { - form, err := reader.NextPart() - if err != nil && strings.Contains(err.Error(), io.EOF.Error()) { - break - } - assert.NilError(t, err) - count++ - name := form.FormName() - expected, ok := tc.Expected[name] - if !ok { - t.Fatalf("field %s does not exist", name) - } else { - result, err := io.ReadAll(form) - assert.NilError(t, err) - assert.Equal(t, expected, string(result)) - results[name] = string(result) - expectedHeader := tc.ExpectedHeaders[name] - - for key, value := range expectedHeader { - assert.DeepEqual(t, value, form.Header[key]) - } - } - } - if len(tc.Expected) != count { - assert.DeepEqual(t, tc.Expected, results) - } - }) - } -} - -func TestCreateFormURLEncoded(t *testing.T) { - testCases := []struct { - Name string - RawBody string - RawArguments string - Expected string - }{ - { - Name: "multiple_fields", - RawBody: `{ - "contentType": "application/x-www-form-urlencoded", - "schema": { - "type": "object", - "properties": { - "expand": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "expand_expose": { - "type": "array", - "nullable": true, - "items": { - "type": "String", - "maxLength": 5000 - } - }, - "file": { - "type": "Binary" - }, - "file_link_data": { - "type": "object", - "nullable": true, - "properties": { - "create": { - "type": "Boolean" - }, - "expires_at": { - "type": "UnixTime", - "nullable": true - } - } - }, - "purpose": { - "type": "PostFilesBodyPurpose" - } - } - }, - "encoding": { - "expand_expose": { - "style": "deepObject", - "explode": true - }, - "file_link_data": { - "style": "deepObject", - "explode": true - }, - "file": { - "headers": { - "X-Rate-Limit-Limit": { - "argumentName": "headerXRateLimitLimit", - "schema": { - "type": "integer" - } - } - } - } - } - }`, - RawArguments: `{ - "body": { - "expand": ["foo", "bar"], - "expand_expose": ["foo"], - "file": "aGVsbG8gd29ybGQ=", - "file_link_data": { - "create": true, - "expires_at": 181320689 - }, - "purpose": "business_icon" - }, - "headerXRateLimitLimit": 10 - }`, - Expected: "expand_expose[]=foo&file=aGVsbG8gd29ybGQ=&file_link_data[create]=true&file_link_data[expires_at]=181320689&purpose=business_icon&expand=foo&expand=bar", - }, - } - - rc := &RESTConnector{ - schema: &schema.SchemaResponse{ - ScalarTypes: schema.SchemaResponseScalarTypes{ - "PostFilesBodyPurpose": schema.ScalarType{ - AggregateFunctions: schema.ScalarTypeAggregateFunctions{}, - ComparisonOperators: map[string]schema.ComparisonOperatorDefinition{}, - Representation: schema.NewTypeRepresentationEnum([]string{ - "account_requirement", - "additional_verification", - "business_icon", - "business_logo", - "customer_signature", - "dispute_evidence", - "identity_document", - "pci_document", - "tax_document_user_upload", - "terminal_reader_splashscreen", - }).Encode(), - }, - }, - }, - } - - parseQueryAndSort := func(input string) []string { - items := strings.Split(input, "&") - slices.Sort(items) - return items - } - - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - var reqBody rest.RequestBody - var arguments map[string]any - assert.NilError(t, json.Unmarshal([]byte(tc.RawBody), &reqBody)) - assert.NilError(t, json.Unmarshal([]byte(tc.RawArguments), &arguments)) - - buf, err := rc.createFormURLEncoded(&reqBody, &rest.ArgumentInfo{}, arguments["body"]) - assert.NilError(t, err) - result, err := io.ReadAll(buf) - assert.NilError(t, err) - assert.DeepEqual(t, parseQueryAndSort(tc.Expected), parseQueryAndSort(string(result))) - }) - } -} diff --git a/rest/types.go b/rest/types.go index 8aafc79..dabd2bf 100644 --- a/rest/types.go +++ b/rest/types.go @@ -10,14 +10,9 @@ import ( ) var ( - errBuildSchemaFailed = errors.New("failed to build NDC REST schema") errInvalidSchema = errors.New("failed to validate NDC REST schema") + errBuildSchemaFailed = errors.New("failed to build NDC REST schema") errHTTPMethodRequired = errors.New("the HTTP method is required") - errArgumentRequired = errors.New("argument is required") -) - -const ( - contentTypeJSON = "application/json" ) // ConfigItem extends the ConvertConfig with advanced options