Skip to content
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

CLOUDP-236398: Add validation logic for paths #7

Merged
merged 10 commits into from Mar 11, 2024
19 changes: 16 additions & 3 deletions tools/cli/internal/cli/merge/merge.go
Expand Up @@ -19,6 +19,7 @@ import (
"log"
"mongodb/openapi/tools/cli/internal/cli/flag"
"mongodb/openapi/tools/cli/internal/cli/usage"
"mongodb/openapi/tools/cli/internal/openapi"
"os"

"github.com/spf13/cobra"
Expand All @@ -29,14 +30,24 @@ const (
)

type Opts struct {
Merger openapi.Merger
basePath string
outputPath string
externalPaths []string
}

func (o *Opts) Run(_ []string) error {
// To add in follow up PR: CLOUDP-225849
return o.saveFile([]byte("test"))
federated, err := o.Merger.MergeOpenAPISpecs(o.externalPaths)
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we add unit tests or do we want to capture in another ticket?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

planning to add them in a follow-up PR where I add mocking

if err != nil {
return err
}

federatedBytes, err := federated.Spec.MarshalJSON()
if err != nil {
return err
}

return o.saveFile(federatedBytes)
}

func (o *Opts) PreRunE(_ []string) error {
Expand All @@ -48,7 +59,9 @@ func (o *Opts) PreRunE(_ []string) error {
return fmt.Errorf("no external OAS detected. Please, use the flag %s to include at least one OAS", flag.External)
}

return nil
m, err := openapi.NewOasDiff(o.basePath)
o.Merger = m
return err
}

func (o *Opts) saveFile(data []byte) error {
Expand Down
11 changes: 11 additions & 0 deletions tools/cli/internal/openapi/errors/path_conflict_error.go
@@ -0,0 +1,11 @@
package errors

import "fmt"

type PathConflictError struct {
Entry string
}

func (e PathConflictError) Error() string {
return fmt.Sprintf("there was a conflict with the path: %q", e.Entry)
}
78 changes: 78 additions & 0 deletions tools/cli/internal/openapi/oasdiff.go
@@ -0,0 +1,78 @@
package openapi

import (
"log"
"mongodb/openapi/tools/cli/internal/openapi/errors"

"github.com/tufin/oasdiff/diff"
"github.com/tufin/oasdiff/load"
)

type OasDiff struct {
base *load.SpecInfo
external *load.SpecInfo
config *diff.Config
specDiff *diff.Diff
parser Parser
}

func NewOasDiff(base string) (*OasDiff, error) {
parser := NewOpenAPI3()
baseSpec, err := parser.CreateOpenAPISpecFromPath(base)
if err != nil {
return nil, err
}

return &OasDiff{
base: baseSpec,
parser: parser,
config: &diff.Config{
IncludePathParams: true,
},
}, nil
}

func (o *OasDiff) MergeOpenAPISpecs(paths []string) (*load.SpecInfo, error) {
for _, p := range paths {
spec, err := o.parser.CreateOpenAPISpecFromPath(p)
if err != nil {
return nil, err
}

specDiff, err := diff.Get(o.config, o.base.Spec, spec.Spec)
if err != nil {
log.Fatalf("error in calculating the diff of the specs: %s", err)
return nil, err
}

o.specDiff = specDiff
o.external = spec
err = o.mergeSpecIntoBase()
if err != nil {
return nil, err
}
}

return o.base, nil
}

func (o OasDiff) mergeSpecIntoBase() error {
return o.mergePaths()
}

func (o OasDiff) mergePaths() error {
pathsToMerge := o.external.Spec.Paths
basePaths := o.base.Spec.Paths
for k, v := range pathsToMerge {
if _, ok := basePaths[k]; !ok {
basePaths[k] = v
} else {
return errors.PathConflictError{
Entry: k,
}
}
}

o.base.Spec.Paths = basePaths
return nil
}
13 changes: 13 additions & 0 deletions tools/cli/internal/openapi/openapi.go
@@ -0,0 +1,13 @@
package openapi

import (
"github.com/tufin/oasdiff/load"
)

type Merger interface {
MergeOpenAPISpecs([]string) (*load.SpecInfo, error)
}

type Parser interface {
CreateOpenAPISpecFromPath(string) (*load.SpecInfo, error)
}
31 changes: 31 additions & 0 deletions tools/cli/internal/openapi/openapi3.go
@@ -0,0 +1,31 @@
package openapi

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

type OpenAPI3 struct {
IsExternalRefsAllowed bool
CircularReferenceCounter int
}

func NewOpenAPI3() *OpenAPI3 {
return &OpenAPI3{
IsExternalRefsAllowed: true,
CircularReferenceCounter: 10,
}
}

func (o *OpenAPI3) CreateOpenAPISpecFromPath(path string) (*load.SpecInfo, error) {
openapi3.CircularReferenceCounter = o.CircularReferenceCounter
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = o.IsExternalRefsAllowed

spec, err := load.LoadSpecInfo(loader, load.NewSource(path))
if err != nil {
return nil, err
}

return spec, nil
}