github.com/fastwego/offiaccount@v1.0.1/cmd/build.go (about)

     1  // Copyright 2020 FastWeGo
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"flag"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"net/url"
    22  	"os"
    23  	"path"
    24  	"regexp"
    25  	"strings"
    26  
    27  	"github.com/iancoleman/strcase"
    28  )
    29  
    30  func main() {
    31  	var pkgFlag string
    32  	flag.StringVar(&pkgFlag, "package", "default", "")
    33  	flag.Parse()
    34  	for _, group := range apiConfig {
    35  		if group.Package == "oauth" { // 单独处理 oauth 模块
    36  			continue
    37  		}
    38  
    39  		if group.Package == pkgFlag {
    40  			build(group)
    41  		}
    42  	}
    43  
    44  	if pkgFlag == "apilist" {
    45  		apilist()
    46  	}
    47  
    48  }
    49  
    50  func apilist() {
    51  	for _, group := range apiConfig {
    52  		fmt.Printf("- %s(%s)\n", group.Name, group.Package)
    53  		for _, api := range group.Apis {
    54  			split := strings.Split(api.Request, " ")
    55  			parse, _ := url.Parse(split[1])
    56  
    57  			if api.FuncName == "" {
    58  				api.FuncName = strcase.ToCamel(path.Base(parse.Path))
    59  			}
    60  
    61  			godocLink := fmt.Sprintf("https://pkg.go.dev/github.com/fastwego/offiaccount/apis/%s?tab=doc#%s", group.Package, api.FuncName)
    62  			fmt.Printf("\t- [%s](%s) \n\t\t- [%s (%s)](%s)\n", api.Name, api.See, api.FuncName, parse.Path, godocLink)
    63  		}
    64  	}
    65  }
    66  
    67  func build(group ApiGroup) {
    68  	var funcs []string
    69  	var consts []string
    70  	var testFuncs []string
    71  	var exampleFuncs []string
    72  
    73  	for _, api := range group.Apis {
    74  		tpl := postFuncTpl
    75  		_FUNC_NAME_ := ""
    76  		_GET_PARAMS_ := ""
    77  		_GET_SUFFIX_PARAMS_ := ""
    78  		_UPLOAD_ := "media"
    79  		_FIELD_NAME_ := ""
    80  		_FIELDS_ := ""
    81  		_PAYLOAD_ := ""
    82  		switch {
    83  		case strings.Contains(api.Request, "GET http"):
    84  			tpl = getFuncTpl
    85  		case strings.Contains(api.Request, "POST http"):
    86  			tpl = postFuncTpl
    87  		case strings.Contains(api.Request, "POST(@media"):
    88  			tpl = postUploadFuncTpl
    89  			_UPLOAD_ = "media"
    90  
    91  			pattern := `POST\(@media\|field=(\S+)\) http`
    92  			reg := regexp.MustCompile(pattern)
    93  			matched := reg.FindAllStringSubmatch(api.Request, -1)
    94  			if matched != nil {
    95  				_FIELD_NAME_ = matched[0][1]
    96  				_PAYLOAD_ = ", payload []byte"
    97  			}
    98  		}
    99  		if len(api.GetParams) > 0 {
   100  			_GET_PARAMS_ = `, params url.Values`
   101  			//if strings.Contains(api.Request, "POST") {
   102  			//	_GET_PARAMS_ = `, ` + _GET_PARAMS_
   103  			//}
   104  			_GET_SUFFIX_PARAMS_ = `+ "?" + params.Encode()`
   105  		}
   106  
   107  		split := strings.Split(api.Request, " ")
   108  		parseUrl, _ := url.Parse(split[1])
   109  
   110  		if api.FuncName == "" {
   111  			_FUNC_NAME_ = strcase.ToCamel(path.Base(parseUrl.Path))
   112  		} else {
   113  			_FUNC_NAME_ = api.FuncName
   114  		}
   115  
   116  		tpl = strings.ReplaceAll(tpl, "_TITLE_", api.Name)
   117  		tpl = strings.ReplaceAll(tpl, "_DESCRIPTION_", api.Description)
   118  		tpl = strings.ReplaceAll(tpl, "_REQUEST_", api.Request)
   119  		tpl = strings.ReplaceAll(tpl, "_SEE_", api.See)
   120  		tpl = strings.ReplaceAll(tpl, "_FUNC_NAME_", _FUNC_NAME_)
   121  		tpl = strings.ReplaceAll(tpl, "_UPLOAD_", _UPLOAD_)
   122  		tpl = strings.ReplaceAll(tpl, "_GET_PARAMS_", _GET_PARAMS_)
   123  		tpl = strings.ReplaceAll(tpl, "_GET_SUFFIX_PARAMS_", _GET_SUFFIX_PARAMS_)
   124  		if _FIELD_NAME_ != "" {
   125  			_FIELDS_ = strings.ReplaceAll(fieldTpl, "_FIELD_NAME_", _FIELD_NAME_)
   126  		}
   127  		tpl = strings.ReplaceAll(tpl, "_FIELDS_", _FIELDS_)
   128  		tpl = strings.ReplaceAll(tpl, "_PAYLOAD_", _PAYLOAD_)
   129  
   130  		funcs = append(funcs, tpl)
   131  
   132  		tpl = strings.ReplaceAll(constTpl, "_FUNC_NAME_", _FUNC_NAME_)
   133  		tpl = strings.ReplaceAll(tpl, "_API_PATH_", parseUrl.Path)
   134  
   135  		consts = append(consts, tpl)
   136  
   137  		// TestFunc
   138  		_TEST_ARGS_STRUCT_ := ""
   139  		switch {
   140  		case strings.Contains(api.Request, "GET http"):
   141  			_TEST_ARGS_STRUCT_ = `ctx *offiaccount.OffiAccount, ` + _GET_PARAMS_
   142  		case strings.Contains(api.Request, "POST http"):
   143  			_TEST_ARGS_STRUCT_ = `ctx *offiaccount.OffiAccount, payload []byte`
   144  			if _GET_PARAMS_ != "" {
   145  				_TEST_ARGS_STRUCT_ += `,` + _GET_PARAMS_
   146  			}
   147  		case strings.Contains(api.Request, "POST(@media"):
   148  			_TEST_ARGS_STRUCT_ = `ctx *offiaccount.OffiAccount, ` + _UPLOAD_ + ` string` + _PAYLOAD_ + _GET_PARAMS_
   149  		}
   150  		_TEST_ARGS_STRUCT_ = strings.ReplaceAll(_TEST_ARGS_STRUCT_, ",", "\n")
   151  
   152  		_TEST_FUNC_SIGNATURE_ := ""
   153  		_EXAMPLE_ARGS_STMT_ := ""
   154  		if strings.TrimSpace(_TEST_ARGS_STRUCT_) != "" {
   155  			signatures := strings.Split(_TEST_ARGS_STRUCT_, "\n")
   156  			paramNames := []string{}
   157  			exampleStmt := []string{}
   158  			for _, signature := range signatures {
   159  				signature = strings.TrimSpace(signature)
   160  				tmp := strings.Split(signature, " ")
   161  				//fmt.Println(tmp)
   162  				if len(tmp[0]) > 0 {
   163  					paramNames = append(paramNames, "tt.args."+tmp[0])
   164  
   165  					switch tmp[1] {
   166  					case `[]byte`:
   167  						exampleStmt = append(exampleStmt, tmp[0]+" := []byte(\"{}\")")
   168  					case `string`:
   169  						exampleStmt = append(exampleStmt, tmp[0]+" := \"\"")
   170  					case `url.Values`:
   171  						exampleStmt = append(exampleStmt, tmp[0]+" := url.Values{}")
   172  					}
   173  				}
   174  			}
   175  			_TEST_FUNC_SIGNATURE_ = strings.Join(paramNames, ",")
   176  			_EXAMPLE_ARGS_STMT_ = strings.Join(exampleStmt, "\n")
   177  		}
   178  
   179  		tpl = strings.ReplaceAll(testFuncTpl, "_FUNC_NAME_", _FUNC_NAME_)
   180  		tpl = strings.ReplaceAll(tpl, "_TEST_ARGS_STRUCT_", _TEST_ARGS_STRUCT_)
   181  		tpl = strings.ReplaceAll(tpl, "_TEST_FUNC_SIGNATURE_", _TEST_FUNC_SIGNATURE_)
   182  		testFuncs = append(testFuncs, tpl)
   183  
   184  		//Example
   185  		tpl = strings.ReplaceAll(exampleFuncTpl, "_FUNC_NAME_", _FUNC_NAME_)
   186  		tpl = strings.ReplaceAll(tpl, "_PACKAGE_", path.Base(group.Package))
   187  		tpl = strings.ReplaceAll(tpl, "_TEST_FUNC_SIGNATURE_", strings.ReplaceAll(_TEST_FUNC_SIGNATURE_, "tt.args.", ""))
   188  		tpl = strings.ReplaceAll(tpl, "_EXAMPLE_ARGS_STMT_", _EXAMPLE_ARGS_STMT_)
   189  		exampleFuncs = append(exampleFuncs, tpl)
   190  
   191  	}
   192  
   193  	fileContent := fmt.Sprintf(fileTpl, path.Base(group.Package), group.Name, path.Base(group.Package), strings.Join(consts, ``), strings.Join(funcs, ``))
   194  	filename := "./../apis/" + group.Package + "/" + path.Base(group.Package) + ".go"
   195  	_ = os.MkdirAll(path.Dir(filename), 0644)
   196  	err := ioutil.WriteFile(filename, []byte(fileContent), 0644)
   197  	if err != nil {
   198  		fmt.Println(err.Error())
   199  		return
   200  	}
   201  
   202  	// output Test
   203  	testFileContent := fmt.Sprintf(testFileTpl, path.Base(group.Package), strings.Join(testFuncs, ``))
   204  	//fmt.Println(testFileContent)
   205  	err = ioutil.WriteFile("./../apis/"+group.Package+"/"+path.Base(group.Package)+"_test.go", []byte(testFileContent), 0644)
   206  	if err != nil {
   207  		fmt.Println(err.Error())
   208  		return
   209  	}
   210  	// output example
   211  	exampleFileContent := fmt.Sprintf(exampleFileTpl, path.Base(group.Package), strings.Join(exampleFuncs, ``))
   212  	//fmt.Println(testFileContent)
   213  	err = ioutil.WriteFile("./../apis/"+group.Package+"/example_"+path.Base(group.Package)+"_test.go", []byte(exampleFileContent), 0644)
   214  	if err != nil {
   215  		fmt.Println(err.Error())
   216  		return
   217  	}
   218  }
   219  
   220  var constTpl = `
   221  	api_FUNC_NAME_ = "_API_PATH_"`
   222  var commentTpl = `
   223  /*
   224  _TITLE_
   225  
   226  _DESCRIPTION_
   227  
   228  See: _SEE_
   229  
   230  _REQUEST_
   231  */`
   232  var postFuncTpl = commentTpl + `
   233  func _FUNC_NAME_(ctx *offiaccount.OffiAccount, payload []byte_GET_PARAMS_) (resp []byte, err error) {
   234  	return ctx.Client.HTTPPost(api_FUNC_NAME__GET_SUFFIX_PARAMS_, bytes.NewReader(payload), "application/json;charset=utf-8")
   235  }
   236  `
   237  var getFuncTpl = commentTpl + `
   238  func _FUNC_NAME_(ctx *offiaccount.OffiAccount_GET_PARAMS_) (resp []byte, err error) {
   239  	return ctx.Client.HTTPGet(api_FUNC_NAME__GET_SUFFIX_PARAMS_)
   240  }
   241  `
   242  var postUploadFuncTpl = commentTpl + `
   243  func _FUNC_NAME_(ctx *offiaccount.OffiAccount, _UPLOAD_ string_PAYLOAD__GET_PARAMS_) (resp []byte, err error) {
   244  	r, w := io.Pipe()
   245  	m := multipart.NewWriter(w)
   246  	go func() {
   247  		defer w.Close()
   248  		defer m.Close()
   249  
   250  		part, err := m.CreateFormFile("_UPLOAD_", path.Base(_UPLOAD_))
   251  		if err != nil {
   252  			return
   253  		}
   254  		file, err := os.Open(_UPLOAD_)
   255  		if err != nil {
   256  			return
   257  		}
   258  		defer file.Close()
   259  		if _, err = io.Copy(part, file); err != nil {
   260  			return
   261  		}
   262  
   263  		_FIELDS_
   264  	}()
   265  	return ctx.Client.HTTPPost(api_FUNC_NAME__GET_SUFFIX_PARAMS_, r, m.FormDataContentType())
   266  }
   267  `
   268  
   269  var fieldTpl = `
   270  		// field
   271  		err = m.WriteField("_FIELD_NAME_", string(payload))
   272  		if err != nil {
   273  			return
   274  		}
   275  `
   276  
   277  var fileTpl = `// Package %s %s
   278  package %s
   279  
   280  const (
   281  	%s
   282  )
   283  %s`
   284  
   285  var testFileTpl = `package %s
   286  
   287  func TestMain(m *testing.M) {
   288  	test.Setup()
   289  	os.Exit(m.Run())
   290  }
   291  
   292  %s
   293  `
   294  
   295  var testFuncTpl = `
   296  func Test_FUNC_NAME_(t *testing.T) {
   297  	mockResp := map[string][]byte{
   298  		"case1": []byte("{\"errcode\":0,\"errmsg\":\"ok\"}"),
   299  	}
   300  	var resp []byte
   301  	test.MockSvrHandler.HandleFunc(api_FUNC_NAME_, func(w http.ResponseWriter, r *http.Request) {
   302  		w.Write([]byte(resp))
   303  	})
   304  
   305  	type args struct {
   306  		_TEST_ARGS_STRUCT_
   307  	}
   308  	tests := []struct {
   309  		name     string
   310  		args     args
   311  		wantResp []byte
   312  		wantErr  bool
   313  	}{
   314  		{name: "case1", args: args{ctx: test.MockOffiAccount}, wantResp: mockResp["case1"], wantErr: false},
   315  	}
   316  	for _, tt := range tests {
   317  		t.Run(tt.name, func(t *testing.T) {
   318  			resp = mockResp[tt.name]
   319  			gotResp, err := _FUNC_NAME_(_TEST_FUNC_SIGNATURE_)
   320  			//fmt.Println(string(gotResp), err)
   321  			if (err != nil) != tt.wantErr {
   322  				t.Errorf("_FUNC_NAME_() error = %v, wantErr %v", err, tt.wantErr)
   323  				return
   324  			}
   325  			if !reflect.DeepEqual(gotResp, tt.wantResp) {
   326  				t.Errorf("_FUNC_NAME_() gotResp = %v, want %v", gotResp, tt.wantResp)
   327  			}
   328  		})
   329  	}
   330  }`
   331  
   332  var exampleFileTpl = `package %s_test
   333  
   334  %s
   335  `
   336  var exampleFuncTpl = `
   337  func Example_FUNC_NAME_() {
   338  	var ctx *offiaccount.OffiAccount
   339  
   340  	_EXAMPLE_ARGS_STMT_
   341  	resp, err := _PACKAGE_._FUNC_NAME_(_TEST_FUNC_SIGNATURE_)
   342  
   343  	fmt.Println(resp, err)
   344  }
   345  
   346  `