From 4cfd0b1b17b70a22b19e208ce95d2073a7215cc2 Mon Sep 17 00:00:00 2001
From: Pablo Morelli <pabmorelli@gmail.com>
Date: Tue, 5 Nov 2024 11:39:22 +0100
Subject: [PATCH] add support for servers on operation level for 3.1

---
 README.md           |  5 +++--
 operationv3.go      | 17 +++++++++++++++++
 operationv3_test.go | 28 ++++++++++++++++++++++++++++
 3 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 1cd185b48..321bcfe28 100644
--- a/README.md
+++ b/README.md
@@ -478,7 +478,8 @@ The following annotations are only available if you set the -v3.1 flag in the CL
 | x-name               | The extension key, must be start by x- and take only json value.                                                                                                                                  |
 | x-codeSample         | Optional Markdown usage. take `file` as parameter. This will then search for a file named like the summary in the given folder.                                                                   |
 | deprecated           | Mark endpoint as deprecated.                                                                                                                                                                      |
-
+| servers.url          | (Only for -v3.1 on the CLI) The URL of a server that will override the base one for this operation                                                                                                |
+| servers.description  | (Only for -v3.1 on the CLI) The description of a server that will override the base one for this operation                                                                                        |
 
 
 ## Mime Types
@@ -993,7 +994,7 @@ If the struct is defined in a dependency package, use `--parseDependency`.
 
 If the struct is defined in your main project, use `--parseInternal`.
 
-if you want to include both internal and from dependencies use both flags 
+if you want to include both internal and from dependencies use both flags
 ```
 swag init --parseDependency --parseInternal
 ```
diff --git a/operationv3.go b/operationv3.go
index 5d94f1045..bd65f8698 100644
--- a/operationv3.go
+++ b/operationv3.go
@@ -96,6 +96,10 @@ func (o *OperationV3) ParseComment(comment string, astFile *ast.File) error {
 		o.Deprecated = true
 	case xCodeSamplesAttr, xCodeSamplesAttrOriginal:
 		return o.ParseCodeSample(attribute, commentLine, lineRemainder)
+	case "@servers.url":
+		return o.ParseServerURLComment(lineRemainder)
+	case "@servers.description":
+		return o.ParseServerDescriptionComment(lineRemainder)
 	default:
 		return o.ParseMetadata(attribute, lowerAttribute, lineRemainder)
 	}
@@ -741,6 +745,19 @@ func (o *OperationV3) ParseRouterComment(commentLine string) error {
 	return nil
 }
 
+func (o *OperationV3) ParseServerURLComment(commentLine string) error {
+	server := spec.NewServer()
+	server.Spec.URL = commentLine
+	o.Servers = append(o.Servers, server)
+	return nil
+}
+
+func (o *OperationV3) ParseServerDescriptionComment(commentLine string) error {
+	lastAddedServer := o.Servers[len(o.Servers)-1]
+	lastAddedServer.Spec.Description = commentLine
+	return nil
+}
+
 // createParameter returns swagger spec.Parameter for given  paramType, description, paramName, schemaType, required.
 func createParameterV3(in, description, paramName, objectType, schemaType string, required bool, enums []interface{}, collectionFormat string) spec.Parameter {
 	// //five possible parameter types. 	query, path, body, header, form
diff --git a/operationv3_test.go b/operationv3_test.go
index 8408aa34f..9f0b77569 100644
--- a/operationv3_test.go
+++ b/operationv3_test.go
@@ -2054,3 +2054,31 @@ func TestProcessProduceComment(t *testing.T) {
 	content = operation.Responses.Spec.Response["500"].Spec.Spec.Content
 	assert.Nil(t, content)
 }
+
+func TestParseServerCommentV3(t *testing.T) {
+	t.Parallel()
+
+	operation := NewOperationV3(nil)
+
+	comment := `/@servers.url https://api.example.com/v1`
+	err := operation.ParseComment(comment, nil)
+	require.NoError(t, err)
+
+	comment = `/@servers.description override path 1`
+	err = operation.ParseComment(comment, nil)
+	require.NoError(t, err)
+
+	comment = `/@servers.url https://api.example.com/v2`
+	err = operation.ParseComment(comment, nil)
+	require.NoError(t, err)
+
+	comment = `/@servers.description override path 2`
+	err = operation.ParseComment(comment, nil)
+	require.NoError(t, err)
+
+	assert.Len(t, operation.Servers, 2)
+	assert.Equal(t, "https://api.example.com/v1", operation.Servers[0].Spec.URL)
+	assert.Equal(t, "override path 1", operation.Servers[0].Spec.Description)
+	assert.Equal(t, "https://api.example.com/v2", operation.Servers[1].Spec.URL)
+	assert.Equal(t, "override path 2", operation.Servers[1].Spec.Description)
+}