diff --git a/openapi/bootstrap.go b/openapi/bootstrap.go new file mode 100644 index 0000000..919cd57 --- /dev/null +++ b/openapi/bootstrap.go @@ -0,0 +1,243 @@ +package openapi + +import ( + "github.com/speakeasy-api/openapi/jsonschema/oas3" + "github.com/speakeasy-api/openapi/pointer" + "github.com/speakeasy-api/openapi/sequencedmap" + "github.com/speakeasy-api/openapi/values" + "github.com/speakeasy-api/openapi/yml" +) + +// Bootstrap creates a new OpenAPI document with best practice examples. +// This serves as a template demonstrating proper structure for operations, +// components, security schemes, servers, tags, and references. +func Bootstrap() *OpenAPI { + return &OpenAPI{ + OpenAPI: Version, + Info: createBootstrapInfo(), + Servers: createBootstrapServers(), + Tags: createBootstrapTags(), + Security: []*SecurityRequirement{ + NewSecurityRequirement( + sequencedmap.NewElem("ApiKeyAuth", []string{}), + ), + }, + Paths: createBootstrapPaths(), + Components: createBootstrapComponents(), + } +} + +// createBootstrapInfo creates a complete info section with best practices +func createBootstrapInfo() Info { + return Info{ + Title: "My API", + Description: pointer.From("A new OpenAPI document template ready to populate"), + Version: "1.0.0", + Contact: &Contact{ + Name: pointer.From("API Support"), + Email: pointer.From("support@example.com"), + URL: pointer.From("https://example.com/support"), + }, + License: &License{ + Name: "MIT", + URL: pointer.From("https://opensource.org/licenses/MIT"), + }, + TermsOfService: pointer.From("https://example.com/terms"), + } +} + +// createBootstrapServers creates example servers for different environments +func createBootstrapServers() []*Server { + return []*Server{ + { + URL: "https://api.example.com/v1", + Description: pointer.From("Production server"), + }, + { + URL: "https://staging-api.example.com/v1", + Description: pointer.From("Staging server"), + }, + } +} + +// createBootstrapTags creates example tags for organizing operations +func createBootstrapTags() []*Tag { + return []*Tag{ + { + Name: "users", + Description: pointer.From("User management operations"), + ExternalDocs: &oas3.ExternalDocumentation{ + Description: pointer.From("User API documentation"), + URL: "https://docs.example.com/users", + }, + }, + } +} + +// createBootstrapPaths creates a single POST operation demonstrating best practices +func createBootstrapPaths() *Paths { + paths := NewPaths() + + // Create a single POST operation as an example + usersPath := NewPathItem() + usersPath.Set(HTTPMethodPost, &Operation{ + OperationID: pointer.From("createUser"), + Summary: pointer.From("Create a new user"), + Description: pointer.From("Creates a new user account with the provided information"), + Tags: []string{"users"}, + RequestBody: NewReferencedRequestBodyFromRequestBody(&RequestBody{ + Description: pointer.From("User creation request"), + Required: pointer.From(true), + Content: sequencedmap.New( + sequencedmap.NewElem("application/json", &MediaType{ + Schema: oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeObject), + Properties: sequencedmap.New( + sequencedmap.NewElem("name", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeString), + Description: pointer.From("Full name of the user"), + MinLength: pointer.From(int64(1)), + MaxLength: pointer.From(int64(100)), + })), + sequencedmap.NewElem("email", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeString), + Format: pointer.From("email"), + Description: pointer.From("Email address of the user"), + })), + ), + Required: []string{"name", "email"}, + }), + }), + ), + }), + Responses: createUserResponses(), + }) + + paths.Set("/users", &ReferencedPathItem{Object: usersPath}) + return paths +} + +// createUserResponses creates example responses with references +func createUserResponses() *Responses { + return NewResponses( + sequencedmap.NewElem("201", NewReferencedResponseFromRef("#/components/responses/UserResponse")), + sequencedmap.NewElem("400", NewReferencedResponseFromRef("#/components/responses/BadRequestResponse")), + sequencedmap.NewElem("401", NewReferencedResponseFromRef("#/components/responses/UnauthorizedResponse")), + ) +} + +// createBootstrapComponents creates reusable components demonstrating best practices +func createBootstrapComponents() *Components { + return &Components{ + Schemas: createBootstrapSchemas(), + Responses: createBootstrapResponses(), + SecuritySchemes: createBootstrapSecuritySchemes(), + } +} + +// createBootstrapSchemas creates example schemas with proper structure +func createBootstrapSchemas() *sequencedmap.Map[string, *oas3.JSONSchema[oas3.Referenceable]] { + return sequencedmap.New( + sequencedmap.NewElem("User", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeObject), + Title: pointer.From("User"), + Description: pointer.From("A user account"), + Properties: sequencedmap.New( + sequencedmap.NewElem("id", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeInteger), + Format: pointer.From("int64"), + Description: pointer.From("Unique identifier for the user"), + Examples: []values.Value{yml.CreateIntNode(123)}, + })), + sequencedmap.NewElem("name", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeString), + Description: pointer.From("Full name of the user"), + MinLength: pointer.From(int64(1)), + MaxLength: pointer.From(int64(100)), + Examples: []values.Value{yml.CreateStringNode("John Doe")}, + })), + sequencedmap.NewElem("email", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeString), + Format: pointer.From("email"), + Description: pointer.From("Email address of the user"), + Examples: []values.Value{yml.CreateStringNode("john.doe@example.com")}, + })), + sequencedmap.NewElem("status", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeString), + Description: pointer.From("Current status of the user account"), + Enum: []values.Value{yml.CreateStringNode("active"), yml.CreateStringNode("inactive"), yml.CreateStringNode("pending")}, + Examples: []values.Value{yml.CreateStringNode("active")}, + })), + ), + Required: []string{"name", "email"}, + })), + sequencedmap.NewElem("Error", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeObject), + Title: pointer.From("Error"), + Description: pointer.From("Error response"), + Properties: sequencedmap.New( + sequencedmap.NewElem("code", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeString), + Description: pointer.From("Error code"), + Examples: []values.Value{yml.CreateStringNode("VALIDATION_ERROR")}, + })), + sequencedmap.NewElem("message", oas3.NewJSONSchemaFromSchema[oas3.Referenceable](&oas3.Schema{ + Type: oas3.NewTypeFromString(oas3.SchemaTypeString), + Description: pointer.From("Human-readable error message"), + Examples: []values.Value{yml.CreateStringNode("The request is invalid")}, + })), + ), + Required: []string{"code", "message"}, + })), + ) +} + +// createBootstrapResponses creates reusable response components +func createBootstrapResponses() *sequencedmap.Map[string, *ReferencedResponse] { + return sequencedmap.New( + sequencedmap.NewElem("UserResponse", &ReferencedResponse{ + Object: &Response{ + Description: "User details", + Content: sequencedmap.New( + sequencedmap.NewElem("application/json", &MediaType{ + Schema: oas3.NewJSONSchemaFromReference("#/components/schemas/User"), + }), + ), + }, + }), + sequencedmap.NewElem("BadRequestResponse", &ReferencedResponse{ + Object: &Response{ + Description: "Bad request - validation error", + Content: sequencedmap.New( + sequencedmap.NewElem("application/json", &MediaType{ + Schema: oas3.NewJSONSchemaFromReference("#/components/schemas/Error"), + }), + ), + }, + }), + sequencedmap.NewElem("UnauthorizedResponse", &ReferencedResponse{ + Object: &Response{ + Description: "Unauthorized - authentication required", + Content: sequencedmap.New( + sequencedmap.NewElem("application/json", &MediaType{ + Schema: oas3.NewJSONSchemaFromReference("#/components/schemas/Error"), + }), + ), + }, + }), + ) +} + +// createBootstrapSecuritySchemes creates example security schemes +func createBootstrapSecuritySchemes() *sequencedmap.Map[string, *ReferencedSecurityScheme] { + return sequencedmap.New( + sequencedmap.NewElem("ApiKeyAuth", &ReferencedSecurityScheme{ + Object: &SecurityScheme{ + Type: "apiKey", + In: pointer.From[SecuritySchemeIn]("header"), + Name: pointer.From("X-API-Key"), + Description: pointer.From("API key for authentication"), + }, + }), + ) +} diff --git a/openapi/bootstrap_test.go b/openapi/bootstrap_test.go new file mode 100644 index 0000000..8c1309f --- /dev/null +++ b/openapi/bootstrap_test.go @@ -0,0 +1,41 @@ +package openapi_test + +import ( + "bytes" + "os" + "testing" + + "github.com/speakeasy-api/openapi/openapi" + "github.com/speakeasy-api/openapi/yml" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestBootstrap_Success(t *testing.T) { + t.Parallel() + ctx := t.Context() + + // Create bootstrap document + doc := openapi.Bootstrap() + + // Marshal to YAML + var buf bytes.Buffer + ctx = yml.ContextWithConfig(ctx, &yml.Config{ + ValueStringStyle: yaml.DoubleQuotedStyle, + Indentation: 2, + OutputFormat: yml.OutputFormatYAML, + }) + err := openapi.Marshal(ctx, doc, &buf) + require.NoError(t, err, "should marshal without error") + + // Read expected output + expectedBytes, err := os.ReadFile("testdata/bootstrap_expected.yaml") + require.NoError(t, err, "should read expected file") + + // Compare outputs + expected := string(expectedBytes) + actual := buf.String() + + assert.Equal(t, expected, actual, "marshaled output should match expected YAML") +} diff --git a/openapi/bundle_test.go b/openapi/bundle_test.go index 448755b..a14b461 100644 --- a/openapi/bundle_test.go +++ b/openapi/bundle_test.go @@ -43,10 +43,6 @@ func TestBundle_Success(t *testing.T) { require.NoError(t, err) actualYAML := buf.Bytes() - // Save the current output for comparison - err = os.WriteFile("testdata/inline/bundled_current.yaml", actualYAML, 0644) - require.NoError(t, err) - // Load the expected output expectedBytes, err := os.ReadFile("testdata/inline/bundled_expected.yaml") require.NoError(t, err) @@ -88,10 +84,6 @@ func TestBundle_CounterNaming_Success(t *testing.T) { require.NoError(t, err) actualYAML := buf.Bytes() - // Save the current output for comparison - err = os.WriteFile("testdata/inline/bundled_counter_current.yaml", actualYAML, 0644) - require.NoError(t, err) - // Load the expected output expectedBytes, err := os.ReadFile("testdata/inline/bundled_counter_expected.yaml") require.NoError(t, err) diff --git a/openapi/cmd/bootstrap.go b/openapi/cmd/bootstrap.go new file mode 100644 index 0000000..b078988 --- /dev/null +++ b/openapi/cmd/bootstrap.go @@ -0,0 +1,90 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/speakeasy-api/openapi/openapi" + "github.com/speakeasy-api/openapi/yml" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" +) + +var bootstrapCmd = &cobra.Command{ + Use: "bootstrap [output-file]", + Short: "Create a new OpenAPI document with best practice examples", + Long: `Bootstrap creates a new OpenAPI document template with comprehensive examples of best practices. + +This command generates a complete OpenAPI specification that demonstrates: +• Proper document structure and metadata (info, servers, tags) +• Example operations with request/response definitions +• Reusable components (schemas, responses, security schemes) +• Reference usage ($ref) for component reuse +• Security scheme definitions (API key authentication) +• Comprehensive schema examples with validation rules + +The generated document serves as both a template for new APIs and a learning +resource for OpenAPI best practices. + +Examples: + # Create bootstrap document and output to stdout + openapi openapi bootstrap + + # Create bootstrap document and save to file + openapi openapi bootstrap ./my-api.yaml + + # Create bootstrap document in current directory + openapi openapi bootstrap ./openapi.yaml`, + Args: cobra.MaximumNArgs(1), + Run: runBootstrap, +} + +func runBootstrap(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + + if err := createBootstrapDocument(ctx, args); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func createBootstrapDocument(ctx context.Context, args []string) error { + // Create the bootstrap document + doc := openapi.Bootstrap() + + // Determine output destination + var outputFile string + writeToStdout := true + + if len(args) > 0 { + outputFile = args[0] + writeToStdout = false + } + + // Create processor for output handling + processor, err := NewOpenAPIProcessor("", outputFile, false) + if err != nil { + return err + } + + // Override stdout setting based on our logic + processor.WriteToStdout = writeToStdout + + // Write the document + ctx = yml.ContextWithConfig(ctx, &yml.Config{ + ValueStringStyle: yaml.DoubleQuotedStyle, + Indentation: 2, + OutputFormat: yml.OutputFormatYAML, + }) + if err := processor.WriteDocument(ctx, doc); err != nil { + return fmt.Errorf("failed to write bootstrap document: %w", err) + } + + // Print success message (only if not writing to stdout) + if !writeToStdout { + processor.PrintSuccess("Bootstrap OpenAPI document created: " + outputFile) + } + + return nil +} diff --git a/openapi/cmd/root.go b/openapi/cmd/root.go index e88d414..3a2c191 100644 --- a/openapi/cmd/root.go +++ b/openapi/cmd/root.go @@ -9,4 +9,5 @@ func Apply(rootCmd *cobra.Command) { rootCmd.AddCommand(inlineCmd) rootCmd.AddCommand(bundleCmd) rootCmd.AddCommand(joinCmd) + rootCmd.AddCommand(bootstrapCmd) } diff --git a/openapi/inline_test.go b/openapi/inline_test.go index 69a94d6..e937a7a 100644 --- a/openapi/inline_test.go +++ b/openapi/inline_test.go @@ -43,10 +43,6 @@ func TestInline_Success(t *testing.T) { require.NoError(t, err) actualYAML := buf.Bytes() - // Save the current output for comparison - err = os.WriteFile("testdata/inline/inline_current.yaml", actualYAML, 0644) - require.NoError(t, err) - // Load the expected output expectedBytes, err := os.ReadFile("testdata/inline/inline_expected.yaml") require.NoError(t, err) diff --git a/openapi/join_test.go b/openapi/join_test.go index 9c56dfa..f44c1a9 100644 --- a/openapi/join_test.go +++ b/openapi/join_test.go @@ -68,10 +68,6 @@ func TestJoin_Counter_Success(t *testing.T) { require.NoError(t, err) actualYAML := buf.Bytes() - // Save the current output for comparison - err = os.WriteFile("testdata/join/joined_counter_current.yaml", actualYAML, 0644) - require.NoError(t, err) - // Load the expected output expectedBytes, err := os.ReadFile("testdata/join/joined_counter_expected.yaml") require.NoError(t, err) @@ -138,10 +134,6 @@ func TestJoin_FilePath_Success(t *testing.T) { require.NoError(t, err) actualYAML := buf.Bytes() - // Save the current output for comparison - err = os.WriteFile("testdata/join/joined_filepath_current.yaml", actualYAML, 0644) - require.NoError(t, err) - // Load the expected output expectedBytes, err := os.ReadFile("testdata/join/joined_filepath_expected.yaml") require.NoError(t, err) @@ -329,10 +321,6 @@ func TestJoin_ServersSecurityConflicts_Success(t *testing.T) { require.NoError(t, err) actualYAML := buf.Bytes() - // Save the current output for comparison - err = os.WriteFile("testdata/join/joined_conflicts_current.yaml", actualYAML, 0644) - require.NoError(t, err) - // Read expected output expectedBytes, err := os.ReadFile("testdata/join/joined_conflicts_expected.yaml") require.NoError(t, err) diff --git a/openapi/reference.go b/openapi/reference.go index 62b50f9..5fed65e 100644 --- a/openapi/reference.go +++ b/openapi/reference.go @@ -38,9 +38,9 @@ type ( ) // NewReferencedPathItemFromRef creates a new ReferencedPathItem from a reference. -func NewReferencedPathItemFromRef(ref *references.Reference) *ReferencedPathItem { +func NewReferencedPathItemFromRef(ref references.Reference) *ReferencedPathItem { return &ReferencedPathItem{ - Reference: ref, + Reference: &ref, } } @@ -52,9 +52,9 @@ func NewReferencedPathItemFromPathItem(pathItem *PathItem) *ReferencedPathItem { } // NewReferencedExampleFromRef creates a new ReferencedExample from a reference. -func NewReferencedExampleFromRef(ref *references.Reference) *ReferencedExample { +func NewReferencedExampleFromRef(ref references.Reference) *ReferencedExample { return &ReferencedExample{ - Reference: ref, + Reference: &ref, } } @@ -66,9 +66,9 @@ func NewReferencedExampleFromExample(example *Example) *ReferencedExample { } // NewReferencedParameterFromRef creates a new ReferencedParameter from a reference. -func NewReferencedParameterFromRef(ref *references.Reference) *ReferencedParameter { +func NewReferencedParameterFromRef(ref references.Reference) *ReferencedParameter { return &ReferencedParameter{ - Reference: ref, + Reference: &ref, } } @@ -80,9 +80,9 @@ func NewReferencedParameterFromParameter(parameter *Parameter) *ReferencedParame } // NewReferencedHeaderFromRef creates a new ReferencedHeader from a reference. -func NewReferencedHeaderFromRef(ref *references.Reference) *ReferencedHeader { +func NewReferencedHeaderFromRef(ref references.Reference) *ReferencedHeader { return &ReferencedHeader{ - Reference: ref, + Reference: &ref, } } @@ -94,9 +94,9 @@ func NewReferencedHeaderFromHeader(header *Header) *ReferencedHeader { } // NewReferencedRequestBodyFromRef creates a new ReferencedRequestBody from a reference. -func NewReferencedRequestBodyFromRef(ref *references.Reference) *ReferencedRequestBody { +func NewReferencedRequestBodyFromRef(ref references.Reference) *ReferencedRequestBody { return &ReferencedRequestBody{ - Reference: ref, + Reference: &ref, } } @@ -108,9 +108,9 @@ func NewReferencedRequestBodyFromRequestBody(requestBody *RequestBody) *Referenc } // NewReferencedResponseFromRef creates a new ReferencedResponse from a reference. -func NewReferencedResponseFromRef(ref *references.Reference) *ReferencedResponse { +func NewReferencedResponseFromRef(ref references.Reference) *ReferencedResponse { return &ReferencedResponse{ - Reference: ref, + Reference: &ref, } } @@ -122,9 +122,9 @@ func NewReferencedResponseFromResponse(response *Response) *ReferencedResponse { } // NewReferencedCallbackFromRef creates a new ReferencedCallback from a reference. -func NewReferencedCallbackFromRef(ref *references.Reference) *ReferencedCallback { +func NewReferencedCallbackFromRef(ref references.Reference) *ReferencedCallback { return &ReferencedCallback{ - Reference: ref, + Reference: &ref, } } @@ -136,9 +136,9 @@ func NewReferencedCallbackFromCallback(callback *Callback) *ReferencedCallback { } // NewReferencedLinkFromRef creates a new ReferencedLink from a reference. -func NewReferencedLinkFromRef(ref *references.Reference) *ReferencedLink { +func NewReferencedLinkFromRef(ref references.Reference) *ReferencedLink { return &ReferencedLink{ - Reference: ref, + Reference: &ref, } } @@ -150,9 +150,9 @@ func NewReferencedLinkFromLink(link *Link) *ReferencedLink { } // NewReferencedSecuritySchemeFromRef creates a new ReferencedSecurityScheme from a reference. -func NewReferencedSecuritySchemeFromRef(ref *references.Reference) *ReferencedSecurityScheme { +func NewReferencedSecuritySchemeFromRef(ref references.Reference) *ReferencedSecurityScheme { return &ReferencedSecurityScheme{ - Reference: ref, + Reference: &ref, } } diff --git a/openapi/security.go b/openapi/security.go index 629c5b6..7a06ae7 100644 --- a/openapi/security.go +++ b/openapi/security.go @@ -205,9 +205,9 @@ type SecurityRequirement struct { var _ interfaces.Model[core.SecurityRequirement] = (*SecurityRequirement)(nil) // NewSecurityRequirement creates a new SecurityRequirement object with the embedded map initialized. -func NewSecurityRequirement() *SecurityRequirement { +func NewSecurityRequirement(elems ...*sequencedmap.Element[string, []string]) *SecurityRequirement { return &SecurityRequirement{ - Map: *sequencedmap.New[string, []string](), + Map: *sequencedmap.New(elems...), } } diff --git a/openapi/testdata/bootstrap_expected.yaml b/openapi/testdata/bootstrap_expected.yaml new file mode 100644 index 0000000..4387b0b --- /dev/null +++ b/openapi/testdata/bootstrap_expected.yaml @@ -0,0 +1,142 @@ +openapi: "3.1.1" +info: + title: "My API" + version: "1.0.0" + description: "A new OpenAPI document template ready to populate" + termsOfService: "https://example.com/terms" + contact: + name: "API Support" + url: "https://example.com/support" + email: "support@example.com" + license: + name: "MIT" + url: "https://opensource.org/licenses/MIT" +tags: + - name: "users" + description: "User management operations" + externalDocs: + description: "User API documentation" + url: "https://docs.example.com/users" +servers: + - url: "https://api.example.com/v1" + description: "Production server" + - url: "https://staging-api.example.com/v1" + description: "Staging server" +security: + - ApiKeyAuth: [] +paths: + /users: + post: + operationId: "createUser" + summary: "Create a new user" + description: "Creates a new user account with the provided information" + tags: + - "users" + requestBody: + description: "User creation request" + content: + application/json: + schema: + type: "object" + properties: + name: + type: "string" + maxLength: 100 + minLength: 1 + description: "Full name of the user" + email: + type: "string" + format: "email" + description: "Email address of the user" + required: + - "name" + - "email" + required: true + responses: + "201": + $ref: "#/components/responses/UserResponse" + "400": + $ref: "#/components/responses/BadRequestResponse" + "401": + $ref: "#/components/responses/UnauthorizedResponse" +components: + schemas: + User: + type: "object" + properties: + id: + type: "integer" + examples: + - 123 + format: "int64" + description: "Unique identifier for the user" + name: + type: "string" + examples: + - John Doe + maxLength: 100 + minLength: 1 + description: "Full name of the user" + email: + type: "string" + examples: + - john.doe@example.com + format: "email" + description: "Email address of the user" + status: + type: "string" + examples: + - active + enum: + - active + - inactive + - pending + description: "Current status of the user account" + title: "User" + required: + - "name" + - "email" + description: "A user account" + Error: + type: "object" + properties: + code: + type: "string" + examples: + - VALIDATION_ERROR + description: "Error code" + message: + type: "string" + examples: + - The request is invalid + description: "Human-readable error message" + title: "Error" + required: + - "code" + - "message" + description: "Error response" + responses: + UserResponse: + description: "User details" + content: + application/json: + schema: + $ref: "#/components/schemas/User" + BadRequestResponse: + description: "Bad request - validation error" + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + UnauthorizedResponse: + description: "Unauthorized - authentication required" + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + securitySchemes: + ApiKeyAuth: + type: "apiKey" + description: "API key for authentication" + name: "X-API-Key" + in: "header" diff --git a/openapi/testdata/inline/bundled_counter_current.yaml b/openapi/testdata/inline/bundled_counter_current.yaml deleted file mode 100644 index 859b2a2..0000000 --- a/openapi/testdata/inline/bundled_counter_current.yaml +++ /dev/null @@ -1,661 +0,0 @@ -openapi: 3.1.0 -info: - title: Enhanced Test API with Complex References - version: 1.0.0 - description: A comprehensive test document with circular references, external refs, and duplicate schemas - contact: - name: Test Contact - url: https://example.com - license: - name: MIT - url: https://opensource.org/licenses/MIT -servers: - - url: https://api.example.com/v1 - description: Production server -tags: - - name: users - description: User operations - - name: posts - description: Post operations - - name: organizations - description: Organization operations - - name: external - description: External reference operations -paths: - /users: - get: - tags: - - users - summary: List users - parameters: - - $ref: "#/components/parameters/LimitParam" - - $ref: "#/components/parameters/OffsetParam" - responses: - "200": - $ref: "#/components/responses/UserListResponse" - "400": - $ref: "#/components/responses/ErrorResponse" - post: - tags: - - users - summary: Create user - requestBody: - $ref: "#/components/requestBodies/CreateUserRequest" - responses: - "201": - $ref: "#/components/responses/UserResponse" - "400": - $ref: "#/components/responses/ErrorResponse" - /users/{id}: - parameters: - - $ref: "#/components/parameters/UserIdParam" - get: - tags: - - users - summary: Get user by ID - responses: - "200": - $ref: "#/components/responses/UserResponse" - "404": - $ref: "#/components/responses/ErrorResponse" - put: - tags: - - users - summary: Update user - requestBody: - $ref: "#/components/requestBodies/UpdateUserRequest" - responses: - "200": - $ref: "#/components/responses/UserResponse" - "404": - $ref: "#/components/responses/ErrorResponse" - /external-users: - get: - tags: - - users - summary: Get external users (uses conflicting User schema) - responses: - "200": - description: List of external users - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/User_1" - /posts: - get: - tags: - - posts - summary: List posts - responses: - "200": - description: List of posts - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Post" - examples: - posts_example: - $ref: "#/components/examples/PostsExample" - post: - tags: - - posts - summary: Create post (uses circular Post schema) - requestBody: - description: Post creation data - required: true - content: - application/json: - schema: - type: object - required: [title, content, author_id] - properties: - title: - type: string - minLength: 1 - maxLength: 200 - content: - type: string - minLength: 1 - author_id: - type: string - format: uuid - tags: - type: array - items: - type: string - responses: - "201": - description: Created post - content: - application/json: - schema: - $ref: "#/components/schemas/Post" - # Additional operations using the same circular reference schemas - /users/{id}/posts: - parameters: - - $ref: "#/components/parameters/UserIdParam" - get: - tags: [posts] - summary: Get user posts (uses circular Post schema) - responses: - "200": - description: List of user posts - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Post" - "404": - $ref: "#/components/responses/ErrorResponse" - /posts/{id}: - parameters: - - name: id - in: path - required: true - description: Post ID - schema: - type: string - format: uuid - get: - tags: [posts] - summary: Get post by ID (uses circular Post schema) - responses: - "200": - description: Single post - content: - application/json: - schema: - $ref: "#/components/schemas/Post" - "404": - $ref: "#/components/responses/ErrorResponse" - # External reference operations - /external/users: - get: - tags: [external] - summary: Get external users (external ref to same User schema) - responses: - "200": - description: External users - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/User" - /external/organizations: - get: - tags: [external] - summary: Get organizations (external ref to different schema) - responses: - "200": - description: Organizations - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Organization" - /external/simple: - get: - tags: [external] - summary: Get simple external data - responses: - "200": - description: Simple external data - content: - application/json: - schema: - $ref: "#/components/schemas/external_simple_schema" - /mixed/user-with-external-profile: - get: - tags: [external] - summary: Mixed internal/external references - responses: - "200": - description: User with external profile - content: - application/json: - schema: - type: object - properties: - user: - $ref: "#/components/schemas/User" - external_profile: - $ref: "#/components/schemas/external_user_profile" - simple_data: - $ref: "#/components/schemas/external_simple_schema" - # More operations to test $defs duplication - /users/search: - get: - tags: [users] - summary: Search users (another operation using User schema) - parameters: - - name: q - in: query - description: Search query - schema: - type: string - responses: - "200": - description: Search results - content: - application/json: - schema: - type: object - properties: - results: - type: array - items: - $ref: "#/components/schemas/User" - total: - type: integer - /posts/search: - get: - tags: [posts] - summary: Search posts (another operation using Post schema) - parameters: - - name: q - in: query - description: Search query - schema: - type: string - responses: - "200": - description: Search results - content: - application/json: - schema: - type: object - properties: - results: - type: array - items: - $ref: "#/components/schemas/Post" - total: - type: integer -components: - parameters: - LimitParam: - name: limit - in: query - description: Maximum number of items to return - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - OffsetParam: - name: offset - in: query - description: Number of items to skip - schema: - type: integer - minimum: 0 - default: 0 - UserIdParam: - name: id - in: path - required: true - description: User ID - schema: - type: string - format: uuid - schemas: - User: - type: object - required: - - id - - name - - email - properties: - id: - type: string - format: uuid - description: Unique user identifier - name: - type: string - minLength: 1 - maxLength: 100 - description: User's full name - email: - type: string - format: email - description: User's email address - profile: - $ref: "#/components/schemas/UserProfile" - posts: - type: array - items: - $ref: "#/components/schemas/Post" - description: Posts created by this user - UserProfile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - $ref: "#/components/schemas/UserPreferences" - UserPreferences: - type: object - properties: - theme: - type: string - enum: [light, dark, auto] - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - Post: - type: object - required: - - id - - title - - content - - author_id - properties: - id: - type: string - format: uuid - title: - type: string - minLength: 1 - maxLength: 200 - content: - type: string - minLength: 1 - author_id: - type: string - format: uuid - author: - $ref: "#/components/schemas/User" - tags: - type: array - items: - type: string - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - # Circular reference example - Tree structure - TreeNode: - type: object - properties: - id: - type: string - name: - type: string - children: - type: array - items: - $ref: "#/components/schemas/TreeNode" - parent: - $ref: "#/components/schemas/TreeNode" - Error: - type: object - required: - - code - - message - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - User_1: - type: object - properties: - userId: - type: integer - description: Numeric user identifier (conflicts with string id) - username: - type: string - maxLength: 50 - minLength: 3 - description: User's username (conflicts with name field) - email: - type: string - format: email - description: User's email address - role: - type: string - enum: - - admin - - user - - guest - description: User's role in the system - default: user - createdAt: - type: string - format: date-time - description: When the user was created - manager: - $ref: '#/components/schemas/User_1' - description: User's manager (creates circular reference) - required: - - userId - - username - Organization: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - maxLength: 200 - minLength: 1 - description: - type: string - website: - type: string - format: uri - members: - type: array - items: - $ref: '#/components/schemas/User' - required: - - id - - name - external_simple_schema: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - maxLength: 100 - minLength: 1 - description: - type: string - maxLength: 500 - created_at: - type: string - format: date-time - required: - - id - - name - external_user_profile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - $ref: '#/components/schemas/external_user_preferences' - external_user_preferences: - type: object - properties: - theme: - type: string - enum: - - light - - dark - - auto - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - requestBodies: - CreateUserRequest: - description: Request body for creating a user - required: true - content: - application/json: - schema: - type: object - required: - - name - - email - properties: - name: - type: string - minLength: 1 - maxLength: 100 - email: - type: string - format: email - profile: - $ref: "#/components/schemas/UserProfile" - UpdateUserRequest: - description: Request body for updating a user - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - minLength: 1 - maxLength: 100 - email: - type: string - format: email - profile: - $ref: "#/components/schemas/UserProfile" - responses: - UserResponse: - description: Single user response - content: - application/json: - schema: - $ref: "#/components/schemas/User" - examples: - user_example: - $ref: "#/components/examples/UserExample" - UserListResponse: - description: List of users response - content: - application/json: - schema: - type: object - properties: - users: - type: array - items: - $ref: "#/components/schemas/User" - total: - type: integer - minimum: 0 - limit: - type: integer - minimum: 1 - offset: - type: integer - minimum: 0 - ErrorResponse: - description: Error response - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - UserExample: - summary: Example user - description: An example user object - value: - id: "123e4567-e89b-12d3-a456-426614174000" - name: "John Doe" - email: "john.doe@example.com" - profile: - bio: "Software developer" - avatar_url: "https://example.com/avatar.jpg" - social_links: - github: "https://github.com/johndoe" - twitter: "https://twitter.com/johndoe" - preferences: - theme: "dark" - notifications: - email: true - push: false - PostsExample: - summary: Example posts - description: An example list of posts - value: - - id: "456e7890-e89b-12d3-a456-426614174001" - title: "My First Post" - content: "This is my first post content" - author_id: "123e4567-e89b-12d3-a456-426614174000" - tags: ["introduction", "first-post"] - created_at: "2023-01-01T12:00:00Z" - updated_at: "2023-01-01T12:00:00Z" - headers: - X-Rate-Limit: - description: Rate limit information - schema: - type: integer - minimum: 0 - securitySchemes: - BearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - description: JWT Bearer token authentication -security: - - BearerAuth: [] diff --git a/openapi/testdata/inline/bundled_current.yaml b/openapi/testdata/inline/bundled_current.yaml deleted file mode 100644 index 4d0dff2..0000000 --- a/openapi/testdata/inline/bundled_current.yaml +++ /dev/null @@ -1,661 +0,0 @@ -openapi: 3.1.0 -info: - title: Enhanced Test API with Complex References - version: 1.0.0 - description: A comprehensive test document with circular references, external refs, and duplicate schemas - contact: - name: Test Contact - url: https://example.com - license: - name: MIT - url: https://opensource.org/licenses/MIT -servers: - - url: https://api.example.com/v1 - description: Production server -tags: - - name: users - description: User operations - - name: posts - description: Post operations - - name: organizations - description: Organization operations - - name: external - description: External reference operations -paths: - /users: - get: - tags: - - users - summary: List users - parameters: - - $ref: "#/components/parameters/LimitParam" - - $ref: "#/components/parameters/OffsetParam" - responses: - "200": - $ref: "#/components/responses/UserListResponse" - "400": - $ref: "#/components/responses/ErrorResponse" - post: - tags: - - users - summary: Create user - requestBody: - $ref: "#/components/requestBodies/CreateUserRequest" - responses: - "201": - $ref: "#/components/responses/UserResponse" - "400": - $ref: "#/components/responses/ErrorResponse" - /users/{id}: - parameters: - - $ref: "#/components/parameters/UserIdParam" - get: - tags: - - users - summary: Get user by ID - responses: - "200": - $ref: "#/components/responses/UserResponse" - "404": - $ref: "#/components/responses/ErrorResponse" - put: - tags: - - users - summary: Update user - requestBody: - $ref: "#/components/requestBodies/UpdateUserRequest" - responses: - "200": - $ref: "#/components/responses/UserResponse" - "404": - $ref: "#/components/responses/ErrorResponse" - /external-users: - get: - tags: - - users - summary: Get external users (uses conflicting User schema) - responses: - "200": - description: List of external users - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/external_conflicting_user_yaml~User" - /posts: - get: - tags: - - posts - summary: List posts - responses: - "200": - description: List of posts - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Post" - examples: - posts_example: - $ref: "#/components/examples/PostsExample" - post: - tags: - - posts - summary: Create post (uses circular Post schema) - requestBody: - description: Post creation data - required: true - content: - application/json: - schema: - type: object - required: [title, content, author_id] - properties: - title: - type: string - minLength: 1 - maxLength: 200 - content: - type: string - minLength: 1 - author_id: - type: string - format: uuid - tags: - type: array - items: - type: string - responses: - "201": - description: Created post - content: - application/json: - schema: - $ref: "#/components/schemas/Post" - # Additional operations using the same circular reference schemas - /users/{id}/posts: - parameters: - - $ref: "#/components/parameters/UserIdParam" - get: - tags: [posts] - summary: Get user posts (uses circular Post schema) - responses: - "200": - description: List of user posts - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Post" - "404": - $ref: "#/components/responses/ErrorResponse" - /posts/{id}: - parameters: - - name: id - in: path - required: true - description: Post ID - schema: - type: string - format: uuid - get: - tags: [posts] - summary: Get post by ID (uses circular Post schema) - responses: - "200": - description: Single post - content: - application/json: - schema: - $ref: "#/components/schemas/Post" - "404": - $ref: "#/components/responses/ErrorResponse" - # External reference operations - /external/users: - get: - tags: [external] - summary: Get external users (external ref to same User schema) - responses: - "200": - description: External users - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/User" - /external/organizations: - get: - tags: [external] - summary: Get organizations (external ref to different schema) - responses: - "200": - description: Organizations - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Organization" - /external/simple: - get: - tags: [external] - summary: Get simple external data - responses: - "200": - description: Simple external data - content: - application/json: - schema: - $ref: "#/components/schemas/external_simple_schema" - /mixed/user-with-external-profile: - get: - tags: [external] - summary: Mixed internal/external references - responses: - "200": - description: User with external profile - content: - application/json: - schema: - type: object - properties: - user: - $ref: "#/components/schemas/User" - external_profile: - $ref: "#/components/schemas/external_user_profile" - simple_data: - $ref: "#/components/schemas/external_simple_schema" - # More operations to test $defs duplication - /users/search: - get: - tags: [users] - summary: Search users (another operation using User schema) - parameters: - - name: q - in: query - description: Search query - schema: - type: string - responses: - "200": - description: Search results - content: - application/json: - schema: - type: object - properties: - results: - type: array - items: - $ref: "#/components/schemas/User" - total: - type: integer - /posts/search: - get: - tags: [posts] - summary: Search posts (another operation using Post schema) - parameters: - - name: q - in: query - description: Search query - schema: - type: string - responses: - "200": - description: Search results - content: - application/json: - schema: - type: object - properties: - results: - type: array - items: - $ref: "#/components/schemas/Post" - total: - type: integer -components: - parameters: - LimitParam: - name: limit - in: query - description: Maximum number of items to return - schema: - type: integer - minimum: 1 - maximum: 100 - default: 20 - OffsetParam: - name: offset - in: query - description: Number of items to skip - schema: - type: integer - minimum: 0 - default: 0 - UserIdParam: - name: id - in: path - required: true - description: User ID - schema: - type: string - format: uuid - schemas: - User: - type: object - required: - - id - - name - - email - properties: - id: - type: string - format: uuid - description: Unique user identifier - name: - type: string - minLength: 1 - maxLength: 100 - description: User's full name - email: - type: string - format: email - description: User's email address - profile: - $ref: "#/components/schemas/UserProfile" - posts: - type: array - items: - $ref: "#/components/schemas/Post" - description: Posts created by this user - UserProfile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - $ref: "#/components/schemas/UserPreferences" - UserPreferences: - type: object - properties: - theme: - type: string - enum: [light, dark, auto] - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - Post: - type: object - required: - - id - - title - - content - - author_id - properties: - id: - type: string - format: uuid - title: - type: string - minLength: 1 - maxLength: 200 - content: - type: string - minLength: 1 - author_id: - type: string - format: uuid - author: - $ref: "#/components/schemas/User" - tags: - type: array - items: - type: string - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - # Circular reference example - Tree structure - TreeNode: - type: object - properties: - id: - type: string - name: - type: string - children: - type: array - items: - $ref: "#/components/schemas/TreeNode" - parent: - $ref: "#/components/schemas/TreeNode" - Error: - type: object - required: - - code - - message - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - external_conflicting_user_yaml~User: - type: object - properties: - userId: - type: integer - description: Numeric user identifier (conflicts with string id) - username: - type: string - maxLength: 50 - minLength: 3 - description: User's username (conflicts with name field) - email: - type: string - format: email - description: User's email address - role: - type: string - enum: - - admin - - user - - guest - description: User's role in the system - default: user - createdAt: - type: string - format: date-time - description: When the user was created - manager: - $ref: '#/components/schemas/external_conflicting_user_yaml~User' - description: User's manager (creates circular reference) - required: - - userId - - username - Organization: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - maxLength: 200 - minLength: 1 - description: - type: string - website: - type: string - format: uri - members: - type: array - items: - $ref: '#/components/schemas/User' - required: - - id - - name - external_simple_schema: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - maxLength: 100 - minLength: 1 - description: - type: string - maxLength: 500 - created_at: - type: string - format: date-time - required: - - id - - name - external_user_profile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - $ref: '#/components/schemas/external_user_preferences' - external_user_preferences: - type: object - properties: - theme: - type: string - enum: - - light - - dark - - auto - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - requestBodies: - CreateUserRequest: - description: Request body for creating a user - required: true - content: - application/json: - schema: - type: object - required: - - name - - email - properties: - name: - type: string - minLength: 1 - maxLength: 100 - email: - type: string - format: email - profile: - $ref: "#/components/schemas/UserProfile" - UpdateUserRequest: - description: Request body for updating a user - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - minLength: 1 - maxLength: 100 - email: - type: string - format: email - profile: - $ref: "#/components/schemas/UserProfile" - responses: - UserResponse: - description: Single user response - content: - application/json: - schema: - $ref: "#/components/schemas/User" - examples: - user_example: - $ref: "#/components/examples/UserExample" - UserListResponse: - description: List of users response - content: - application/json: - schema: - type: object - properties: - users: - type: array - items: - $ref: "#/components/schemas/User" - total: - type: integer - minimum: 0 - limit: - type: integer - minimum: 1 - offset: - type: integer - minimum: 0 - ErrorResponse: - description: Error response - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - examples: - UserExample: - summary: Example user - description: An example user object - value: - id: "123e4567-e89b-12d3-a456-426614174000" - name: "John Doe" - email: "john.doe@example.com" - profile: - bio: "Software developer" - avatar_url: "https://example.com/avatar.jpg" - social_links: - github: "https://github.com/johndoe" - twitter: "https://twitter.com/johndoe" - preferences: - theme: "dark" - notifications: - email: true - push: false - PostsExample: - summary: Example posts - description: An example list of posts - value: - - id: "456e7890-e89b-12d3-a456-426614174001" - title: "My First Post" - content: "This is my first post content" - author_id: "123e4567-e89b-12d3-a456-426614174000" - tags: ["introduction", "first-post"] - created_at: "2023-01-01T12:00:00Z" - updated_at: "2023-01-01T12:00:00Z" - headers: - X-Rate-Limit: - description: Rate limit information - schema: - type: integer - minimum: 0 - securitySchemes: - BearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - description: JWT Bearer token authentication -security: - - BearerAuth: [] diff --git a/openapi/testdata/inline/inline_current.yaml b/openapi/testdata/inline/inline_current.yaml deleted file mode 100644 index 83c1c2c..0000000 --- a/openapi/testdata/inline/inline_current.yaml +++ /dev/null @@ -1,914 +0,0 @@ -openapi: 3.1.0 -info: - title: Enhanced Test API with Complex References - version: 1.0.0 - description: A comprehensive test document with circular references, external refs, and duplicate schemas - contact: - name: Test Contact - url: https://example.com - license: - name: MIT - url: https://opensource.org/licenses/MIT -servers: - - url: https://api.example.com/v1 - description: Production server -tags: - - name: users - description: User operations - - name: posts - description: Post operations - - name: organizations - description: Organization operations - - name: external - description: External reference operations -paths: - /users: - get: - tags: - - users - summary: List users - parameters: - - name: limit - in: query - description: Maximum number of items to return - schema: - type: integer - maximum: 100 - minimum: 1 - default: 20 - - name: offset - in: query - description: Number of items to skip - schema: - type: integer - minimum: 0 - default: 0 - responses: - "200": - description: List of users response - content: - application/json: - schema: - type: object - properties: - users: - type: array - items: - $ref: '#/components/schemas/User' - total: - type: integer - minimum: 0 - limit: - type: integer - minimum: 1 - offset: - type: integer - minimum: 0 - "400": - description: Error response - content: - application/json: - schema: - type: object - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - required: - - code - - message - post: - tags: - - users - summary: Create user - requestBody: - description: Request body for creating a user - content: - application/json: - schema: - type: object - properties: - name: - type: string - maxLength: 100 - minLength: 1 - email: - type: string - format: email - profile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - type: object - properties: - theme: - type: string - enum: - - light - - dark - - auto - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - required: - - name - - email - required: true - responses: - "201": - description: Single user response - content: - application/json: - schema: - $ref: '#/components/schemas/User' - examples: - user_example: - summary: Example user - description: An example user object - value: - id: "123e4567-e89b-12d3-a456-426614174000" - name: "John Doe" - email: "john.doe@example.com" - profile: - bio: "Software developer" - avatar_url: "https://example.com/avatar.jpg" - social_links: - github: "https://github.com/johndoe" - twitter: "https://twitter.com/johndoe" - preferences: - theme: "dark" - notifications: - email: true - push: false - "400": - description: Error response - content: - application/json: - schema: - type: object - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - required: - - code - - message - /users/{id}: - parameters: - - name: id - in: path - description: User ID - required: true - schema: - type: string - format: uuid - get: - tags: - - users - summary: Get user by ID - responses: - "200": - description: Single user response - content: - application/json: - schema: - $ref: '#/components/schemas/User' - examples: - user_example: - summary: Example user - description: An example user object - value: - id: "123e4567-e89b-12d3-a456-426614174000" - name: "John Doe" - email: "john.doe@example.com" - profile: - bio: "Software developer" - avatar_url: "https://example.com/avatar.jpg" - social_links: - github: "https://github.com/johndoe" - twitter: "https://twitter.com/johndoe" - preferences: - theme: "dark" - notifications: - email: true - push: false - "404": - description: Error response - content: - application/json: - schema: - type: object - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - required: - - code - - message - put: - tags: - - users - summary: Update user - requestBody: - description: Request body for updating a user - content: - application/json: - schema: - type: object - properties: - name: - type: string - maxLength: 100 - minLength: 1 - email: - type: string - format: email - profile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - type: object - properties: - theme: - type: string - enum: - - light - - dark - - auto - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - required: true - responses: - "200": - description: Single user response - content: - application/json: - schema: - $ref: '#/components/schemas/User' - examples: - user_example: - summary: Example user - description: An example user object - value: - id: "123e4567-e89b-12d3-a456-426614174000" - name: "John Doe" - email: "john.doe@example.com" - profile: - bio: "Software developer" - avatar_url: "https://example.com/avatar.jpg" - social_links: - github: "https://github.com/johndoe" - twitter: "https://twitter.com/johndoe" - preferences: - theme: "dark" - notifications: - email: true - push: false - "404": - description: Error response - content: - application/json: - schema: - type: object - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - required: - - code - - message - /external-users: - get: - tags: - - users - summary: Get external users (uses conflicting User schema) - responses: - "200": - description: List of external users - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/User_1" - /posts: - get: - tags: - - posts - summary: List posts - responses: - "200": - description: List of posts - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Post" - examples: - posts_example: - summary: Example posts - description: An example list of posts - value: - - id: "456e7890-e89b-12d3-a456-426614174001" - title: "My First Post" - content: "This is my first post content" - author_id: "123e4567-e89b-12d3-a456-426614174000" - tags: ["introduction", "first-post"] - created_at: "2023-01-01T12:00:00Z" - updated_at: "2023-01-01T12:00:00Z" - post: - tags: - - posts - summary: Create post (uses circular Post schema) - requestBody: - description: Post creation data - required: true - content: - application/json: - schema: - type: object - required: [title, content, author_id] - properties: - title: - type: string - minLength: 1 - maxLength: 200 - content: - type: string - minLength: 1 - author_id: - type: string - format: uuid - tags: - type: array - items: - type: string - responses: - "201": - description: Created post - content: - application/json: - schema: - $ref: "#/components/schemas/Post" - # Additional operations using the same circular reference schemas - /users/{id}/posts: - parameters: - - name: id - in: path - description: User ID - required: true - schema: - type: string - format: uuid - get: - tags: [posts] - summary: Get user posts (uses circular Post schema) - responses: - "200": - description: List of user posts - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Post" - "404": - description: Error response - content: - application/json: - schema: - type: object - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - required: - - code - - message - /posts/{id}: - parameters: - - name: id - in: path - required: true - description: Post ID - schema: - type: string - format: uuid - get: - tags: [posts] - summary: Get post by ID (uses circular Post schema) - responses: - "200": - description: Single post - content: - application/json: - schema: - $ref: "#/components/schemas/Post" - "404": - description: Error response - content: - application/json: - schema: - type: object - properties: - code: - type: string - description: Error code - message: - type: string - description: Error message - details: - type: object - additionalProperties: true - description: Additional error details - required: - - code - - message - # External reference operations - /external/users: - get: - tags: [external] - summary: Get external users (external ref to same User schema) - responses: - "200": - description: External users - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/User" - /external/organizations: - get: - tags: [external] - summary: Get organizations (external ref to different schema) - responses: - "200": - description: Organizations - content: - application/json: - schema: - type: array - items: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - maxLength: 200 - minLength: 1 - description: - type: string - website: - type: string - format: uri - members: - type: array - items: - $ref: '#/components/schemas/User' - required: - - id - - name - /external/simple: - get: - tags: [external] - summary: Get simple external data - responses: - "200": - description: Simple external data - content: - application/json: - schema: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - maxLength: 100 - minLength: 1 - description: - type: string - maxLength: 500 - created_at: - type: string - format: date-time - required: - - id - - name - /mixed/user-with-external-profile: - get: - tags: [external] - summary: Mixed internal/external references - responses: - "200": - description: User with external profile - content: - application/json: - schema: - type: object - properties: - user: - $ref: "#/components/schemas/User" - external_profile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - type: object - properties: - theme: - type: string - enum: - - light - - dark - - auto - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - simple_data: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - maxLength: 100 - minLength: 1 - description: - type: string - maxLength: 500 - created_at: - type: string - format: date-time - required: - - id - - name - # More operations to test $defs duplication - /users/search: - get: - tags: [users] - summary: Search users (another operation using User schema) - parameters: - - name: q - in: query - description: Search query - schema: - type: string - responses: - "200": - description: Search results - content: - application/json: - schema: - type: object - properties: - results: - type: array - items: - $ref: "#/components/schemas/User" - total: - type: integer - /posts/search: - get: - tags: [posts] - summary: Search posts (another operation using Post schema) - parameters: - - name: q - in: query - description: Search query - schema: - type: string - responses: - "200": - description: Search results - content: - application/json: - schema: - type: object - properties: - results: - type: array - items: - $ref: "#/components/schemas/Post" - total: - type: integer -components: - schemas: - User: - type: object - required: - - id - - name - - email - properties: - id: - type: string - format: uuid - description: Unique user identifier - name: - type: string - minLength: 1 - maxLength: 100 - description: User's full name - email: - type: string - format: email - description: User's email address - profile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - type: object - properties: - theme: - type: string - enum: - - light - - dark - - auto - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - posts: - type: array - items: - type: object - properties: - id: - type: string - format: uuid - title: - type: string - maxLength: 200 - minLength: 1 - content: - type: string - minLength: 1 - author_id: - type: string - format: uuid - author: - $ref: '#/components/schemas/User' - tags: - type: array - items: - type: string - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - required: - - id - - title - - content - - author_id - description: Posts created by this user - Post: - type: object - required: - - id - - title - - content - - author_id - properties: - id: - type: string - format: uuid - title: - type: string - minLength: 1 - maxLength: 200 - content: - type: string - minLength: 1 - author_id: - type: string - format: uuid - author: - type: object - properties: - id: - type: string - format: uuid - description: Unique user identifier - name: - type: string - maxLength: 100 - minLength: 1 - description: User's full name - email: - type: string - format: email - description: User's email address - profile: - type: object - properties: - bio: - type: string - maxLength: 500 - description: User biography - avatar_url: - type: string - format: uri - description: URL to user's avatar image - social_links: - type: object - additionalProperties: - type: string - format: uri - preferences: - type: object - properties: - theme: - type: string - enum: - - light - - dark - - auto - default: auto - notifications: - type: object - properties: - email: - type: boolean - default: true - push: - type: boolean - default: false - posts: - type: array - items: - $ref: '#/components/schemas/Post' - description: Posts created by this user - required: - - id - - name - - email - tags: - type: array - items: - type: string - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - User_1: - type: object - properties: - userId: - type: integer - description: Numeric user identifier (conflicts with string id) - username: - type: string - maxLength: 50 - minLength: 3 - description: User's username (conflicts with name field) - email: - type: string - format: email - description: User's email address - role: - type: string - enum: - - admin - - user - - guest - description: User's role in the system - default: user - createdAt: - type: string - format: date-time - description: When the user was created - manager: - $ref: '#/components/schemas/User_1' - description: User's manager (creates circular reference) - required: - - userId - - username -security: - - BearerAuth: [] diff --git a/openapi/testdata/join/joined_conflicts_current.yaml b/openapi/testdata/join/joined_conflicts_current.yaml deleted file mode 100644 index 6460deb..0000000 --- a/openapi/testdata/join/joined_conflicts_current.yaml +++ /dev/null @@ -1,150 +0,0 @@ -openapi: 3.1.0 -info: - title: Main API - version: 1.0.0 - description: Main API for joining tests -tags: - - name: users - description: User operations -paths: - /users: - get: - summary: Get users - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/User' - servers: - - url: https://main-api.example.com - description: Main server - security: - - ApiKeyAuth: [] - servers: - - url: https://main-api.example.com - description: Main server - /health: - get: - summary: Health check - responses: - '200': - description: OK - servers: - - url: https://main-api.example.com - description: Main server - security: - - ApiKeyAuth: [] - servers: - - url: https://main-api.example.com - description: Main server - /orders: - get: - summary: Get orders with different server requirements - servers: - - url: https://different-api.example.com - description: Different server that conflicts - security: - - ApiKeyAuth: [] - responses: - "200": - description: Success - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Order' - servers: - - url: https://different-api.example.com - description: Different server that conflicts - /admin: - get: - summary: Admin endpoint with different security requirements - security: - - BearerAuth: [] - - OAuth2: - - read - - write - responses: - "200": - description: Success - content: - application/json: - schema: - type: object - properties: - message: - type: string -webhooks: - userCreated: - post: - summary: User created webhook - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/User' - responses: - '200': - description: OK -components: - schemas: - User: - type: object - properties: - id: - type: integer - name: - type: string - email: - type: string - format: email - Error: - type: object - properties: - code: - type: integer - message: - type: string - Order: - type: object - properties: - id: - type: integer - status: - type: string - responses: - NotFound: - description: The specified resource was not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - parameters: - limitParam: - name: limit - in: query - description: max records to return - schema: - type: integer - format: int32 - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key - BearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - OAuth2: - type: oauth2 - flows: - authorizationCode: - authorizationUrl: https://example.com/oauth/authorize - tokenUrl: https://example.com/oauth/token - scopes: - read: Read access - write: Write access diff --git a/openapi/testdata/join/joined_counter_current.yaml b/openapi/testdata/join/joined_counter_current.yaml deleted file mode 100644 index bb7bb65..0000000 --- a/openapi/testdata/join/joined_counter_current.yaml +++ /dev/null @@ -1,214 +0,0 @@ -openapi: 3.1.0 -info: - title: Main API - version: 1.0.0 - description: Main API for joining tests -servers: - - url: https://main-api.example.com - description: Main server -security: - - ApiKeyAuth: [] -tags: - - name: users - description: User operations - - name: products - description: Product operations -paths: - /users: - get: - summary: Get users - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/User' - /health: - get: - summary: Health check - responses: - '200': - description: OK - /products: - get: - summary: Get products - responses: - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/Product' - /users#second: - post: - summary: Create user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/User_1' - responses: - "201": - description: Created - /orders: - get: - summary: Get orders - responses: - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/Order' -webhooks: - userCreated: - post: - summary: User created webhook - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/User' - responses: - '200': - description: OK - productUpdated: - post: - summary: Product updated webhook - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Product' - responses: - "200": - description: OK -components: - schemas: - User: - type: object - properties: - id: - type: integer - name: - type: string - email: - type: string - format: email - Error: - type: object - properties: - code: - type: integer - message: - type: string - Product: - type: object - properties: - id: - type: integer - name: - type: string - price: - type: number - format: float - User_1: - type: object - properties: - id: - type: string - username: - type: string - active: - type: boolean - Category: - type: object - properties: - id: - type: integer - name: - type: string - Order: - type: object - properties: - id: - type: integer - userId: - type: integer - productId: - type: integer - quantity: - type: integer - status: - type: string - enum: - - pending - - confirmed - - shipped - - delivered - responses: - NotFound: - description: The specified resource was not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - NotFound_1: - description: Resource not found - content: - application/json: - schema: - type: object - properties: - error: - type: string - BadRequest: - description: Bad request - content: - application/json: - schema: - type: object - properties: - message: - type: string - Unauthorized: - description: Unauthorized access - content: - application/json: - schema: - type: object - properties: - error: - type: string - parameters: - limitParam: - name: limit - in: query - description: max records to return - schema: - type: integer - format: int32 - offsetParam: - name: offset - in: query - description: number of items to skip - schema: - type: integer - format: int32 - limitParam_1: - name: limit - in: query - description: maximum number of items to return - schema: - type: integer - format: int32 - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key - BearerAuth: - type: http - scheme: bearer - bearerFormat: JWT diff --git a/openapi/testdata/join/joined_filepath_current.yaml b/openapi/testdata/join/joined_filepath_current.yaml deleted file mode 100644 index dbd5df3..0000000 --- a/openapi/testdata/join/joined_filepath_current.yaml +++ /dev/null @@ -1,214 +0,0 @@ -openapi: 3.1.0 -info: - title: Main API - version: 1.0.0 - description: Main API for joining tests -servers: - - url: https://main-api.example.com - description: Main server -security: - - ApiKeyAuth: [] -tags: - - name: users - description: User operations - - name: products - description: Product operations -paths: - /users: - get: - summary: Get users - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/User' - /health: - get: - summary: Health check - responses: - '200': - description: OK - /products: - get: - summary: Get products - responses: - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/Product' - /users#second: - post: - summary: Create user - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/second_yaml~User' - responses: - "201": - description: Created - /orders: - get: - summary: Get orders - responses: - "200": - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/Order' -webhooks: - userCreated: - post: - summary: User created webhook - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/User' - responses: - '200': - description: OK - productUpdated: - post: - summary: Product updated webhook - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Product' - responses: - "200": - description: OK -components: - schemas: - User: - type: object - properties: - id: - type: integer - name: - type: string - email: - type: string - format: email - Error: - type: object - properties: - code: - type: integer - message: - type: string - Product: - type: object - properties: - id: - type: integer - name: - type: string - price: - type: number - format: float - second_yaml~User: - type: object - properties: - id: - type: string - username: - type: string - active: - type: boolean - Category: - type: object - properties: - id: - type: integer - name: - type: string - Order: - type: object - properties: - id: - type: integer - userId: - type: integer - productId: - type: integer - quantity: - type: integer - status: - type: string - enum: - - pending - - confirmed - - shipped - - delivered - responses: - NotFound: - description: The specified resource was not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - second_yaml~NotFound: - description: Resource not found - content: - application/json: - schema: - type: object - properties: - error: - type: string - BadRequest: - description: Bad request - content: - application/json: - schema: - type: object - properties: - message: - type: string - Unauthorized: - description: Unauthorized access - content: - application/json: - schema: - type: object - properties: - error: - type: string - parameters: - limitParam: - name: limit - in: query - description: max records to return - schema: - type: integer - format: int32 - offsetParam: - name: offset - in: query - description: number of items to skip - schema: - type: integer - format: int32 - second_yaml~limitParam: - name: limit - in: query - description: maximum number of items to return - schema: - type: integer - format: int32 - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key - BearerAuth: - type: http - scheme: bearer - bearerFormat: JWT diff --git a/yml/yml.go b/yml/yml.go index 61a1a2a..b4c0a3f 100644 --- a/yml/yml.go +++ b/yml/yml.go @@ -2,6 +2,7 @@ package yml import ( "context" + "strconv" "gopkg.in/yaml.v3" ) @@ -87,6 +88,30 @@ func CreateStringNode(value string) *yaml.Node { } } +func CreateIntNode(value int64) *yaml.Node { + return &yaml.Node{ + Value: strconv.FormatInt(value, 10), + Kind: yaml.ScalarNode, + Tag: "!!int", + } +} + +func CreateFloatNode(value float64) *yaml.Node { + return &yaml.Node{ + Value: strconv.FormatFloat(value, 'f', -1, 64), + Kind: yaml.ScalarNode, + Tag: "!!float", + } +} + +func CreateBoolNode(value bool) *yaml.Node { + return &yaml.Node{ + Value: strconv.FormatBool(value), + Kind: yaml.ScalarNode, + Tag: "!!bool", + } +} + func CreateMapNode(ctx context.Context, content []*yaml.Node) *yaml.Node { return &yaml.Node{ Content: content,