-
Notifications
You must be signed in to change notification settings - Fork 8
[WIP] feat: Add support for 3.2.0 openapi spec #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6c51416
ee2f0a2
87dc8d0
5f0f350
77282cc
79a5a47
b79f07f
fd2a04c
f5724d6
9581e4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,18 @@ | ||
[tools] | ||
go = "1.24.3" | ||
golangci-lint = "2.1.1" | ||
gotestsum = "latest" | ||
|
||
[tasks.setup-vscode-symlinks] | ||
description = "Create VSCode symlinks for tools not automatically handled by mise-vscode" | ||
run = [ | ||
"mkdir -p .vscode/mise-tools", | ||
"ln -sf $(mise exec golangci-lint@2.1.1 -- which golangci-lint) .vscode/mise-tools/golangci-lint", | ||
"ln -sf $(mise exec golangci-lint@2.5.0 -- which golangci-lint) .vscode/mise-tools/golangci-lint", | ||
] | ||
|
||
[hooks] | ||
postinstall = [ | ||
"git submodule update --init --recursive", | ||
"mise exec [email protected] -- go install github.com/golangci/golangci-lint/v2/cmd/[email protected]", | ||
"mise run setup-vscode-symlinks", | ||
"go install go.uber.org/nilaway/cmd/nilaway@8ad05f0", | ||
] |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package version | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
type Version struct { | ||
Major int | ||
Minor int | ||
Patch int | ||
} | ||
|
||
func New(major, minor, patch int) *Version { | ||
return &Version{ | ||
Major: major, | ||
Minor: minor, | ||
Patch: patch, | ||
} | ||
} | ||
|
||
func (v Version) String() string { | ||
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) | ||
} | ||
|
||
func (v Version) Equal(other Version) bool { | ||
return v.Major == other.Major && v.Minor == other.Minor && v.Patch == other.Patch | ||
} | ||
|
||
func (v Version) GreaterThan(other Version) bool { | ||
if v.Major > other.Major { | ||
return true | ||
} else if v.Major < other.Major { | ||
return false | ||
} | ||
|
||
if v.Minor > other.Minor { | ||
return true | ||
} else if v.Minor < other.Minor { | ||
return false | ||
} | ||
|
||
return v.Patch > other.Patch | ||
} | ||
|
||
func (v Version) LessThan(other Version) bool { | ||
return !v.Equal(other) && !v.GreaterThan(other) | ||
} | ||
|
||
func ParseVersion(version string) (*Version, error) { | ||
parts := strings.Split(version, ".") | ||
if len(parts) != 3 { | ||
return nil, fmt.Errorf("invalid version %s", version) | ||
} | ||
|
||
major, err := strconv.Atoi(parts[0]) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid major version %s: %w", parts[0], err) | ||
} | ||
if major < 0 { | ||
return nil, fmt.Errorf("invalid major version %s: cannot be negative", parts[0]) | ||
} | ||
|
||
minor, err := strconv.Atoi(parts[1]) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid minor version %s: %w", parts[1], err) | ||
} | ||
if minor < 0 { | ||
return nil, fmt.Errorf("invalid minor version %s: cannot be negative", parts[1]) | ||
} | ||
|
||
patch, err := strconv.Atoi(parts[2]) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid patch version %s: %w", parts[2], err) | ||
} | ||
if patch < 0 { | ||
return nil, fmt.Errorf("invalid patch version %s: cannot be negative", parts[2]) | ||
} | ||
|
||
return New(major, minor, patch), nil | ||
} | ||
|
||
func IsVersionGreaterOrEqual(a, b string) (bool, error) { | ||
versionA, err := ParseVersion(a) | ||
if err != nil { | ||
return false, fmt.Errorf("invalid version %s: %w", a, err) | ||
} | ||
|
||
versionB, err := ParseVersion(b) | ||
if err != nil { | ||
return false, fmt.Errorf("invalid version %s: %w", b, err) | ||
} | ||
return versionA.Equal(*versionB) || versionA.GreaterThan(*versionB), nil | ||
} | ||
|
||
func IsVersionLessThan(a, b string) (bool, error) { | ||
greaterOrEqual, err := IsVersionGreaterOrEqual(a, b) | ||
if err != nil { | ||
return false, err | ||
} | ||
return !greaterOrEqual, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,7 +103,7 @@ func TestBundle_EmptyDocument(t *testing.T) { | |
|
||
// Test with minimal document | ||
doc := &openapi.OpenAPI{ | ||
OpenAPI: "3.1.0", | ||
OpenAPI: openapi.Version, | ||
Info: openapi.Info{ | ||
Title: "Empty API", | ||
Version: "1.0.0", | ||
|
@@ -122,7 +122,7 @@ func TestBundle_EmptyDocument(t *testing.T) { | |
require.NoError(t, err) | ||
|
||
// Document should remain unchanged | ||
assert.Equal(t, "3.1.0", doc.OpenAPI) | ||
assert.Equal(t, openapi.Version, doc.OpenAPI) | ||
assert.Equal(t, "Empty API", doc.Info.Title) | ||
assert.Equal(t, "1.0.0", doc.Info.Version) | ||
|
||
|
@@ -172,3 +172,77 @@ func TestBundle_SiblingDirectories_Success(t *testing.T) { | |
// Compare the actual output with expected output | ||
assert.Equal(t, string(expectedBytes), string(actualYAML), "Bundled document should match expected output") | ||
} | ||
|
||
func TestBundle_AdditionalOperations_Success(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if possible I would try and add the additionalOperations test in to the original test above and update the expected doc etc, to make sure everything is working together. Generally also with tests like this its better to be asserting the exact shape of the document like we do in the test above rather than all the assertions you have below to catch where we might be introducing unintended changes etc, but that is just another reason to integrate it into the test above (which will then also mean we have the inline tests also testing it) |
||
t.Parallel() | ||
|
||
ctx := t.Context() | ||
|
||
// Load the input document with additionalOperations | ||
inputFile, err := os.Open("testdata/inline/additionaloperations_input.yaml") | ||
require.NoError(t, err) | ||
defer inputFile.Close() | ||
|
||
inputDoc, validationErrs, err := openapi.Unmarshal(ctx, inputFile) | ||
require.NoError(t, err) | ||
require.Empty(t, validationErrs, "Input document should be valid") | ||
|
||
// Configure bundling options | ||
opts := openapi.BundleOptions{ | ||
ResolveOptions: openapi.ResolveOptions{ | ||
RootDocument: inputDoc, | ||
TargetLocation: "testdata/inline/additionaloperations_input.yaml", | ||
}, | ||
NamingStrategy: openapi.BundleNamingFilePath, | ||
} | ||
|
||
// Bundle all external references | ||
err = openapi.Bundle(ctx, inputDoc, opts) | ||
require.NoError(t, err) | ||
|
||
// Marshal the bundled document to YAML | ||
var buf bytes.Buffer | ||
err = openapi.Marshal(ctx, inputDoc, &buf) | ||
require.NoError(t, err) | ||
actualYAML := buf.String() | ||
|
||
// Verify that external references in additionalOperations were bundled | ||
assert.Contains(t, actualYAML, "components:", "Components section should be created") | ||
assert.Contains(t, actualYAML, "additionalOperations:", "additionalOperations should be preserved") | ||
|
||
// Verify external schemas were bundled into components | ||
assert.Contains(t, actualYAML, "ResourceMetadata:", "External ResourceMetadata schema should be bundled") | ||
assert.Contains(t, actualYAML, "SyncConfig:", "External SyncConfig schema should be bundled") | ||
assert.Contains(t, actualYAML, "BatchConfig:", "External BatchConfig schema should be bundled") | ||
|
||
// Verify external parameters were bundled | ||
assert.Contains(t, actualYAML, "DestinationParam:", "External DestinationParam should be bundled") | ||
assert.Contains(t, actualYAML, "ConfirmationParam:", "External ConfirmationParam should be bundled") | ||
|
||
// Verify external responses were bundled | ||
assert.Contains(t, actualYAML, "CopyResponse:", "External CopyResponse should be bundled") | ||
assert.Contains(t, actualYAML, "ValidationErrorResponse:", "External ValidationErrorResponse should be bundled") | ||
|
||
// Verify external request bodies were bundled | ||
assert.Contains(t, actualYAML, "CopyRequest:", "External CopyRequest should be bundled") | ||
|
||
// Verify references in additionalOperations now point to components | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/parameters/DestinationParam\"", "COPY operation should reference bundled parameter") | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/requestBodies/CopyRequest\"", "COPY operation should reference bundled request body") | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/responses/CopyResponse\"", "COPY operation should reference bundled response") | ||
|
||
// Verify references in PURGE operation | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/parameters/ConfirmationParam\"", "PURGE operation should reference bundled parameter") | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/responses/ValidationErrorResponse\"", "PURGE operation should reference bundled response") | ||
|
||
// Verify references in SYNC operation | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/schemas/SyncConfig\"", "SYNC operation should reference bundled schema") | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/schemas/SyncResult\"", "SYNC operation should reference bundled schema") | ||
|
||
// Verify references in BATCH operation | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/schemas/BatchConfig\"", "BATCH operation should reference bundled schema") | ||
assert.Contains(t, actualYAML, "$ref: \"#/components/schemas/BatchResult\"", "BATCH operation should reference bundled schema") | ||
|
||
// Verify no external file references remain in additionalOperations | ||
assert.NotContains(t, actualYAML, "external_custom_operations.yaml#/", "No external file references should remain") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this just adds a compile time check that you implement the Stringer interface correctly with your String() method