chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:33 +08:00
commit e071084ebe
982 changed files with 160368 additions and 0 deletions
+447
View File
@@ -0,0 +1,447 @@
package util
import (
"bytes"
"context"
"encoding/json"
"fmt"
"math"
"os"
"sort"
"strconv"
"strings"
"unicode"
"github.com/stretchr/objx"
"github.com/urfave/cli/v2"
"go-micro.dev/v6/client"
"go-micro.dev/v6/metadata"
"go-micro.dev/v6/registry"
)
// AddMetadataToContext parses metadata strings in the format "Key:Value" and adds them to the context
func AddMetadataToContext(ctx context.Context, metadataStrings []string) context.Context {
if len(metadataStrings) == 0 {
return ctx
}
md := make(metadata.Metadata)
for _, m := range metadataStrings {
parts := strings.SplitN(m, ":", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
md[key] = value
}
return metadata.MergeContext(ctx, md, true)
}
// LookupService queries the service for a service with the given alias. If
// no services are found for a given alias, the registry will return nil and
// the error will also be nil. An error is only returned if there was an issue
// listing from the registry.
func LookupService(name string) (*registry.Service, error) {
// return a lookup in the default domain as a catch all
return serviceWithName(name)
}
// FormatServiceUsage returns a string containing the service usage.
func FormatServiceUsage(srv *registry.Service, c *cli.Context) string {
alias := c.Args().First()
subcommand := c.Args().Get(1)
commands := make([]string, len(srv.Endpoints))
endpoints := make([]*registry.Endpoint, len(srv.Endpoints))
for i, e := range srv.Endpoints {
// map "Helloworld.Call" to "helloworld.call"
parts := strings.Split(e.Name, ".")
for i, part := range parts {
parts[i] = lowercaseInitial(part)
}
name := strings.Join(parts, ".")
// remove the prefix if it is the service name, e.g. rather than
// "micro run helloworld helloworld call", it would be
// "micro run helloworld call".
name = strings.TrimPrefix(name, alias+".")
// instead of "micro run helloworld foo.bar", the command should
// be "micro run helloworld foo bar".
commands[i] = strings.Replace(name, ".", " ", 1)
endpoints[i] = e
}
result := ""
if len(subcommand) > 0 && subcommand != "--help" {
result += fmt.Sprintf("NAME:\n\tmicro %v %v\n\n", alias, subcommand)
result += fmt.Sprintf("USAGE:\n\tmicro %v %v [flags]\n\n", alias, subcommand)
result += "FLAGS:\n"
for i, command := range commands {
if command == subcommand {
result += renderFlags(endpoints[i])
}
}
} else {
// sort the command names alphabetically
sort.Strings(commands)
result += fmt.Sprintf("NAME:\n\tmicro %v\n\n", alias)
result += fmt.Sprintf("VERSION:\n\t%v\n\n", srv.Version)
result += fmt.Sprintf("USAGE:\n\tmicro %v [command]\n\n", alias)
result += fmt.Sprintf("COMMANDS:\n\t%v\n", strings.Join(commands, "\n\t"))
}
return result
}
func lowercaseInitial(str string) string {
for i, v := range str {
return string(unicode.ToLower(v)) + str[i+1:]
}
return ""
}
func renderFlags(endpoint *registry.Endpoint) string {
ret := ""
for _, value := range endpoint.Request.Values {
ret += renderValue([]string{}, value) + "\n"
}
return ret
}
func renderValue(path []string, value *registry.Value) string {
if len(value.Values) > 0 {
renders := []string{}
for _, v := range value.Values {
renders = append(renders, renderValue(append(path, value.Name), v))
}
return strings.Join(renders, "\n")
}
return fmt.Sprintf("\t--%v %v", strings.Join(append(path, value.Name), "_"), value.Type)
}
// CallService will call a service using the arguments and flags provided
// in the context. It will print the result or error to stdout. If there
// was an error performing the call, it will be returned.
func CallService(srv *registry.Service, args []string) error {
// parse the flags and args
args, flags, err := splitCmdArgs(args)
if err != nil {
return err
}
// construct the endpoint
endpoint, err := constructEndpoint(args)
if err != nil {
return err
}
// ensure the endpoint exists on the service
var ep *registry.Endpoint
for _, e := range srv.Endpoints {
if e.Name == endpoint {
ep = e
break
}
}
if ep == nil {
return fmt.Errorf("endpoint %v not found for service %v", endpoint, srv.Name)
}
// create a context for the call
callCtx := context.TODO()
// parse out --header or --metadata flags before parsing request body
// Note: This is for dynamic service calls (e.g., 'micro helloworld call --header X:Y').
// Direct 'micro call' commands are handled in cli.go.
if headerFlags, ok := flags["header"]; ok {
callCtx = AddMetadataToContext(callCtx, headerFlags)
delete(flags, "header")
}
if metadataFlags, ok := flags["metadata"]; ok {
callCtx = AddMetadataToContext(callCtx, metadataFlags)
delete(flags, "metadata")
}
// parse the flags into request body
body, err := FlagsToRequest(flags, ep.Request)
if err != nil {
return err
}
// construct and execute the request using the json content type
req := client.DefaultClient.NewRequest(srv.Name, endpoint, body, client.WithContentType("application/json"))
var rsp json.RawMessage
if err := client.DefaultClient.Call(callCtx, req, &rsp); err != nil {
return err
}
// format the response
var out bytes.Buffer
defer out.Reset()
if err := json.Indent(&out, rsp, "", "\t"); err != nil {
return err
}
out.Write([]byte("\n"))
_, _ = out.WriteTo(os.Stdout)
return nil
}
// splitCmdArgs takes a cli context and parses out the args and flags, for
// example "micro helloworld --name=foo call apple" would result in "call",
// "apple" as args and {"name":"foo"} as the flags.
func splitCmdArgs(arguments []string) ([]string, map[string][]string, error) {
args := []string{}
flags := map[string][]string{}
prev := ""
for _, a := range arguments {
if !strings.HasPrefix(a, "--") {
if len(prev) == 0 {
args = append(args, a)
continue
}
_, exists := flags[prev]
if !exists {
flags[prev] = []string{}
}
flags[prev] = append(flags[prev], a)
prev = ""
continue
}
// comps would be "foo", "bar" for "--foo=bar"
comps := strings.Split(strings.TrimPrefix(a, "--"), "=")
_, exists := flags[comps[0]]
if !exists {
flags[comps[0]] = []string{}
}
switch len(comps) {
case 1:
prev = comps[0]
case 2:
flags[comps[0]] = append(flags[comps[0]], comps[1])
default:
return nil, nil, fmt.Errorf("invalid flag: %v. Expected format: --foo=bar", a)
}
}
return args, flags, nil
}
// constructEndpoint takes a slice of args and converts it into a valid endpoint
// such as Helloworld.Call or Foo.Bar, it will return an error if an invalid number
// of arguments were provided
func constructEndpoint(args []string) (string, error) {
var epComps []string
switch len(args) {
case 1:
epComps = append(args, "call")
case 2:
epComps = args
case 3:
epComps = args[1:3]
default:
return "", fmt.Errorf("incorrect number of arguments")
}
// transform the endpoint components, e.g ["helloworld", "call"] to the
// endpoint name: "Helloworld.Call".
return fmt.Sprintf("%v.%v", strings.Title(epComps[0]), strings.Title(epComps[1])), nil
}
// ShouldRenderHelp returns true if the help flag was passed
func ShouldRenderHelp(args []string) bool {
args, flags, _ := splitCmdArgs(args)
// only 1 arg e.g micro helloworld
if len(args) == 1 {
return true
}
for key := range flags {
if key == "help" {
return true
}
}
return false
}
// FlagsToRequest parses a set of flags, e.g {name:"Foo", "options_surname","Bar"} and
// converts it into a request body. If the key is not a valid object in the request, an
// error will be returned.
//
// This function constructs []interface{} slices
// as opposed to typed ([]string etc) slices for easier testing
func FlagsToRequest(flags map[string][]string, req *registry.Value) (map[string]interface{}, error) {
coerceValue := func(valueType string, value []string) (interface{}, error) {
switch valueType {
case "bool":
if len(value) == 0 || len(strings.TrimSpace(value[0])) == 0 {
return true, nil
}
return strconv.ParseBool(value[0])
case "int32":
i, err := strconv.Atoi(value[0])
if err != nil {
return nil, err
}
if i < math.MinInt32 || i > math.MaxInt32 {
return nil, fmt.Errorf("value out of range for int32: %d", i)
}
return int32(i), nil
case "int64":
return strconv.ParseInt(value[0], 0, 64)
case "float64":
return strconv.ParseFloat(value[0], 64)
case "[]bool":
// length is one if it's a `,` separated int slice
if len(value) == 1 {
value = strings.Split(value[0], ",")
}
ret := []interface{}{}
for _, v := range value {
i, err := strconv.ParseBool(v)
if err != nil {
return nil, err
}
ret = append(ret, i)
}
return ret, nil
case "[]int32":
// length is one if it's a `,` separated int slice
if len(value) == 1 {
value = strings.Split(value[0], ",")
}
ret := []interface{}{}
for _, v := range value {
i, err := strconv.Atoi(v)
if err != nil {
return nil, err
}
if i < math.MinInt32 || i > math.MaxInt32 {
return nil, fmt.Errorf("value out of range for int32: %d", i)
}
ret = append(ret, int32(i))
}
return ret, nil
case "[]int64":
// length is one if it's a `,` separated int slice
if len(value) == 1 {
value = strings.Split(value[0], ",")
}
ret := []interface{}{}
for _, v := range value {
i, err := strconv.ParseInt(v, 0, 64)
if err != nil {
return nil, err
}
ret = append(ret, i)
}
return ret, nil
case "[]float64":
// length is one if it's a `,` separated float slice
if len(value) == 1 {
value = strings.Split(value[0], ",")
}
ret := []interface{}{}
for _, v := range value {
i, err := strconv.ParseFloat(v, 64)
if err != nil {
return nil, err
}
ret = append(ret, i)
}
return ret, nil
case "[]string":
// length is one it's a `,` separated string slice
if len(value) == 1 {
value = strings.Split(value[0], ",")
}
ret := []interface{}{}
for _, v := range value {
ret = append(ret, v)
}
return ret, nil
case "string":
return value[0], nil
case "map[string]string":
var val map[string]string
if err := json.Unmarshal([]byte(value[0]), &val); err != nil {
return value[0], nil
}
return val, nil
default:
return value, nil
}
}
result := objx.MustFromJSON("{}")
var flagType func(key string, values []*registry.Value, path ...string) (string, bool)
flagType = func(key string, values []*registry.Value, path ...string) (string, bool) {
for _, attr := range values {
if strings.Join(append(path, attr.Name), "-") == key {
return attr.Type, true
}
if attr.Values != nil {
typ, found := flagType(key, attr.Values, append(path, attr.Name)...)
if found {
return typ, found
}
}
}
return "", false
}
for key, value := range flags {
ty, found := flagType(key, req.Values)
if !found {
return nil, fmt.Errorf("unknown flag: %v", key)
}
parsed, err := coerceValue(ty, value)
if err != nil {
return nil, err
}
// objx.Set does not create the path,
// so we do that here
if strings.Contains(key, "-") {
parts := strings.Split(key, "-")
for i := range parts {
pToCreate := strings.Join(parts[0:i], ".")
if i > 0 && i < len(parts) && !result.Has(pToCreate) {
result.Set(pToCreate, map[string]interface{}{})
}
}
}
path := strings.ReplaceAll(key, "-", ".")
result.Set(path, parsed)
}
return result, nil
}
// find a service in a domain matching the name
func serviceWithName(name string) (*registry.Service, error) {
srvs, err := registry.GetService(name)
if err == registry.ErrNotFound {
return nil, nil
} else if err != nil {
return nil, err
}
if len(srvs) == 0 {
return nil, nil
}
return srvs[0], nil
}
+453
View File
@@ -0,0 +1,453 @@
package util
import (
"context"
"reflect"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
"go-micro.dev/v6/metadata"
goregistry "go-micro.dev/v6/registry"
)
type parseCase struct {
args []string
values *goregistry.Value
expected map[string]interface{}
}
func TestDynamicFlagParsing(t *testing.T) {
cases := []parseCase{
{
args: []string{"--ss=a,b"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "ss",
Type: "[]string",
},
},
},
expected: map[string]interface{}{
"ss": []interface{}{"a", "b"},
},
},
{
args: []string{"--ss", "a,b"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "ss",
Type: "[]string",
},
},
},
expected: map[string]interface{}{
"ss": []interface{}{"a", "b"},
},
},
{
args: []string{"--ss=a", "--ss=b"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "ss",
Type: "[]string",
},
},
},
expected: map[string]interface{}{
"ss": []interface{}{"a", "b"},
},
},
{
args: []string{"--ss", "a", "--ss", "b"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "ss",
Type: "[]string",
},
},
},
expected: map[string]interface{}{
"ss": []interface{}{"a", "b"},
},
},
{
args: []string{"--bs=true,false"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "bs",
Type: "[]bool",
},
},
},
expected: map[string]interface{}{
"bs": []interface{}{true, false},
},
},
{
args: []string{"--bs", "true,false"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "bs",
Type: "[]bool",
},
},
},
expected: map[string]interface{}{
"bs": []interface{}{true, false},
},
},
{
args: []string{"--bs=true", "--bs=false"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "bs",
Type: "[]bool",
},
},
},
expected: map[string]interface{}{
"bs": []interface{}{true, false},
},
},
{
args: []string{"--bs", "true", "--bs", "false"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "bs",
Type: "[]bool",
},
},
},
expected: map[string]interface{}{
"bs": []interface{}{true, false},
},
},
{
args: []string{"--is=10,20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int32",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int32(10), int32(20)},
},
},
{
args: []string{"--is", "10,20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int32",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int32(10), int32(20)},
},
},
{
args: []string{"--is=10", "--is=20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int32",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int32(10), int32(20)},
},
},
{
args: []string{"--is", "10", "--is", "20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int32",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int32(10), int32(20)},
},
},
{
args: []string{"--is=10,20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int64",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int64(10), int64(20)},
},
},
{
args: []string{"--is", "10,20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int64",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int64(10), int64(20)},
},
},
{
args: []string{"--is=10", "--is=20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int64",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int64(10), int64(20)},
},
},
{
args: []string{"--is", "10", "--is", "20"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "is",
Type: "[]int64",
},
},
},
expected: map[string]interface{}{
"is": []interface{}{int64(10), int64(20)},
},
},
{
args: []string{"--fs=10.1,20.2"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "fs",
Type: "[]float64",
},
},
},
expected: map[string]interface{}{
"fs": []interface{}{float64(10.1), float64(20.2)},
},
},
{
args: []string{"--fs", "10.1,20.2"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "fs",
Type: "[]float64",
},
},
},
expected: map[string]interface{}{
"fs": []interface{}{float64(10.1), float64(20.2)},
},
},
{
args: []string{"--fs=10.1", "--fs=20.2"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "fs",
Type: "[]float64",
},
},
},
expected: map[string]interface{}{
"fs": []interface{}{float64(10.1), float64(20.2)},
},
},
{
args: []string{"--fs", "10.1", "--fs", "20.2"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "fs",
Type: "[]float64",
},
},
},
expected: map[string]interface{}{
"fs": []interface{}{float64(10.1), float64(20.2)},
},
},
{
args: []string{"--user_email=someemail"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "user_email",
Type: "string",
},
},
},
expected: map[string]interface{}{
"user_email": "someemail",
},
},
{
args: []string{"--user_email=someemail", "--user_name=somename"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "user_email",
Type: "string",
},
{
Name: "user_name",
Type: "string",
},
},
},
expected: map[string]interface{}{
"user_email": "someemail",
"user_name": "somename",
},
},
{
args: []string{"--b"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "b",
Type: "bool",
},
},
},
expected: map[string]interface{}{
"b": true,
},
},
{
args: []string{"--user_friend_email=hi"},
values: &goregistry.Value{
Values: []*goregistry.Value{
{
Name: "user_friend_email",
Type: "string",
},
},
},
expected: map[string]interface{}{
"user_friend_email": "hi",
},
},
}
for _, c := range cases {
t.Run(strings.Join(c.args, " "), func(t *testing.T) {
_, flags, err := splitCmdArgs(c.args)
if err != nil {
t.Fatal(err)
}
req, err := FlagsToRequest(flags, c.values)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(c.expected, req) {
spew.Dump("Expected:", c.expected, "got: ", req)
t.Fatalf("Expected %v, got %v", c.expected, req)
}
})
}
}
func TestAddMetadataToContext(t *testing.T) {
tests := []struct {
name string
metadataStrs []string
expectedKeys []string
expectedValues []string
}{
{
name: "Single metadata",
metadataStrs: []string{"Key1:Value1"},
expectedKeys: []string{"Key1"},
expectedValues: []string{"Value1"},
},
{
name: "Multiple metadata",
metadataStrs: []string{"Key1:Value1", "Key2:Value2"},
expectedKeys: []string{"Key1", "Key2"},
expectedValues: []string{"Value1", "Value2"},
},
{
name: "Metadata with spaces",
metadataStrs: []string{"Key1: Value1 ", " Key2 : Value2"},
expectedKeys: []string{"Key1", "Key2"},
expectedValues: []string{"Value1", "Value2"},
},
{
name: "Metadata with colon in value",
metadataStrs: []string{"Authorization:Bearer token:123"},
expectedKeys: []string{"Authorization"},
expectedValues: []string{"Bearer token:123"},
},
{
name: "Empty metadata",
metadataStrs: []string{},
expectedKeys: []string{},
expectedValues: []string{},
},
{
name: "Invalid metadata format",
metadataStrs: []string{"InvalidFormat"},
expectedKeys: []string{},
expectedValues: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
ctx = AddMetadataToContext(ctx, tt.metadataStrs)
md, ok := metadata.FromContext(ctx)
if len(tt.expectedKeys) == 0 && !ok {
return // Expected no metadata
}
if !ok && len(tt.expectedKeys) > 0 {
t.Fatal("Expected metadata in context but got none")
}
for i, key := range tt.expectedKeys {
value, found := md.Get(key)
if !found {
t.Fatalf("Expected key %s not found in metadata", key)
}
if value != tt.expectedValues[i] {
t.Fatalf("Expected value %s for key %s, got %s", tt.expectedValues[i], key, value)
}
}
})
}
}
+72
View File
@@ -0,0 +1,72 @@
// Package cliutil contains methods used across all cli commands
// @todo: get rid of os.Exits and use errors instread
package util
import (
"fmt"
"regexp"
"strings"
"github.com/urfave/cli/v2"
merrors "go-micro.dev/v6/errors"
)
type Exec func(*cli.Context, []string) ([]byte, error)
func Print(e Exec) func(*cli.Context) error {
return func(c *cli.Context) error {
rsp, err := e(c, c.Args().Slice())
if err != nil {
return CliError(err)
}
if len(rsp) > 0 {
fmt.Printf("%s\n", string(rsp))
}
return nil
}
}
// CliError returns a user friendly message from error. If we can't determine a good one returns an error with code 128
func CliError(err error) cli.ExitCoder {
if err == nil {
return nil
}
// if it's already a cli.ExitCoder we use this
cerr, ok := err.(cli.ExitCoder)
if ok {
return cerr
}
// grpc errors
if mname := regexp.MustCompile(`malformed method name: \\?"(\w+)\\?"`).FindStringSubmatch(err.Error()); len(mname) > 0 {
return cli.Exit(fmt.Sprintf(`Method name "%s" invalid format. Expecting service.endpoint`, mname[1]), 3)
}
if service := regexp.MustCompile(`service ([\w\.]+): route not found`).FindStringSubmatch(err.Error()); len(service) > 0 {
return cli.Exit(fmt.Sprintf(`Service "%s" not found`, service[1]), 4)
}
if service := regexp.MustCompile(`unknown service ([\w\.]+)`).FindStringSubmatch(err.Error()); len(service) > 0 {
if strings.Contains(service[0], ".") {
return cli.Exit(fmt.Sprintf(`Service method "%s" not found`, service[1]), 5)
}
return cli.Exit(fmt.Sprintf(`Service "%s" not found`, service[1]), 5)
}
if address := regexp.MustCompile(`Error while dialing dial tcp.*?([\w]+\.[\w:\.]+): `).FindStringSubmatch(err.Error()); len(address) > 0 {
return cli.Exit(fmt.Sprintf(`Failed to connect to micro server at %s`, address[1]), 4)
}
merr, ok := err.(*merrors.Error)
if !ok {
return cli.Exit(err, 128)
}
switch merr.Code {
case 408:
return cli.Exit("Request timed out", 1)
case 401:
// TODO check if not signed in, prompt to sign in
return cli.Exit("Not authorized to perform this request", 2)
}
// fallback to using the detail from the merr
return cli.Exit(merr.Detail, 127)
}