private/apigen: prevent self imports
This change prevents Go code produced by the API generator from importing its own package. Previously, we tried to prevent self imports by skipping import paths whose last segment matched the generated Go code's package name. However, aliased imports circumvented this. We now require API definitions to define the Go package path so that we can compare this with the import path directly. Change-Id: I7ae7ec5e1a342d2f76cd28ff72d4cd7285c2820a
This commit is contained in:
parent
24370964ab
commit
a52934ef4d
@ -30,7 +30,11 @@ type API struct {
|
||||
Version string
|
||||
Description string
|
||||
// The package name to use for the Go generated code.
|
||||
// If omitted, the last segment of the PackagePath will be used as the package name.
|
||||
PackageName string
|
||||
// The path of the package that will use the generated Go code.
|
||||
// This is used to prevent the code from importing its own package.
|
||||
PackagePath string
|
||||
// BasePath is the base path for the API endpoints. E.g. "/api".
|
||||
// It doesn't require to begin with "/". When empty, "/" is used.
|
||||
BasePath string
|
||||
|
@ -16,7 +16,11 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := &apigen.API{PackageName: "example", Version: "v0", BasePath: "/api"}
|
||||
a := &apigen.API{
|
||||
PackagePath: "storj.io/storj/private/apigen/example",
|
||||
Version: "v0",
|
||||
BasePath: "/api",
|
||||
}
|
||||
|
||||
g := a.Group("Documents", "docs")
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
package apigen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/format"
|
||||
"os"
|
||||
"reflect"
|
||||
@ -38,14 +39,14 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
result := &StringBuilder{}
|
||||
pf := result.Writelnf
|
||||
|
||||
getPackageName := func(path string) string {
|
||||
pathPackages := strings.Split(path, "/")
|
||||
name := pathPackages[len(pathPackages)-1]
|
||||
if name == "main" {
|
||||
panic(errs.New(`invalid package name. Your types cannot be defined in a package named "main"`))
|
||||
}
|
||||
if a.PackagePath == "" {
|
||||
return nil, errs.New("Package path must be defined")
|
||||
}
|
||||
|
||||
return name
|
||||
packageName := a.PackageName
|
||||
if packageName == "" {
|
||||
parts := strings.Split(a.PackagePath, "/")
|
||||
packageName = parts[len(parts)-1]
|
||||
}
|
||||
|
||||
imports := struct {
|
||||
@ -59,7 +60,7 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
|
||||
i := func(paths ...string) {
|
||||
for _, path := range paths {
|
||||
if path == "" || getPackageName(path) == a.PackageName {
|
||||
if path == "" || path == a.PackagePath {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -107,7 +108,7 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
pf(
|
||||
"var Err%sAPI = errs.Class(\"%s %s api\")",
|
||||
capitalize(group.Prefix),
|
||||
a.PackageName,
|
||||
packageName,
|
||||
strings.ToLower(group.Prefix),
|
||||
)
|
||||
}
|
||||
@ -286,7 +287,7 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
pf("// DO NOT EDIT.")
|
||||
pf("")
|
||||
|
||||
pf("package %s", a.PackageName)
|
||||
pf("package %s", packageName)
|
||||
pf("")
|
||||
|
||||
pf("import (")
|
||||
@ -322,8 +323,17 @@ func (a *API) generateGo() ([]byte, error) {
|
||||
// If type is from the same package then we use only type's name.
|
||||
// If type is from external package then we use type along with its appropriate package name.
|
||||
func (a *API) handleTypesPackage(t reflect.Type) string {
|
||||
if strings.HasPrefix(t.String(), a.PackageName) {
|
||||
return t.Elem().Name()
|
||||
switch t.Kind() {
|
||||
case reflect.Array:
|
||||
return fmt.Sprintf("[%d]%s", t.Len(), a.handleTypesPackage(t.Elem()))
|
||||
case reflect.Slice:
|
||||
return "[]" + a.handleTypesPackage(t.Elem())
|
||||
case reflect.Pointer:
|
||||
return "*" + a.handleTypesPackage(t.Elem())
|
||||
}
|
||||
|
||||
if t.PkgPath() == a.PackagePath {
|
||||
return t.Name()
|
||||
}
|
||||
|
||||
return t.String()
|
||||
|
@ -13,7 +13,12 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
api := &apigen.API{PackageName: "admin", Version: "v1", BasePath: "/api"}
|
||||
api := &apigen.API{
|
||||
PackageName: "admin",
|
||||
PackagePath: "storj.io/storj/satellite/admin/back-office",
|
||||
Version: "v1",
|
||||
BasePath: "/api",
|
||||
}
|
||||
|
||||
// This is an example and must be deleted when we define the first real endpoint.
|
||||
group := api.Group("Example", "example")
|
||||
|
@ -24,7 +24,7 @@ func main() {
|
||||
Version: "v0",
|
||||
BasePath: "/api",
|
||||
Description: "Interacts with projects",
|
||||
PackageName: "consoleapi",
|
||||
PackagePath: "storj.io/storj/satellite/console/consoleweb/consoleapi",
|
||||
}
|
||||
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user