Skip to content
Merged
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
2 changes: 1 addition & 1 deletion tools/cli/internal/cli/split/split.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func Builder() *cobra.Command {
}

cmd.Flags().StringVarP(&opts.basePath, flag.Spec, flag.SpecShort, "-", usage.Spec)
cmd.Flags().StringVar(&opts.env, flag.Environment, "", usage.Environment)
cmd.Flags().StringVar(&opts.env, flag.Environment, "prod", usage.Environment)
cmd.Flags().StringVarP(&opts.outputPath, flag.Output, flag.OutputShort, "", usage.Output)
cmd.Flags().StringVarP(&opts.format, flag.Format, flag.FormatShort, openapi.ALL, usage.Format)
cmd.Flags().StringVar(&opts.gitSha, flag.GitSha, "", usage.GitSha)
Expand Down
5 changes: 5 additions & 0 deletions tools/cli/internal/cli/split/split_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestSuccessfulSplit_Run(t *testing.T) {
basePath: "../../../test/data/base_spec.json",
outputPath: "foas.yaml",
fs: fs,
env: "dev",
}

if err := opts.Run(); err != nil {
Expand All @@ -44,6 +45,7 @@ func TestSplitPublicPreviewRun(t *testing.T) {
basePath: "../../../test/data/base_spec_with_public_preview.json",
outputPath: "foas.yaml",
fs: fs,
env: "dev",
}

if err := opts.Run(); err != nil {
Expand All @@ -64,6 +66,7 @@ func TestSplitPrivatePreviewRun(t *testing.T) {
basePath: "../../../test/data/base_spec_with_private_preview.json",
outputPath: "foas.yaml",
fs: fs,
env: "dev",
}

if err := opts.Run(); err != nil {
Expand All @@ -83,6 +86,7 @@ func TestSplitMulitplePreviewsRun(t *testing.T) {
opts := &Opts{
basePath: "../../../test/data/base_spec_with_multiple_private_and_public_previews.json",
outputPath: "foas.yaml",
env: "dev",
fs: fs,
}

Expand Down Expand Up @@ -122,6 +126,7 @@ func TestInjectSha_Run(t *testing.T) {
outputPath: "foas.yaml",
fs: fs,
gitSha: "123456",
env: "dev",
}

if err := opts.Run(); err != nil {
Expand Down
12 changes: 7 additions & 5 deletions tools/cli/internal/openapi/filter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ The Atlas Admin API OpenAPI specifications are used not only to document REST en
- Filtering per version, so that only the endpoints that are available in that version are shown.
## What filters are available?
### List of filters
[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)
[OperationsFilter is a filter that removes the x-xgen-owner-team extension from operations](../internal/openapi/filter/operations.go?plain=1#L20)
[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)
[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)
[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)
6 changes: 5 additions & 1 deletion tools/cli/internal/openapi/filter/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/getkin/kin-openapi/openapi3"
)

// Filter: ExtensionFilter is a filter that removes the x-xgen-IPA-exception extension from the OpenAPI spec.
// ExtensionFilter: is a filter that removes the x-xgen-IPA-exception extension from the OpenAPI spec.
type ExtensionFilter struct {
oas *openapi3.T
metadata *Metadata
Expand All @@ -29,6 +29,10 @@ const (
format = "2006-01-02T15:04:05Z07:00"
)

func (f *ExtensionFilter) ValidateMetadata() error {
return validateMetadata(f.metadata)
}

func (f *ExtensionFilter) Apply() error {
for _, pathItem := range f.oas.Paths.Map() {
if pathItem == nil {
Expand Down
40 changes: 35 additions & 5 deletions tools/cli/internal/openapi/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"encoding/json"
"errors"
"fmt"
reflect "reflect"

"github.com/getkin/kin-openapi/openapi3"
"github.com/mongodb/openapi/tools/cli/internal/apiversion"
Expand All @@ -25,6 +26,7 @@ import (
//go:generate mockgen -destination=../filter/mock_filter.go -package=filter github.com/mongodb/openapi/tools/cli/internal/openapi/filter Filter
type Filter interface {
Apply() error
ValidateMetadata() error
}

type Metadata struct {
Expand All @@ -39,11 +41,29 @@ func NewMetadata(targetVersion *apiversion.APIVersion, targetEnv string) *Metada
}
}

// validateMetadata validates the metadata object, ensuring its not nil and has a target env.
func validateMetadata(metadata *Metadata) error {
if metadata == nil {
return errors.New("metadata is nil")
}

if metadata.targetEnv == "" {
return errors.New("target environment is empty")
}

return nil
}

// validateMetadataWithVersion validates the metadata object, ensuring its not nil and has a target version.
func validateMetadataWithVersion(metadata *Metadata) error {
if err := validateMetadata(metadata); err != nil {
return err
}

if metadata.targetVersion == nil {
return errors.New("target version is nil")
}

return nil
}

Expand All @@ -52,7 +72,17 @@ func DefaultFilters(oas *openapi3.T, metadata *Metadata) []Filter {
&ExtensionFilter{oas: oas, metadata: metadata},
&VersioningExtensionFilter{oas: oas, metadata: metadata},
&VersioningFilter{oas: oas, metadata: metadata},
&InfoFilter{oas: oas, metadata: metadata},
&InfoVersioningFilter{oas: oas, metadata: metadata},
&HiddenEnvsFilter{oas: oas, metadata: metadata},
&TagsFilter{oas: oas},
&OperationsFilter{oas: oas},
}
}

func FiltersWithoutVersioning(oas *openapi3.T, metadata *Metadata) []Filter {
return []Filter{
&ExtensionFilter{oas: oas, metadata: metadata},
&InfoVersioningFilter{oas: oas, metadata: metadata},
&HiddenEnvsFilter{oas: oas, metadata: metadata},
&TagsFilter{oas: oas},
&OperationsFilter{oas: oas},
Expand All @@ -71,17 +101,17 @@ func ApplyFilters(doc *openapi3.T, metadata *Metadata, filters func(oas *openapi
return nil, errors.New("openapi document is nil")
}

if err := validateMetadata(metadata); err != nil {
return nil, err
}

// make a copy of the oas to avoid modifying the original document when applying filters
oas, err := duplicateOas(doc)
if err != nil {
return nil, err
}

for _, filter := range filters(oas, metadata) {
if err := filter.ValidateMetadata(); err != nil {
s := reflect.TypeOf(filter)
return nil, fmt.Errorf("failed to validate metadata for filter %s with: %w", s, err)
}
if err := filter.Apply(); err != nil {
return nil, err
}
Expand Down
132 changes: 132 additions & 0 deletions tools/cli/internal/openapi/filter/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2024 MongoDB Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package filter

import (
"testing"

"github.com/getkin/kin-openapi/openapi3"
"github.com/mongodb/openapi/tools/cli/internal/apiversion"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNewMetadata(t *testing.T) {
version := &apiversion.APIVersion{}
env := "test-env"
metadata := NewMetadata(version, env)

assert.Equal(t, version, metadata.targetVersion)
assert.Equal(t, env, metadata.targetEnv)
}

func TestValidateMetadata(t *testing.T) {
t.Run("Valid metadata", func(t *testing.T) {
metadata := &Metadata{
targetEnv: "dev",
}
err := validateMetadata(metadata)
assert.NoError(t, err)
})

t.Run("Nil metadata", func(t *testing.T) {
err := validateMetadata(nil)
require.ErrorContains(t, err, "metadata is nil")
})
}

func TestDuplicateOas(t *testing.T) {
doc := &openapi3.T{
Info: &openapi3.Info{
Title: "Test API",
Version: "1.0.0",
},
}

duplicateDoc, err := duplicateOas(doc)
require.NoError(t, err)
require.NotNil(t, duplicateDoc)
assert.Equal(t, doc.Info.Title, duplicateDoc.Info.Title)
assert.Equal(t, doc.Info.Version, duplicateDoc.Info.Version)
}

func TestApplyFilters(t *testing.T) {
doc := &openapi3.T{
Info: &openapi3.Info{
Title: "Test API",
Version: "1.0.0",
},
}
metadata := &Metadata{}

t.Run("Nil document", func(t *testing.T) {
_, err := ApplyFilters(nil, metadata, DefaultFilters)
require.ErrorContains(t, err, "openapi document is nil")
})

t.Run("Nil metadata", func(t *testing.T) {
_, err := ApplyFilters(doc, nil, DefaultFilters)
require.ErrorContains(t, err, "metadata is nil")
})

t.Run("Invalid metadata", func(t *testing.T) {
_, err := ApplyFilters(doc, metadata, DefaultFilters)
require.ErrorContains(t, err, "target environment is empty")
})

t.Run("Missing versioning metadata", func(t *testing.T) {
metadata := &Metadata{
targetEnv: "dev",
}
_, err := ApplyFilters(doc, metadata, DefaultFilters)
require.ErrorContains(t, err, "failed to validate metadata for filter *filter.VersioningExtensionFilter with: target version is nil")
})

t.Run("Valid metadata for default filters", func(t *testing.T) {
version, err := apiversion.New(apiversion.WithVersion("2023-11-15"))
require.NoError(t, err)
metadata := &Metadata{
targetEnv: "dev",
targetVersion: version,
}

filteredDoc, err := ApplyFilters(doc, metadata, DefaultFilters)
require.NoError(t, err)
assert.NotNil(t, filteredDoc)
})
}

func TestDefaultFilters(t *testing.T) {
doc := &openapi3.T{}
metadata := &Metadata{}
filters := DefaultFilters(doc, metadata)

assert.Len(t, filters, 7)
}

func TestFiltersWithoutVersioning(t *testing.T) {
doc := &openapi3.T{}
metadata := &Metadata{}
filters := FiltersWithoutVersioning(doc, metadata)

assert.Len(t, filters, 5)
}

func TestFiltersToGetVersions(t *testing.T) {
doc := &openapi3.T{}
metadata := &Metadata{}
filters := FiltersToGetVersions(doc, metadata)

assert.Len(t, filters, 1)
}
6 changes: 5 additions & 1 deletion tools/cli/internal/openapi/filter/hidden_envs.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ const (
hiddenEnvsExtKey = "envs"
)

// Filter: HiddenEnvsFilter is a filter that removes paths, operations,
// HiddenEnvsFilter: is a filter that removes paths, operations,
// request/response bodies and content types that are hidden for the target environment.
type HiddenEnvsFilter struct {
oas *openapi3.T
metadata *Metadata
}

func (f *HiddenEnvsFilter) ValidateMetadata() error {
return validateMetadata(f.metadata)
}

func (f *HiddenEnvsFilter) Apply() error {
// delete hidden paths first before processing
for pathName, pathItem := range f.oas.Paths.Map() {
Expand Down
10 changes: 7 additions & 3 deletions tools/cli/internal/openapi/filter/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ import (
"github.com/mongodb/openapi/tools/cli/internal/apiversion"
)

// InfoFilter is a filter that modifies the Info object in the OpenAPI spec.
type InfoFilter struct {
// InfoVersioningFilter: Filter that modifies the Info object in the OpenAPI spec with the target version.
type InfoVersioningFilter struct {
oas *openapi3.T
metadata *Metadata
}

func (f *InfoFilter) Apply() error {
func (f *InfoVersioningFilter) ValidateMetadata() error {
return validateMetadataWithVersion(f.metadata)
}

func (f *InfoVersioningFilter) Apply() error {
if f.oas.Info == nil {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion tools/cli/internal/openapi/filter/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestInfoFilter(t *testing.T) {
},
}

filter := &InfoFilter{
filter := &InfoVersioningFilter{
metadata: NewMetadata(targetVersion, "test"),
oas: oas,
}
Expand Down
6 changes: 5 additions & 1 deletion tools/cli/internal/openapi/filter/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ import (
"github.com/getkin/kin-openapi/openapi3"
)

// Filter: OperationsFilter is a filter that removes the x-xgen-owner-team extension from operations
// OperationsFilter: is a filter that removes the x-xgen-owner-team extension from operations
// and moves the x-sunset extension to the operation level.
type OperationsFilter struct {
oas *openapi3.T
}

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

func (f *OperationsFilter) Apply() error {
if f.oas.Paths == nil {
return nil
Expand Down
6 changes: 5 additions & 1 deletion tools/cli/internal/openapi/filter/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ import (
"github.com/getkin/kin-openapi/openapi3"
)

// TagsFilter removes tags that are not used in the operations.
// TagsFilter: removes tags that are not used in the operations.
type TagsFilter struct {
oas *openapi3.T
}

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

func (f *TagsFilter) Apply() error {
if f.oas.Tags == nil {
return nil
Expand Down
Loading
Loading