Repository: goadesign/goa
Pull request #3523 (2024-05-28 14:03:20)

Support nested DSLs by eval.caller()
Related to
* https://github.com/goadesign/goa/pull/3502
* https://github.com/goadesign/goa/pull/3512
* https://github.com/goadesign/goa/pull/3520

raphael: This is great, thank you!

Patch:
diff --git a/eval/eval.go b/eval/eval.go
index a3e622ba..e2ee1649 100644
--- a/eval/eval.go
+++ b/eval/eval.go
@@ -240,20 +240,26 @@ func finalizeSet(set ExpressionSet) {
 
 // caller returns the name of calling function.
 func caller() string {
-	for skip := 2; skip <= 4; skip++ {
+	var latest string
+	for skip := 2; skip <= 5; skip++ {
 		pc, _, _, ok := runtime.Caller(skip)
 		if !ok {
 			break
 		}
 		name := runtime.FuncForPC(pc).Name()
-		elems := strings.Split(name, ".")
-		caller := elems[len(elems)-1]
+		if !strings.HasPrefix(name, "goa.design/goa/v3/dsl.") {
+			break
+		}
+		caller := strings.Split(strings.TrimPrefix(name, "goa.design/goa/v3/dsl."), ".")[0]
 		for _, first := range caller {
 			if unicode.IsUpper(first) {
-				return caller
+				latest = caller
 			}
 			break
 		}
 	}
+	if latest != "" {
+		return latest
+	}
 	return "<unknown>"
 }


Test patch:
diff --git a/eval/eval_test.go b/eval/eval_test.go
index 00d1b5b5..7e4fee00 100644
--- a/eval/eval_test.go
+++ b/eval/eval_test.go
@@ -10,29 +10,45 @@ import (
 )
 
 func TestTooManyArgError(t *testing.T) {
-	cases := map[string]struct {
-		DSL   func()
-		Error string
-	}{
-		"ArrayOf":          {func() { ArrayOf(String, func() {}, func() {}) }, "too many arguments given to ArrayOf"},
-		"Attribute":        {func() { Type("name", func() { Attribute("name", 1, 2, 3, 4) }) }, "too many arguments given to Attribute"},
-		"Example":          {func() { Example(1, 2, 3) }, "too many arguments given to Example"},
-		"Files":            {func() { Files("path", "filename", func() {}, func() {}) }, "too many arguments given to Files"},
-		"MapOf":            {func() { MapOf(String, String, func() {}, func() {}) }, "too many arguments given to MapOf"},
-		"MapParams":        {func() { MapParams(1, 2) }, "too many arguments given to MapParams"},
-		"Payload":          {func() { Payload(String, 1, 2, 3) }, "too many arguments given to Payload"},
-		"Response":         {func() { API("name", func() { HTTP(func() { Response(StatusOK, "name", 1, 2) }) }) }, "too many arguments given to Response"},
-		"Result":           {func() { Result(String, 1, 2, 3) }, "too many arguments given to Result"},
-		"ResultType":       {func() { ResultType("identifier", "name", func() {}, func() {}) }, "too many arguments given to ResultType"},
-		"Scope":            {func() { BasicAuthSecurity("name", func() { Scope("name", "1", "2") }) }, "too many arguments given to Scope"},
-		"Server":           {func() { Server("name", func() {}, func() {}) }, "too many arguments given to Server"},
-		"StreamingPayload": {func() { StreamingPayload(String, 1, 2, 3) }, "too many arguments given to StreamingPayload"},
-		"StreamingResult":  {func() { StreamingResult(String, 1, 2, 3) }, "too many arguments given to StreamingResult"},
-		"Type":             {func() { Type("name", 1, 2, 3) }, "too many arguments given to Type"},
+	dsls := map[string]func(){
+		"APIKey":           func() { Type("name", func() { APIKey("scheme", "name", 1, 2, 3) }) },
+		"APIKeyField":      func() { Type("name", func() { APIKeyField("tag", "scheme", "name", 1, 2, 3) }) },
+		"AccessToken":      func() { Type("name", func() { AccessToken("name", 1, 2, 3) }) },
+		"AccessTokenField": func() { Type("name", func() { AccessTokenField("tag", "name", 1, 2, 3) }) },
+		"ArrayOf":          func() { ArrayOf(String, func() {}, func() {}) },
+		"Attribute":        func() { Type("name", func() { Attribute("name", 1, 2, 3, 4) }) },
+		"Cookie":           func() { API("name", func() { HTTP(func() { Cookie("name", 1, 2, 3, 4) }) }) },
+		"Error":            func() { API("name", func() { Error("name", 1, 2, 3, 4) }) },
+		"ErrorName":        func() { Type("name", func() { ErrorName("name", 1, 2, 3) }) },
+		"Example":          func() { Example(1, 2, 3) },
+		"Field":            func() { Type("name", func() { Field("tag", "name", 1, 2, 3, 4) }) },
+		"Files":            func() { Files("path", "filename", func() {}, func() {}) },
+		"Header":           func() { API("name", func() { HTTP(func() { Header("name", 1, 2, 3, 4) }) }) },
+		"MapOf":            func() { MapOf(String, String, func() {}, func() {}) },
+		"MapParams":        func() { MapParams(1, 2) },
+		"Param":            func() { API("name", func() { HTTP(func() { Param("name", 1, 2, 3, 4) }) }) },
+		"Password":         func() { Type("name", func() { Password("name", 1, 2, 3) }) },
+		"PasswordField":    func() { Type("name", func() { PasswordField("tag", "name", 1, 2, 3) }) },
+		"Payload":          func() { Payload(String, 1, 2, 3) },
+		"Response":         func() { API("name", func() { HTTP(func() { Response(StatusOK, "name", 1, 2) }) }) },
+		"Result":           func() { Result(String, 1, 2, 3) },
+		"ResultType":       func() { ResultType("identifier", "name", func() {}, func() {}) },
+		"Scope":            func() { BasicAuthSecurity("name", func() { Scope("name", "1", "2") }) },
+		"Server":           func() { Server("name", func() {}, func() {}) },
+		"StreamingPayload": func() { StreamingPayload(String, 1, 2, 3) },
+		"StreamingResult":  func() { StreamingResult(String, 1, 2, 3) },
+		"Token":            func() { Type("name", func() { Token("name", 1, 2, 3) }) },
+		"TokenField":       func() { Type("name", func() { TokenField("tag", "name", 1, 2, 3) }) },
+		"Type":             func() { Type("name", 1, 2, 3) },
+		"Username":         func() { Type("name", func() { Username("name", 1, 2, 3) }) },
+		"UsernameField":    func() { Type("name", func() { UsernameField("tag", "name", 1, 2, 3) }) },
+		"Variable":         func() { API("a", func() { Server("s", func() { Host("h", func() { Variable("v", 1, 2, 3, 4) }) }) }) },
 	}
-	for _, tc := range cases {
-		err := expr.RunInvalidDSL(t, tc.DSL)
-		assert.Len(t, strings.Split(err.Error(), "\n"), 1)
-		assert.Contains(t, err.Error(), tc.Error)
+	for name, dsl := range dsls {
+		t.Run(name, func(t *testing.T) {
+			err := expr.RunInvalidDSL(t, dsl)
+			assert.Len(t, strings.Split(err.Error(), "\n"), 1)
+			assert.Contains(t, err.Error(), "too many arguments given to "+name)
+		})
 	}
 }