github.com/snyk/vervet/v4@v4.27.2/internal/cmd/filter.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 6 "github.com/getkin/kin-openapi/openapi3" 7 "github.com/urfave/cli/v2" 8 9 "github.com/snyk/vervet/v4" 10 ) 11 12 // FilterCommand is the `vervet filter` subcommand 13 var FilterCommand = cli.Command{ 14 Name: "filter", 15 Usage: "Filter an OpenAPI document", 16 ArgsUsage: "[spec.yaml file]", 17 Flags: []cli.Flag{ 18 &cli.StringSliceFlag{Name: "include-paths", Aliases: []string{"I"}}, 19 &cli.StringSliceFlag{Name: "exclude-paths", Aliases: []string{"X"}}, 20 }, 21 Action: Filter, 22 } 23 24 // Filter an OpenAPI spec file. 25 func Filter(ctx *cli.Context) error { 26 if ctx.Args().Len() < 1 { 27 return fmt.Errorf("missing spec.yaml file") 28 } 29 specFile, err := absPath(ctx.Args().Get(0)) 30 if err != nil { 31 return fmt.Errorf("failed to resolve %q", ctx.Args().Get(0)) 32 } 33 doc, err := vervet.NewDocumentFile(specFile) 34 if err != nil { 35 return fmt.Errorf("failed to load spec from %q: %v", specFile, err) 36 } 37 38 // Localize all references, so we emit a completely self-contained OpenAPI document. 39 err = vervet.Localize(doc) 40 if err != nil { 41 return fmt.Errorf("failed to localize refs: %w", err) 42 } 43 44 if excludePaths := ctx.StringSlice("exclude-paths"); len(excludePaths) > 0 { 45 for _, excludePath := range excludePaths { 46 delete(doc.Paths, excludePath) 47 } 48 } 49 if includePaths := ctx.StringSlice("include-paths"); len(includePaths) > 0 { 50 newPaths := openapi3.Paths{} 51 for _, includePath := range includePaths { 52 if pathInfo, ok := doc.Paths[includePath]; ok { 53 newPaths[includePath] = pathInfo 54 } 55 } 56 doc.Paths = newPaths 57 } 58 59 err = removeOrphanedComponents(doc.T) 60 if err != nil { 61 return fmt.Errorf("failed to remove orphaned components: %W", err) 62 } 63 64 yamlBuf, err := vervet.ToSpecYAML(doc) 65 if err != nil { 66 return fmt.Errorf("failed to convert JSON to YAML: %w", err) 67 } 68 fmt.Println(string(yamlBuf)) 69 70 err = doc.Validate(ctx.Context) 71 if err != nil { 72 return fmt.Errorf("error: spec validation failed: %w", err) 73 } 74 return nil 75 } 76 77 func removeOrphanedComponents(t *openapi3.T) error { 78 ix, err := vervet.NewRefIndex(t) 79 if err != nil { 80 return err 81 } 82 if t.Components.Schemas != nil { 83 var remove []string 84 for key := range t.Components.Schemas { 85 if !ix.HasRef("#/components/schemas/" + key) { 86 remove = append(remove, key) 87 } 88 } 89 for i := range remove { 90 delete(t.Components.Schemas, remove[i]) 91 } 92 } 93 if t.Components.Parameters != nil { 94 var remove []string 95 for key := range t.Components.Parameters { 96 if !ix.HasRef("#/components/parameters/" + key) { 97 remove = append(remove, key) 98 } 99 } 100 for i := range remove { 101 delete(t.Components.Parameters, remove[i]) 102 } 103 } 104 if t.Components.Headers != nil { 105 var remove []string 106 for key := range t.Components.Headers { 107 if !ix.HasRef("#/components/headers/" + key) { 108 remove = append(remove, key) 109 } 110 } 111 for i := range remove { 112 delete(t.Components.Headers, remove[i]) 113 } 114 } 115 if t.Components.RequestBodies != nil { 116 var remove []string 117 for key := range t.Components.RequestBodies { 118 if !ix.HasRef("#/components/requestbodies/" + key) { 119 remove = append(remove, key) 120 } 121 } 122 for i := range remove { 123 delete(t.Components.RequestBodies, remove[i]) 124 } 125 } 126 if t.Components.Responses != nil { 127 var remove []string 128 for key := range t.Components.Responses { 129 if !ix.HasRef("#/components/responses/" + key) { 130 remove = append(remove, key) 131 } 132 } 133 for i := range remove { 134 delete(t.Components.Responses, remove[i]) 135 } 136 } 137 if t.Components.SecuritySchemes != nil { 138 var remove []string 139 for key := range t.Components.SecuritySchemes { 140 if !ix.HasRef("#/components/securityschemes/" + key) { 141 remove = append(remove, key) 142 } 143 } 144 for i := range remove { 145 delete(t.Components.SecuritySchemes, remove[i]) 146 } 147 } 148 if t.Components.Examples != nil { 149 var remove []string 150 for key := range t.Components.Examples { 151 if !ix.HasRef("#/components/examples/" + key) { 152 remove = append(remove, key) 153 } 154 } 155 for i := range remove { 156 delete(t.Components.Examples, remove[i]) 157 } 158 } 159 if t.Components.Links != nil { 160 var remove []string 161 for key := range t.Components.Links { 162 if !ix.HasRef("#/components/links/" + key) { 163 remove = append(remove, key) 164 } 165 } 166 for i := range remove { 167 delete(t.Components.Links, remove[i]) 168 } 169 } 170 if t.Components.Callbacks != nil { 171 var remove []string 172 for key := range t.Components.Callbacks { 173 if !ix.HasRef("#/components/callbacks/" + key) { 174 remove = append(remove, key) 175 } 176 } 177 for i := range remove { 178 delete(t.Components.Callbacks, remove[i]) 179 } 180 } 181 return nil 182 }