From dae5af798bde1ab81613ab76f8305e4fb93372d8 Mon Sep 17 00:00:00 2001
From: ko_oler <kooler89@gmail.com>
Date: Fri, 22 Sep 2023 14:12:02 +0300
Subject: [PATCH] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D0=BF?=
 =?UTF-8?q?=D0=BE=20=D0=9F=D0=A0:=20-=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B8?=
 =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=20=D1=82=D0=B8=D0=BF?=
 =?UTF-8?q?=20=D1=81=20parsedActionURL=20->=20ActionURL=20-=20=D0=B2=D1=8B?=
 =?UTF-8?q?=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=20=D0=B2=20=D0=BE=D1=82=D0=B4?=
 =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=84=D0=B0=D0=B9=D0=BB?=
 =?UTF-8?q?,=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE?=
 =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D1=8B=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B?=
 =?UTF-8?q?,=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=20=D0=BA=D0=BE?=
 =?UTF-8?q?=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D0=BE=D1=80=20-=20?=
 =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=82?=
 =?UTF-8?q?=D0=B5=D1=81=D1=82=D1=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pkg/extension/action_url.go        | 70 ++++++++++++++++++++++++
 pkg/extension/action_url_test.go   | 87 ++++++++++++++++++++++++++++++
 pkg/extension/extension.go         | 47 ----------------
 pkg/extension/server.go            | 13 +++--
 pkg/extension/server_test.go       | 81 ++++++++++++++++++++++++++++
 pkg/extension/service/extension.go | 12 +++--
 6 files changed, 254 insertions(+), 56 deletions(-)
 create mode 100644 pkg/extension/action_url.go
 create mode 100644 pkg/extension/action_url_test.go

diff --git a/pkg/extension/action_url.go b/pkg/extension/action_url.go
new file mode 100644
index 00000000..72d35371
--- /dev/null
+++ b/pkg/extension/action_url.go
@@ -0,0 +1,70 @@
+package extension
+
+import (
+	"net/url"
+	"strings"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+)
+
+// ActionURL структура для хранения данных о переданном действии
+type ActionURL struct {
+	actionURL   string
+	actionID    string
+	extensionID string
+	scheme      string
+	url         *url.URL
+}
+
+// NewActionURL возвращает пустую структуру ActionURL, если передано пустое действие
+// при передаче в функцию действия - заполняет структуру
+func NewActionURL(action string) (*ActionURL, error) {
+	if action != "" {
+		parsedURL, err := parseActionURL(action)
+		if err != nil {
+			return nil, err
+		}
+		return parsedURL, nil
+	}
+	return &ActionURL{}, nil
+}
+
+// ID возвращает сохраненный в ActionURL id действия
+func (p *ActionURL) ID() string {
+	return p.actionID
+}
+
+// ExtensionID возвращает сохраненный в ActionURL id расширения
+func (p *ActionURL) ExtensionID() string {
+	return p.extensionID
+}
+
+// Scheme возвращает сохраненную в ActionURL схему
+func (p *ActionURL) Scheme() string {
+	return p.scheme
+}
+
+// parseActionURL функция для заполнения структуры ActionURL из переданного действия
+func parseActionURL(action string) (*ActionURL, error) {
+	u, err := url.Parse(action)
+	if err != nil {
+		return nil, err
+	}
+	parsed := &ActionURL{}
+	parsed.actionURL = action
+	parsed.url = u
+	parsed.scheme = u.Scheme
+	if parsed.Scheme() == "grpc" {
+		path := u.Path
+		if strings.HasPrefix(u.Path, "/") {
+			path = u.Path[1:]
+		}
+		splitPath := strings.Split(path, "/")
+		if len(splitPath) < 2 {
+			return nil, errors.Errorf("incorrect action URL, no action id: '%s'", action)
+		}
+		parsed.extensionID = splitPath[0]
+		parsed.actionID = splitPath[1]
+	}
+	return parsed, nil
+}
diff --git a/pkg/extension/action_url_test.go b/pkg/extension/action_url_test.go
new file mode 100644
index 00000000..7c5ef5e4
--- /dev/null
+++ b/pkg/extension/action_url_test.go
@@ -0,0 +1,87 @@
+package extension
+
+import (
+	"fmt"
+	"net/url"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewActionURL(t *testing.T) {
+	u1, _ := url.Parse("build-site")
+	u2, _ := url.Parse("grpc:///perxisweb/build-site")
+	u3, _ := url.Parse("ui:///space/env/coll")
+	u4, _ := url.Parse("https://perx.ru")
+	tests := []struct {
+		name    string
+		action  string
+		want    *ActionURL
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			"Without action",
+			"",
+			&ActionURL{},
+			assert.NoError,
+		},
+		{
+			"Without deprecated action call",
+			"build-site",
+			&ActionURL{
+				actionURL: "build-site",
+				url:       u1,
+			},
+			assert.NoError,
+		},
+		{
+			name:   "With grpc action",
+			action: "grpc:///perxisweb/build-site",
+			want: &ActionURL{
+				actionURL:   "grpc:///perxisweb/build-site",
+				actionID:    "build-site",
+				extensionID: "perxisweb",
+				scheme:      "grpc",
+				url:         u2,
+			},
+			wantErr: assert.NoError,
+		},
+		{
+			name:   "With ui action",
+			action: "ui:///space/env/coll",
+			want: &ActionURL{
+				actionURL:   "ui:///space/env/coll",
+				actionID:    "",
+				extensionID: "",
+				scheme:      "ui",
+				url:         u3,
+			},
+			wantErr: assert.NoError,
+		},
+		{
+			name:   "With http action",
+			action: "https://perx.ru",
+			want: &ActionURL{
+				actionURL: "https://perx.ru",
+				scheme:    "https",
+				url:       u4,
+			},
+			wantErr: assert.NoError,
+		},
+		{
+			name:    "With error in parse",
+			action:  "grpc://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require",
+			want:    nil,
+			wantErr: assert.Error,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := NewActionURL(tt.action)
+			if !tt.wantErr(t, err, fmt.Sprintf("NewActionURL(%v)", tt.action)) {
+				return
+			}
+			assert.Equalf(t, tt.want, got, "NewActionURL(%v)", tt.action)
+		})
+	}
+}
diff --git a/pkg/extension/extension.go b/pkg/extension/extension.go
index 174ac51c..fb8044c7 100644
--- a/pkg/extension/extension.go
+++ b/pkg/extension/extension.go
@@ -3,8 +3,6 @@ package extension
 import (
 	"context"
 	"fmt"
-	"net/url"
-	"strings"
 
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
@@ -107,48 +105,3 @@ func ExtensionFromError(err error) string {
 	ext, _ := v.(string)
 	return ext
 }
-
-type ParsedActionURL struct {
-	actionID    string
-	extensionID string
-	scheme      string
-}
-
-func (p *ParsedActionURL) New() *ParsedActionURL {
-	return &ParsedActionURL{}
-}
-
-func (p *ParsedActionURL) GetActionID() string {
-	return p.actionID
-}
-
-func (p *ParsedActionURL) GetExtensionID() string {
-	return p.extensionID
-}
-
-func (p *ParsedActionURL) GetScheme() string {
-	return p.scheme
-}
-
-func ParseActionURL(action string) (*ParsedActionURL, error) {
-
-	u, err := url.Parse(action)
-	if err != nil {
-		return nil, err
-	}
-	parsed := &ParsedActionURL{}
-	parsed.scheme = u.Scheme
-	if parsed.GetScheme() == "grpc" {
-		path := u.Path
-		if strings.HasPrefix(u.Path, "/") {
-			path = u.Path[1:]
-		}
-		splitPath := strings.Split(path, "/")
-		if len(splitPath) < 2 {
-			return nil, errors.Errorf("incorrect action URL, no action id: '%s'", action)
-		}
-		parsed.extensionID = splitPath[0]
-		parsed.actionID = splitPath[1]
-	}
-	return parsed, nil
-}
diff --git a/pkg/extension/server.go b/pkg/extension/server.go
index be4b6f0a..bdeac58c 100644
--- a/pkg/extension/server.go
+++ b/pkg/extension/server.go
@@ -80,13 +80,16 @@ func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateR
 }
 
 func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (*pb.ActionResponse, error) {
-
-	parsed, err := ParseActionURL(in.Action)
-	if err != nil {
-		return nil, err
+	extensionID := in.Extension
+	if extensionID == "" {
+		actionURL, err := NewActionURL(in.Action)
+		if err != nil {
+			return nil, err
+		}
+		extensionID = actionURL.extensionID
 	}
 
-	svc, ok := srv.services[parsed.GetExtensionID()]
+	svc, ok := srv.services[extensionID]
 	if !ok {
 		return nil, ErrUnknownExtension
 	}
diff --git a/pkg/extension/server_test.go b/pkg/extension/server_test.go
index 5400c8c4..9e8b8f4c 100644
--- a/pkg/extension/server_test.go
+++ b/pkg/extension/server_test.go
@@ -2,11 +2,13 @@ package extension
 
 import (
 	"context"
+	"fmt"
 	"reflect"
 	"strings"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestGetResults(t *testing.T) {
@@ -102,3 +104,82 @@ func (t testServerExtension) Uninstall(ctx context.Context, in *UninstallRequest
 func (t testServerExtension) Action(ctx context.Context, in *ActionRequest) (*ActionResponse, error) {
 	return &ActionResponse{}, t.err
 }
+
+func TestServer_Action(t *testing.T) {
+	getDummyExtension := func(name string, wantErr ...bool) Extension {
+		ext := &testServerExtension{name: name}
+
+		if len(wantErr) > 0 {
+			ext.err = errors.WithDetail(errors.New("some err"), "Ошибка")
+		}
+
+		return ext
+	}
+
+	var tests = []struct {
+		name     string
+		services map[string]Extension
+		in       *ActionRequest
+		want     *ActionResponse
+		wantErr  assert.ErrorAssertionFunc
+	}{
+		{
+			name:     "OK (grpc)",
+			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			in: &ActionRequest{
+				Action:  "grpc:///test-extension/test-action",
+				SpaceId: "sp",
+				EnvId:   "env",
+			},
+			want:    &ActionResponse{State: ResponseDone},
+			wantErr: assert.NoError,
+		},
+		{
+			name:     "OK (deprecated call)",
+			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			in: &ActionRequest{
+				Action:    "test-action",
+				SpaceId:   "sp",
+				EnvId:     "env",
+				Extension: "test-extension",
+			},
+			want:    &ActionResponse{State: ResponseDone},
+			wantErr: assert.NoError,
+		},
+		{
+			name:     "Error (unknown extension)",
+			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			in: &ActionRequest{
+				Action:  "grpc:///test-extension-2/test-action",
+				SpaceId: "sp",
+				EnvId:   "env",
+			},
+			want:    nil,
+			wantErr: assert.Error,
+		},
+		{
+			name:     "Error (deprecated call, no extension)",
+			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			in: &ActionRequest{
+				Action:  "test-action",
+				SpaceId: "sp",
+				EnvId:   "env",
+			},
+			want:    nil,
+			wantErr: assert.Error,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+
+			srv := &Server{
+				services: tt.services,
+			}
+			got, err := srv.Action(context.Background(), tt.in)
+			if !tt.wantErr(t, err, fmt.Sprintf("Action(%v)", tt.in)) {
+				return
+			}
+			assert.Equalf(t, tt.want, got, "Action(%v)", tt.in)
+		})
+	}
+}
diff --git a/pkg/extension/service/extension.go b/pkg/extension/service/extension.go
index e0b4e2ab..f57f8253 100644
--- a/pkg/extension/service/extension.go
+++ b/pkg/extension/service/extension.go
@@ -155,11 +155,15 @@ func (s *Extension) Uninstall(ctx context.Context, in *extension.UninstallReques
 }
 
 func (s *Extension) Action(ctx context.Context, in *extension.ActionRequest) (*extension.ActionResponse, error) {
-	parsed, err := extension.ParseActionURL(in.Action)
-	if err != nil {
-		return nil, err
+	extensionID := in.Extension
+	if extensionID == "" {
+		actionURL, err := extension.NewActionURL(in.Action)
+		if err != nil {
+			return nil, err
+		}
+		extensionID = actionURL.ExtensionID()
 	}
-	ok, err := extension.CheckInstalled(ctx, s.Content, in.SpaceId, in.EnvId, parsed.GetExtensionID())
+	ok, err := extension.CheckInstalled(ctx, s.Content, in.SpaceId, in.EnvId, extensionID)
 	if err != nil {
 		return nil, errors.Wrap(err, "check extension installed")
 	}
-- 
GitLab