|  | 
|  | 1 | +package cmd | 
|  | 2 | + | 
|  | 3 | +import ( | 
|  | 4 | +	"context" | 
|  | 5 | +	"fmt" | 
|  | 6 | +	"io" | 
|  | 7 | +	"net/http" | 
|  | 8 | +	"os" | 
|  | 9 | +	"os/signal" | 
|  | 10 | +	"strings" | 
|  | 11 | +	"syscall" | 
|  | 12 | + | 
|  | 13 | +	"github.com/elazarl/goproxy" | 
|  | 14 | +	"github.com/linuxsuren/api-testing/extensions/collector/pkg" | 
|  | 15 | +	"github.com/linuxsuren/api-testing/extensions/collector/pkg/filter" | 
|  | 16 | +	atestpkg "github.com/linuxsuren/api-testing/pkg/testing" | 
|  | 17 | +	"github.com/spf13/cobra" | 
|  | 18 | +	"gopkg.in/yaml.v2" | 
|  | 19 | +) | 
|  | 20 | + | 
|  | 21 | +type option struct { | 
|  | 22 | +	port       int | 
|  | 23 | +	filterPath string | 
|  | 24 | +	output     string | 
|  | 25 | +} | 
|  | 26 | + | 
|  | 27 | +func NewRootCmd() (c *cobra.Command) { | 
|  | 28 | +	opt := &option{} | 
|  | 29 | +	c = &cobra.Command{ | 
|  | 30 | +		Use:   "atest-collector", | 
|  | 31 | +		Short: "A collector for API testing, it will start a HTTP proxy server", | 
|  | 32 | +		RunE:  opt.runE, | 
|  | 33 | +	} | 
|  | 34 | +	flags := c.Flags() | 
|  | 35 | +	flags.IntVarP(&opt.port, "port", "p", 8080, "The port for the proxy") | 
|  | 36 | +	flags.StringVarP(&opt.filterPath, "filter-path", "", "", "The path prefix for filtering") | 
|  | 37 | +	flags.StringVarP(&opt.output, "output", "o", "sample.yaml", "The output file") | 
|  | 38 | + | 
|  | 39 | +	cobra.MarkFlagRequired(flags, "filter-path") | 
|  | 40 | +	return | 
|  | 41 | +} | 
|  | 42 | + | 
|  | 43 | +func (o *option) runE(cmd *cobra.Command, args []string) (err error) { | 
|  | 44 | +	urlFilter := &filter.URLPathFilter{PathPrefix: o.filterPath} | 
|  | 45 | +	collects := pkg.NewCollects() | 
|  | 46 | + | 
|  | 47 | +	proxy := goproxy.NewProxyHttpServer() | 
|  | 48 | +	proxy.Verbose = true | 
|  | 49 | +	proxy.OnRequest().DoFunc( | 
|  | 50 | +		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { | 
|  | 51 | +			if urlFilter.Filter(r.URL) { | 
|  | 52 | +				collects.Add(r.Clone(context.TODO())) | 
|  | 53 | +			} | 
|  | 54 | +			return r, nil | 
|  | 55 | +		}) | 
|  | 56 | + | 
|  | 57 | +	exporter := &sampleExporter{ | 
|  | 58 | +		testSuite: atestpkg.TestSuite{ | 
|  | 59 | +			Name: "sample", | 
|  | 60 | +		}, | 
|  | 61 | +	} | 
|  | 62 | +	collects.AddEvent(exporter.add) | 
|  | 63 | + | 
|  | 64 | +	srv := &http.Server{ | 
|  | 65 | +		Addr:    fmt.Sprintf(":%d", o.port), | 
|  | 66 | +		Handler: proxy, | 
|  | 67 | +	} | 
|  | 68 | + | 
|  | 69 | +	sig := make(chan os.Signal, 1) | 
|  | 70 | +	signal.Notify(sig, os.Interrupt, syscall.SIGTERM) | 
|  | 71 | +	go func() { | 
|  | 72 | +		<-sig | 
|  | 73 | +		collects.Stop() | 
|  | 74 | +		srv.Shutdown(context.Background()) | 
|  | 75 | +	}() | 
|  | 76 | + | 
|  | 77 | +	cmd.Println("Starting the proxy server with port", o.port) | 
|  | 78 | +	srv.ListenAndServe() | 
|  | 79 | +	var data string | 
|  | 80 | +	if data, err = exporter.export(); err == nil { | 
|  | 81 | +		err = os.WriteFile(o.output, []byte(data), 0644) | 
|  | 82 | +	} | 
|  | 83 | +	return | 
|  | 84 | +} | 
|  | 85 | + | 
|  | 86 | +type sampleExporter struct { | 
|  | 87 | +	testSuite atestpkg.TestSuite | 
|  | 88 | +} | 
|  | 89 | + | 
|  | 90 | +func (e *sampleExporter) add(r *http.Request) { | 
|  | 91 | +	body := r.Body | 
|  | 92 | +	data, _ := io.ReadAll(body) | 
|  | 93 | + | 
|  | 94 | +	fmt.Println("receive", r.URL.Path) | 
|  | 95 | +	req := atestpkg.Request{ | 
|  | 96 | +		API:    r.URL.String(), | 
|  | 97 | +		Method: r.Method, | 
|  | 98 | +		Header: map[string]string{}, | 
|  | 99 | +		Body:   string(data), | 
|  | 100 | +	} | 
|  | 101 | + | 
|  | 102 | +	testCase := atestpkg.TestCase{ | 
|  | 103 | +		Request: req, | 
|  | 104 | +		Expect: atestpkg.Response{ | 
|  | 105 | +			StatusCode: 200, | 
|  | 106 | +		}, | 
|  | 107 | +	} | 
|  | 108 | + | 
|  | 109 | +	specs := strings.Split(r.URL.Path, "/") | 
|  | 110 | +	if len(specs) > 0 { | 
|  | 111 | +		testCase.Name = specs[len(specs)-1] | 
|  | 112 | +	} | 
|  | 113 | + | 
|  | 114 | +	if val := r.Header.Get("Content-Type"); val != "" { | 
|  | 115 | +		req.Header["Content-Type"] = val | 
|  | 116 | +	} | 
|  | 117 | + | 
|  | 118 | +	e.testSuite.Items = append(e.testSuite.Items, testCase) | 
|  | 119 | +} | 
|  | 120 | + | 
|  | 121 | +var prefix = `#!api-testing | 
|  | 122 | +# yaml-language-server: $schema=https://gitee.com/linuxsuren/api-testing/raw/master/sample/api-testing-schema.json | 
|  | 123 | +` | 
|  | 124 | + | 
|  | 125 | +func (e *sampleExporter) export() (string, error) { | 
|  | 126 | +	marker := map[string]int{} | 
|  | 127 | + | 
|  | 128 | +	for i, item := range e.testSuite.Items { | 
|  | 129 | +		if _, ok := marker[item.Name]; ok { | 
|  | 130 | +			marker[item.Name]++ | 
|  | 131 | +			e.testSuite.Items[i].Name = fmt.Sprintf("%s-%d", item.Name, marker[item.Name]) | 
|  | 132 | +		} else { | 
|  | 133 | +			marker[item.Name] = 0 | 
|  | 134 | +		} | 
|  | 135 | +	} | 
|  | 136 | + | 
|  | 137 | +	data, err := yaml.Marshal(e.testSuite) | 
|  | 138 | +	return prefix + string(data), err | 
|  | 139 | +} | 
0 commit comments