Repository: go-swagger/go-swagger (go)

Issue:
v0.25.0 generates incorrect imports
## Problem statement

swagger generate server --spec spec/swagger.yaml --target internal --model-package restapi/models

Using v.024.0 this generates responses with proper imports:

	"github.com/foo/bar/internal/restapi/models"

Using v0.25.0 restapi is missing, so the code won't compile

	"github.com/foo/bar/internal/models"

## Swagger specification
```yaml
swagger: '2.0'
info:
  title: Foo Bar
  version: "1.0"
  description: Temp description
schemes:
  - http
basePath: /api
produces:
  - application/json
  - text/plain
consumes:
  - application/json

paths:
  '/ping':
    get:
      summary: Ping the server
      description: Ping the server
      operationId: ping
      produces:
        - text/plain
      security: []
      responses:
        200:
          description: OK
          schema:
            type: string
            example:
              'pong'
        500:
          $ref: "#/responses/internalServerError"

definitions:
  Error:
    type: object
    properties:
      code:
        type: integer
      description:
        type: string

responses:
  internalServerError:
    description: Server error
    schema:
      $ref: '#/definitions/Error'
```

## Steps to reproduce
```bash
go mod init github.com/foo/bar
swagger generate server --spec spec/swagger.yaml --target internal --model-package restapi/models
go build ./...
```

## Environment
swagger version: 0.25.0
go version: 1.14.4
OS:   alpine (docker)


Pull request:
fixed generated package resolution for principal
* fixes #2362

Support pre v0.25 behavior regarding the specification of the principal (e.g. --principal {package}.{type})

* v0.25 behavior is maintained (e.g. --principal github.com/example/auth.Principal), with package alias deconfliction
* specifying the principal without a fully qualified package name (e.g. --principal auth.Principal)
  results in a package assumed to be within the generated target
* as a special case of interest, this works when the principal is a type in generated models
  (e.g. --models-package models/generated --principal generated.Principal)
* covers the edge case raised by #2362, that is, even in the case of models generated in some subdirectory
  (e.g. --models-package restapi/models --package restapi/models.User)

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>

Patch:
diff --git a/fixtures/bugs/2362/.gitignore b/fixtures/bugs/2362/.gitignore
new file mode 100644
index 00000000..e6936eee
--- /dev/null
+++ b/fixtures/bugs/2362/.gitignore
@@ -0,0 +1,1 @@
+case?/
diff --git a/fixtures/bugs/2362/fixture-2362.yaml b/fixtures/bugs/2362/fixture-2362.yaml
new file mode 100644
index 00000000..fe320f11
--- /dev/null
+++ b/fixtures/bugs/2362/fixture-2362.yaml
@@ -0,0 +1,54 @@
+swagger: '2.0'
+info:
+  title: Foo Bar
+  version: "1.0"
+  description: Temp description
+schemes:
+  - http
+basePath: /api
+produces:
+  - application/json
+  - text/plain
+consumes:
+  - application/json
+
+securityDefinitions:
+  apikey:
+    type: apiKey
+    name: api_key
+    in: header
+
+paths:
+  '/ping':
+    get:
+      security:
+      - apikey: []
+      summary: Ping the server
+      description: Ping the server
+      operationId: ping
+      produces:
+        - text/plain
+      responses:
+        200:
+          description: OK
+          schema:
+            type: string
+            example:
+              'pong'
+        500:
+          $ref: "#/responses/internalServerError"
+
+definitions:
+  Error:
+    type: object
+    properties:
+      code:
+        type: integer
+      description:
+        type: string
+
+responses:
+  internalServerError:
+    description: Server error
+    schema:
+      $ref: '#/definitions/Error'
diff --git a/fixtures/bugs/2362/gen-fixtures.sh b/fixtures/bugs/2362/gen-fixtures.sh
new file mode 100755
index 00000000..bcf6b6ec
--- /dev/null
+++ b/fixtures/bugs/2362/gen-fixtures.sh
@@ -0,0 +1,52 @@
+#! /bin/bash
+rm -rf case1 && mkdir case1
+swagger generate server \
+  --spec fixture-2362.yaml \
+  --target case1 \
+  --model-package restapi/models \
+  --principal github.com/go-swagger/go-swagger/fixtures/bugs/2362/case1/restapi/models.Principal \
+  --exclude-main \
+  --name data-updater
+printf "package models\n\ntype Principal struct{}" >> case1/restapi/models/principal.go
+
+rm -rf case2 && mkdir case2
+swagger generate server \
+  --spec fixture-2362.yaml \
+  --target case2 \
+  --model-package restapi/models \
+  --principal models.Principal \
+  --exclude-main \
+  --name data-updater
+printf "package models\n\ntype Principal struct{}" >> case2/restapi/models/principal.go
+
+rm -rf case3 && mkdir case3
+swagger generate server \
+  --spec fixture-2362.yaml \
+  --target case3 \
+  --model-package restapi/models \
+  --principal restapi/models.Principal \
+  --exclude-main \
+  --name data-updater
+printf "package models\n\ntype Principal struct{}" >> case3/restapi/models/principal.go
+
+rm -rf case4 && mkdir case4
+swagger generate server \
+  --spec fixture-2362.yaml \
+  --target case4 \
+  --model-package restapi/models \
+  --principal internal.Principal \
+  --exclude-main \
+  --name data-updater
+mkdir -p case4/internal
+printf "package internal\n\ntype Principal struct{}" >> case4/internal/principal.go
+
+rm -rf case5 && mkdir case5
+swagger generate server \
+  --spec fixture-2362.yaml \
+  --target case5 \
+  --model-package restapi/generated \
+  --principal models.Principal \
+  --exclude-main \
+  --name data-updater
+mkdir -p case5/models
+printf "package models\n\ntype Principal struct{}" >> case5/models/principal.go
diff --git a/generator/shared.go b/generator/shared.go
index 0bd7da26..3777a274 100644
--- a/generator/shared.go
+++ b/generator/shared.go
@@ -690,27 +690,33 @@ func (g *GenOpts) defaultImports() map[string]string {
 	baseImport := g.LanguageOpts.baseImport(g.Target)
 	defaultImports := make(map[string]string, 50)
 
+	var modelsAlias, importPath string
 	if g.ExistingModels == "" {
 		// generated models
-		importPath := path.Join(
+		importPath = path.Join(
 			baseImport,
 			g.LanguageOpts.ManglePackagePath(g.ModelPackage, defaultModelsTarget))
-		defaultImports[g.LanguageOpts.ManglePackageName(g.ModelPackage, defaultModelsTarget)] = importPath
+		modelsAlias = g.LanguageOpts.ManglePackageName(g.ModelPackage, defaultModelsTarget)
 	} else {
 		// external models
-		importPath := g.LanguageOpts.ManglePackagePath(g.ExistingModels, "")
-		defaultImports["models"] = importPath
+		importPath = g.LanguageOpts.ManglePackagePath(g.ExistingModels, "")
+		modelsAlias = path.Base(defaultModelsTarget)
 	}
+	defaultImports[modelsAlias] = importPath
 
+	// resolve model representing an authenticated principal
 	alias, _, target := g.resolvePrincipal()
-	if alias != "" {
-		if pth, _ := path.Split(target); pth != "" {
-			// if principal is specified with an path, generate this import
-			defaultImports[alias] = target
-		} else {
-			// if principal is specified with a relative path, assume it is located in generated target
-			defaultImports[alias] = path.Join(baseImport, target)
-		}
+	if alias == "" || target == g.ModelPackage || path.Base(target) == modelsAlias {
+		// if principal is specified with the models generation package, do not import any extra package
+		return defaultImports
+	}
+
+	if pth, _ := path.Split(target); pth != "" {
+		// if principal is specified with a path, assume this is a fully qualified package and generate this import
+		defaultImports[alias] = target
+	} else {
+		// if principal is specified with a relative path (no "/", e.g. internal.Principal), assume it is located in generated target
+		defaultImports[alias] = path.Join(baseImport, target)
 	}
 	return defaultImports
 }


Test patch:
diff --git a/generator/shared_test.go b/generator/shared_test.go
index d89a7f41..113a1bb3 100644
--- a/generator/shared_test.go
+++ b/generator/shared_test.go
@@ -675,6 +675,50 @@ func TestDefaultImports(t *testing.T) {
 				"models": "github.com/myproject/target/bespoke",
 			},
 		},
+		// issue #2362
+		{
+			Title: "relative principal, in dedicated package under generated target",
+			Opts: &GenOpts{
+				Principal:    "auth.Principal",
+				ModelPackage: "target/bespoke",
+			},
+			Expected: map[string]string{
+				"bespoke": "github.com/go-swagger/go-swagger/generator/target/bespoke",
+				"auth":    "github.com/go-swagger/go-swagger/generator/auth",
+			},
+		},
+		{
+			Title: "relative principal in models (1)",
+			Opts: &GenOpts{
+				Principal:    "bespoke.Principal",
+				ModelPackage: "target/bespoke",
+			},
+			Expected: map[string]string{
+				"bespoke": "github.com/go-swagger/go-swagger/generator/target/bespoke",
+			},
+		},
+		{
+			Title: "relative principal in models (2)",
+			Opts: &GenOpts{
+				Principal:    "target/bespoke.Principal",
+				ModelPackage: "target/bespoke",
+			},
+			Expected: map[string]string{
+				"bespoke": "github.com/go-swagger/go-swagger/generator/target/bespoke",
+			},
+		},
+		{
+			Title: "relative principal: not detected",
+			// NOTE: this case will probably not build: no way to determine the user intent
+			Opts: &GenOpts{
+				Principal:    "target/auth.Principal",
+				ModelPackage: "target/models",
+			},
+			Expected: map[string]string{
+				"models": "github.com/go-swagger/go-swagger/generator/target/models",
+				"auth":   "target/auth",
+			},
+		},
 	} {
 		fixture := toPin
 		t.Run(fixture.Title, func(t *testing.T) {