Skip to content

Commit

Permalink
read specs from stdin
Browse files Browse the repository at this point in the history
  • Loading branch information
Reuven committed Sep 27, 2023
1 parent 4fcbb57 commit 36582ab
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 13 deletions.
2 changes: 1 addition & 1 deletion checker/checker_deprecation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

func open(file string) (*load.SpecInfo, error) {
return load.LoadSpecInfoFromFile(openapi3.NewLoader(), file)
return load.LoadSpecInfo(openapi3.NewLoader(), file)
}

func getDeprecationFile(file string) string {
Expand Down
4 changes: 2 additions & 2 deletions internal/breaking_changes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func getBreakingChangesCmd() *cobra.Command {
Use: "breaking base revision [flags]",
Short: "Display breaking changes",
Long: `Display breaking changes between base and revision specs.
Base and revision can be a path to a file or a URL.
Base and revision can be a path to a file, a URL or '-' to read standard input.
In 'composed' mode, base and revision can be a glob and oasdiff will compare matching endpoints between the two sets of files.
`,
Args: cobra.ExactArgs(2),
Expand Down Expand Up @@ -58,7 +58,7 @@ In 'composed' mode, base and revision can be a glob and oasdiff will compare mat
enumWithOptions(&cmd, newEnumValue([]string{LangEn, LangRu}, LangDefault, &flags.lang), "lang", "l", "language for localized output")
cmd.PersistentFlags().StringVarP(&flags.errIgnoreFile, "err-ignore", "", "", "configuration file for ignoring errors")
cmd.PersistentFlags().StringVarP(&flags.warnIgnoreFile, "warn-ignore", "", "", "configuration file for ignoring warnings")
enumWithOptions(&cmd, newEnumSliceValue(checker.GetOptionalChecks(), nil, &flags.includeChecks), "include-checks", "i", "comma-separated list of optional checks (run 'oasdiff checks' to see options)")
cmd.PersistentFlags().VarP(newEnumSliceValue(checker.GetOptionalChecks(), nil, &flags.includeChecks), "include-checks", "i", "comma-separated list of optional checks (run 'oasdiff checks' to see options)")
cmd.PersistentFlags().IntVarP(&flags.deprecationDaysBeta, "deprecation-days-beta", "", checker.BetaDeprecationDays, "min days required between deprecating a beta resource and removing it")
cmd.PersistentFlags().IntVarP(&flags.deprecationDaysStable, "deprecation-days-stable", "", checker.StableDeprecationDays, "min days required between deprecating a stable resource and removing it")

Expand Down
2 changes: 1 addition & 1 deletion internal/changelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func getChangelogCmd() *cobra.Command {
Use: "changelog base revision [flags]",
Short: "Display changelog",
Long: `Display a changelog between base and revision specs.
Base and revision can be a path to a file or a URL.
Base and revision can be a path to a file, a URL or '-' to read standard input.
In 'composed' mode, base and revision can be a glob and oasdiff will compare mathcing endpoints between the two sets of files.
`,
Args: cobra.ExactArgs(2),
Expand Down
7 changes: 6 additions & 1 deletion internal/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func getDiffCmd() *cobra.Command {
Use: "diff base revision [flags]",
Short: "Generate a diff report",
Long: `Generate a diff report between base and revision specs.
Base and revision can be a path to a file or a URL.
Base and revision can be a path to a file, a URL or '-' to read standard input.
In 'composed' mode, base and revision can be a glob and oasdiff will compare matching endpoints between the two sets of files.
`,
Args: cobra.ExactArgs(2),
Expand Down Expand Up @@ -131,6 +131,11 @@ func normalDiff(loader load.Loader, flags Flags) (*diff.Diff, *diff.OperationsSo
return nil, nil, getErrFailedToLoadSpec("revision", flags.getRevision(), err)
}

if flags.getBase() == "-" && flags.getRevision() == "-" {
// io.ReadAll can only read stdin once, so in this edge case, we copy base into revision
s2.Spec = s1.Spec
}

if flags.getFlatten() {
if err := mergeAllOf("base", []*load.SpecInfo{s1}); err != nil {
return nil, nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/flatten.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func getFlattenCmd() *cobra.Command {
Use: "flatten spec",
Short: "Merge allOf",
Long: `Display a flattened version of the given OpenAPI spec by merging all instances of allOf.
Spec can be a path to a file or a URL.
Spec can be a path to a file, a URL or '-' to read standard input.
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
2 changes: 1 addition & 1 deletion internal/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func getSummaryCmd() *cobra.Command {
Use: "summary base revision [flags]",
Short: "Generate a diff summary",
Long: `Display a summary of changes between base and revision specs.
Base and revision can be a path to a file or a URL.
Base and revision can be a path to a file, a URL or '-' to read standard input.
In 'composed' mode, base and revision can be a glob and oasdiff will compare matching endpoints between the two sets of files.
`,
Args: cobra.ExactArgs(2),
Expand Down
5 changes: 5 additions & 0 deletions load/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ import (
type Loader interface {
LoadFromURI(*url.URL) (*openapi3.T, error)
LoadFromFile(string) (*openapi3.T, error)
LoadFromStdin() (*openapi3.T, error)
}

// From is a convenience function that opens an OpenAPI spec from a URL or a local path based on the format of the path parameter
func From(loader Loader, path string) (*openapi3.T, error) {

if path == "-" {
return loader.LoadFromStdin()
}

uri, err := url.ParseRequestURI(path)
if err == nil {
return loadFromURI(loader, uri)
Expand Down
42 changes: 42 additions & 0 deletions load/load_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package load_test

import (
"log"
"net/url"
"os"
"testing"

"github.com/getkin/kin-openapi/openapi3"
Expand All @@ -19,6 +21,10 @@ func (mockLoader MockLoader) LoadFromURI(location *url.URL) (*openapi3.T, error)
return openapi3.NewLoader().LoadFromFile(RelativeDataPath + location.Path)
}

func (mockLoader MockLoader) LoadFromStdin() (*openapi3.T, error) {
return openapi3.NewLoader().LoadFromStdin()
}

type MockLoader struct{}

func TestLoad_File(t *testing.T) {
Expand All @@ -35,3 +41,39 @@ func TestLoad_URIError(t *testing.T) {
_, err := load.From(MockLoader{}, "http://localhost/null")
require.Error(t, err)
}

func TestLoad_Stdin(t *testing.T) {
content := []byte(`openapi: 3.0.1
info:
title: Test API
version: v1
paths:
/partner-api/test/some-method:
get:
responses:
"200":
description: Success
`)

tmpfile, err := os.CreateTemp("", "example")
if err != nil {
log.Fatal(err)
}

defer os.Remove(tmpfile.Name()) // clean up

if _, err := tmpfile.Write(content); err != nil {
log.Fatal(err)
}

if _, err := tmpfile.Seek(0, 0); err != nil {
log.Fatal(err)
}

oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }() // Restore original Stdin

os.Stdin = tmpfile
_, err = load.From(MockLoader{}, "-")
require.NoError(t, err)
}
6 changes: 0 additions & 6 deletions load/spec_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ type SpecInfo struct {
Spec *openapi3.T
}

// LoadSpecInfoFromFile creates a SpecInfo from a local file path
func LoadSpecInfoFromFile(loader Loader, location string) (*SpecInfo, error) {
s, err := loader.LoadFromFile(location)
return &SpecInfo{Spec: s, Url: location}, err
}

// LoadSpecInfo creates a SpecInfo from a local file path or a URL
func LoadSpecInfo(loader Loader, location string) (*SpecInfo, error) {
s, err := From(loader, location)
Expand Down
42 changes: 42 additions & 0 deletions load/spec_info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package load_test

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/tufin/oasdiff/load"
)

func TestLoadSpecInfo(t *testing.T) {
_, err := load.LoadSpecInfo(MockLoader{}, "openapi-test1.yaml")
require.NoError(t, err)
}

func TestLoadGlob_OK(t *testing.T) {
_, err := load.FromGlob(MockLoader{}, RelativeDataPath+"*.yaml")
require.NoError(t, err)
}

func TestLoadGlob_InvalidSpec(t *testing.T) {
_, err := load.FromGlob(MockLoader{}, RelativeDataPath+"ignore-err-example.txt")
require.Error(t, err)
require.Equal(t, "error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type openapi3.TBis", err.Error())
}

func TestLoadGlob_Invalid(t *testing.T) {
_, err := load.FromGlob(MockLoader{}, "[*")
require.Error(t, err)
require.Equal(t, "syntax error in pattern", err.Error())
}

func TestLoadGlob_URL(t *testing.T) {
_, err := load.FromGlob(MockLoader{}, "http://localhost/openapi-test1.yaml")
require.Error(t, err)
require.Equal(t, "no matching files (should be a glob, not a URL)", err.Error())
}

func TestLoadGlob_NoFiles(t *testing.T) {
_, err := load.FromGlob(MockLoader{}, RelativeDataPath+"*.xxx")
require.Error(t, err)
require.Equal(t, "no matching files", err.Error())
}

0 comments on commit 36582ab

Please sign in to comment.