Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions tools/cli/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ build:
go build -ldflags "$(LINKER_FLAGS)" -o $(DESTINATION) $(SOURCE_FILES)


.PHONY: run
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andreaangiolillo low priority question. Do you have any proven way to run cli for testing with our openapi?
Any gist/documentation that does run CLI?

run:
@echo "==> Running foascli binary"
go run -ldflags "$(LINKER_FLAGS)" $(SOURCE_FILES)


.PHONY: build-debug
build-debug:
@echo "==> Building foascli binary for debugging"
Expand Down
3 changes: 2 additions & 1 deletion tools/cli/internal/openapi/filter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ The Atlas Admin API OpenAPI specifications are used not only to document REST en
[ExtensionFilter: is a filter that removes the x-xgen-IPA-exception extension from the OpenAPI spec.](../internal/openapi/filter/extension.go?plain=1#L21)
[HiddenEnvsFilter: is a filter that removes paths, operations,](../internal/openapi/filter/hidden_envs.go?plain=1#L28)
[InfoVersioningFilter: Filter that modifies the Info object in the OpenAPI spec with the target version.](../internal/openapi/filter/info.go?plain=1#L23)
[MergeOneOfFilter: is a filter that merges properties from allOf into each child schema within oneOf to eliminate circular dependencies.](../internal/openapi/filter/merge_oneof.go?plain=1#L21)
[OperationsFilter: is a filter that removes the x-xgen-owner-team extension from operations.](../internal/openapi/filter/operations.go?plain=1#L20)
[TagsFilter: removes tags that are not used in the operations.](../internal/openapi/filter/tags.go?plain=1#L23)
[VersioningExtensionFilter: is a filter that updates the x-sunset and x-xgen-version extensions to a date string](../internal/openapi/filter/versioning_extension.go?plain=1#L25)
[VersioningFilter: is a filter that modifies the OpenAPI spec by removing operations and responses](../internal/openapi/filter/versioning.go?plain=1#L25)
[VersioningFilter: is a filter that modifies the OpenAPI spec by removing operations and responses](../internal/openapi/filter/versioning.go?plain=1#L25)
2 changes: 2 additions & 0 deletions tools/cli/internal/openapi/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func DefaultFilters(oas *openapi3.T, metadata *Metadata) []Filter {
&HiddenEnvsFilter{oas: oas, metadata: metadata},
&TagsFilter{oas: oas},
&OperationsFilter{oas: oas},
&MergeOneOfFilter{oas: oas}, // Added MergeOneOfFilter
}
}

Expand All @@ -86,6 +87,7 @@ func FiltersWithoutVersioning(oas *openapi3.T, metadata *Metadata) []Filter {
&HiddenEnvsFilter{oas: oas, metadata: metadata},
&TagsFilter{oas: oas},
&OperationsFilter{oas: oas},
&MergeOneOfFilter{oas: oas}, // Added MergeOneOfFilter
}
}

Expand Down
170 changes: 170 additions & 0 deletions tools/cli/internal/openapi/filter/merge_oneof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package filter

import (
"github.com/getkin/kin-openapi/openapi3"
)

// MergeOneOfFilter: is a filter that merges properties from allOf into each child schema within oneOf to eliminate circular dependencies.
type MergeOneOfFilter struct {
oas *openapi3.T
}

func (*MergeOneOfFilter) ValidateMetadata() error {
return nil
}

func (f *MergeOneOfFilter) Apply() error {
if f.oas == nil {
return nil
}

// Process components schemas
if f.oas.Components != nil && f.oas.Components.Schemas != nil {
for _, schemaRef := range f.oas.Components.Schemas {
if schemaRef != nil && schemaRef.Value != nil {
f.processSchema(schemaRef.Value)
}
}
}

// Process paths and their operations
if f.oas.Paths != nil {
for _, pathItem := range f.oas.Paths.Map() {
// Process parameters at path level
for _, paramRef := range pathItem.Parameters {
if paramRef != nil && paramRef.Value != nil && paramRef.Value.Schema != nil && paramRef.Value.Schema.Value != nil {
f.processSchema(paramRef.Value.Schema.Value)
}
}

// Process operations
for _, operation := range pathItem.Operations() {
// Process operation parameters
for _, paramRef := range operation.Parameters {
if paramRef != nil && paramRef.Value != nil && paramRef.Value.Schema != nil && paramRef.Value.Schema.Value != nil {
f.processSchema(paramRef.Value.Schema.Value)
}
}

// Process request body
if operation.RequestBody != nil && operation.RequestBody.Value != nil {
for _, mediaType := range operation.RequestBody.Value.Content {
if mediaType.Schema != nil && mediaType.Schema.Value != nil {
f.processSchema(mediaType.Schema.Value)
}
}
}

// Process responses
for _, responseRef := range operation.Responses.Map() {
if responseRef != nil && responseRef.Value != nil {
for _, mediaType := range responseRef.Value.Content {
if mediaType.Schema != nil && mediaType.Schema.Value != nil {
f.processSchema(mediaType.Schema.Value)
}
}
}
}
}
}
}

return nil
}

// processSchema recursively processes a schema to merge allOf properties into oneOf schemas

Check failure on line 75 in tools/cli/internal/openapi/filter/merge_oneof.go

View workflow job for this annotation

GitHub Actions / lint

Comment should end in a period (godot)
func (f *MergeOneOfFilter) processSchema(schema *openapi3.Schema) {
if schema == nil {
return
}

// If schema has both allOf and oneOf, we need to merge properties
if len(schema.AllOf) > 0 && len(schema.OneOf) > 0 {
// Collect all properties from allOf schemas
allOfProperties := make(map[string]*openapi3.SchemaRef)
allOfRequired := make([]string, 0)

for _, allOfSchema := range schema.AllOf {
if allOfSchema != nil && allOfSchema.Value != nil {
// Merge properties
for propName, propSchema := range allOfSchema.Value.Properties {
allOfProperties[propName] = propSchema
}

// Merge required fields
allOfRequired = append(allOfRequired, allOfSchema.Value.Required...)
}
}

// Apply collected properties to each oneOf schema
for _, oneOfSchema := range schema.OneOf {
if oneOfSchema != nil && oneOfSchema.Value != nil {

Check failure on line 101 in tools/cli/internal/openapi/filter/merge_oneof.go

View workflow job for this annotation

GitHub Actions / lint

nestingReduce: invert if cond, replace body with `continue`, move old body after the statement (gocritic)
// Initialize properties map if nil
if oneOfSchema.Value.Properties == nil {
oneOfSchema.Value.Properties = make(map[string]*openapi3.SchemaRef)
}

// Copy allOf properties into oneOf schema
for propName, propSchema := range allOfProperties {
// Only add if the property doesn't exist in the oneOf schema
if _, exists := oneOfSchema.Value.Properties[propName]; !exists {
oneOfSchema.Value.Properties[propName] = propSchema
}
}

// Merge required fields (avoiding duplicates)
requiredSet := make(map[string]bool)
for _, req := range oneOfSchema.Value.Required {
requiredSet[req] = true
}

for _, req := range allOfRequired {
if !requiredSet[req] {
oneOfSchema.Value.Required = append(oneOfSchema.Value.Required, req)
requiredSet[req] = true
}
}
}
}
}

// Recursively process nested schemas

Check failure on line 132 in tools/cli/internal/openapi/filter/merge_oneof.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofmt)
// Process properties
for _, propSchema := range schema.Properties {
if propSchema != nil && propSchema.Value != nil {
f.processSchema(propSchema.Value)
}
}

// Process additionalProperties
if schema.AdditionalProperties.Schema != nil && schema.AdditionalProperties.Schema.Value != nil {
f.processSchema(schema.AdditionalProperties.Schema.Value)
}

// Process items for arrays
if schema.Items != nil && schema.Items.Value != nil {
f.processSchema(schema.Items.Value)
}

// Process allOf schemas
for _, subSchema := range schema.AllOf {
if subSchema != nil && subSchema.Value != nil {
f.processSchema(subSchema.Value)
}
}

// Process oneOf schemas
for _, subSchema := range schema.OneOf {
if subSchema != nil && subSchema.Value != nil {
f.processSchema(subSchema.Value)
}
}

// Process anyOf schemas
for _, subSchema := range schema.AnyOf {
if subSchema != nil && subSchema.Value != nil {
f.processSchema(subSchema.Value)
}
}
}
Loading