github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/cmd/swagger/commands/diff.go (about)

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