github.com/circl-dev/go-swagger@v0.31.0/cmd/swagger/commands/diff.go (about)

     1  package commands
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  
    11  	"errors"
    12  
    13  	"github.com/circl-dev/go-swagger/cmd/swagger/commands/diff"
    14  	"github.com/circl-dev/loads"
    15  )
    16  
    17  // JSONFormat for json
    18  const JSONFormat = "json"
    19  
    20  // DiffCommand is a command that generates the diff of two swagger specs.
    21  //
    22  // There are no specific options for this expansion.
    23  type DiffCommand struct {
    24  	OnlyBreakingChanges bool   `long:"break" short:"b" description:"When present, only shows incompatible changes"`
    25  	Format              string `long:"format" short:"f" description:"When present, writes output as json" default:"txt" choice:"txt" choice:"json"`
    26  	IgnoreFile          string `long:"ignore" short:"i" description:"Exception file of diffs to ignore (copy output from json diff format)"  default:"none specified"`
    27  	Destination         string `long:"dest" short:"d" description:"Output destination file or stdout" default:"stdout"`
    28  	Args                struct {
    29  		OldSpec string `positional-arg-name:"{old spec}"`
    30  		NewSpec string `positional-arg-name:"{new spec}"`
    31  	} `required:"2" positional-args:"specs" description:"Input specs to be diff-ed"`
    32  }
    33  
    34  // Execute diffs the two specs provided
    35  func (c *DiffCommand) Execute(_ []string) error {
    36  	if c.Args.OldSpec == "" || c.Args.NewSpec == "" {
    37  		return errors.New(`missing arguments for diff command (use --help for more info)`)
    38  	}
    39  
    40  	c.printInfo()
    41  
    42  	var (
    43  		output io.WriteCloser
    44  		err    error
    45  	)
    46  	if c.Destination != "stdout" {
    47  		output, err = os.OpenFile(c.Destination, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
    48  		if err != nil {
    49  			return fmt.Errorf("%s: %w", c.Destination, err)
    50  		}
    51  		defer func() {
    52  			_ = output.Close()
    53  		}()
    54  	} else {
    55  		output = os.Stdout
    56  	}
    57  
    58  	diffs, err := c.getDiffs()
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	ignores, err := c.readIgnores()
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	diffs = diffs.FilterIgnores(ignores)
    69  	if len(ignores) > 0 {
    70  		log.Printf("Diff Report Ignored Items from IgnoreFile")
    71  		for _, eachItem := range ignores {
    72  			log.Printf("%s", eachItem.String())
    73  		}
    74  	}
    75  
    76  	var (
    77  		input io.Reader
    78  		warn  error
    79  	)
    80  	if c.Format != JSONFormat && c.OnlyBreakingChanges {
    81  		input, err, warn = diffs.ReportCompatibility()
    82  	} else {
    83  		input, err, warn = diffs.ReportAllDiffs(c.Format == JSONFormat)
    84  	}
    85  	if err != nil {
    86  		return err
    87  	}
    88  	_, err = io.Copy(output, input)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	return warn
    93  }
    94  
    95  func (c *DiffCommand) readIgnores() (diff.SpecDifferences, error) {
    96  	ignoreFile := c.IgnoreFile
    97  	ignoreDiffs := diff.SpecDifferences{}
    98  
    99  	if ignoreFile == "none specified" || ignoreFile == "" {
   100  		return ignoreDiffs, nil
   101  	}
   102  	// Open our jsonFile
   103  	jsonFile, err := os.Open(ignoreFile)
   104  	if err != nil {
   105  		return nil, fmt.Errorf("%s: %w", ignoreFile, err)
   106  	}
   107  	defer func() {
   108  		_ = jsonFile.Close()
   109  	}()
   110  	byteValue, err := ioutil.ReadAll(jsonFile)
   111  	if err != nil {
   112  		return nil, fmt.Errorf("reading %s: %w", ignoreFile, err)
   113  	}
   114  	err = json.Unmarshal(byteValue, &ignoreDiffs)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return ignoreDiffs, nil
   119  }
   120  
   121  func (c *DiffCommand) getDiffs() (diff.SpecDifferences, error) {
   122  	oldSpecPath, newSpecPath := c.Args.OldSpec, c.Args.NewSpec
   123  	swaggerDoc1 := oldSpecPath
   124  	specDoc1, err := loads.Spec(swaggerDoc1)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	swaggerDoc2 := newSpecPath
   130  	specDoc2, err := loads.Spec(swaggerDoc2)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return diff.Compare(specDoc1.Spec(), specDoc2.Spec())
   136  }
   137  
   138  func (c *DiffCommand) printInfo() {
   139  	log.Println("Run Config:")
   140  	log.Printf("Spec1: %s", c.Args.OldSpec)
   141  	log.Printf("Spec2: %s", c.Args.NewSpec)
   142  	log.Printf("ReportOnlyBreakingChanges (-c) :%v", c.OnlyBreakingChanges)
   143  	log.Printf("OutputFormat (-f) :%s", c.Format)
   144  	log.Printf("IgnoreFile (-i) :%s", c.IgnoreFile)
   145  	log.Printf("Diff Report Destination (-d) :%s", c.Destination)
   146  }