Skip to content
Snippets Groups Projects
Commit 2a6642c8 authored by Pavel Antonov's avatar Pavel Antonov :asterisk:
Browse files

Обработка Action в расширениях

parent cdfe0757
No related branches found
No related tags found
No related merge requests found
package extension
import (
"context"
"git.perx.ru/perxis/perxis-go/pkg/errors"
)
var (
ErrInvalidAction = errors.New("invalid action")
)
// ActionHandler выполняет действие в url
type ActionHandler func(ctx context.Context, url *ActionURL, req *ActionRequest) (*ActionResponse, error)
// ActionRouter возвращает ActionHandler для указанного действия
type ActionRouter func(ctx context.Context, url *ActionURL, req *ActionRequest) (ActionHandler, error)
// Chain возвращает ActionRouter, который последовательно вызывает переданные ActionRouter
func Chain(chain ...ActionRouter) ActionRouter {
return func(ctx context.Context, url *ActionURL, req *ActionRequest) (ActionHandler, error) {
for _, f := range chain {
if h, err := f(ctx, url, req); err == nil && h != nil {
return h, nil
}
}
return nil, ErrInvalidAction
}
}
// Named возвращает ActionRouter, который возвращает ActionHandler для указанного действия по имени
func Named(name string, handler ActionHandler) ActionRouter {
return func(ctx context.Context, url *ActionURL, req *ActionRequest) (ActionHandler, error) {
if url.Action() == name {
return handler, nil
}
return nil, ErrInvalidAction
}
}
package action package extension
import ( import (
"net/url" "net/url"
...@@ -6,20 +6,20 @@ import ( ...@@ -6,20 +6,20 @@ import (
) )
// URL структура для хранения данных о переданном действии. // URL структура для хранения данных о переданном действии.
type URL struct { type ActionURL struct {
*url.URL *url.URL
} }
// NewURL возвращает структуру ActionURL // NewURL возвращает структуру ActionURL
func NewURL(action string) (*URL, error) { func NewActionURL(action string) (*ActionURL, error) {
u, err := url.Parse(action) u, err := url.Parse(action)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &URL{URL: u}, nil return &ActionURL{URL: u}, nil
} }
func (u *URL) actionParts() (string, string) { func (u *ActionURL) actionParts() (string, string) {
if u.URL != nil && u.URL.Scheme == "grpc" { if u.URL != nil && u.URL.Scheme == "grpc" {
splitPath := strings.Split(strings.TrimLeft(u.Path, "/"), "/") splitPath := strings.Split(strings.TrimLeft(u.Path, "/"), "/")
if len(splitPath) >= 2 { if len(splitPath) >= 2 {
...@@ -29,12 +29,12 @@ func (u *URL) actionParts() (string, string) { ...@@ -29,12 +29,12 @@ func (u *URL) actionParts() (string, string) {
return "", "" return "", ""
} }
func (u *URL) Action() string { func (u *ActionURL) Action() string {
_, action := u.actionParts() _, action := u.actionParts()
return action return action
} }
func (u *URL) Extension() string { func (u *ActionURL) Extension() string {
ext, _ := u.actionParts() ext, _ := u.actionParts()
return ext return ext
} }
package action package extension
import ( import (
"fmt" "fmt"
...@@ -12,13 +12,13 @@ func TestActionURL_New(t *testing.T) { ...@@ -12,13 +12,13 @@ func TestActionURL_New(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
action string action string
want *URL want *ActionURL
url string url string
wantErr assert.ErrorAssertionFunc wantErr assert.ErrorAssertionFunc
}{ }{
{ {
name: "Without action", name: "Without action",
want: &URL{ want: &ActionURL{
URL: &url.URL{}, URL: &url.URL{},
}, },
wantErr: assert.NoError, wantErr: assert.NoError,
...@@ -26,7 +26,7 @@ func TestActionURL_New(t *testing.T) { ...@@ -26,7 +26,7 @@ func TestActionURL_New(t *testing.T) {
{ {
name: "Without deprecated action call", name: "Without deprecated action call",
action: "build-site", action: "build-site",
want: &URL{ want: &ActionURL{
URL: &url.URL{ URL: &url.URL{
Path: "build-site", Path: "build-site",
}, },
...@@ -37,7 +37,7 @@ func TestActionURL_New(t *testing.T) { ...@@ -37,7 +37,7 @@ func TestActionURL_New(t *testing.T) {
{ {
name: "With grpc action", name: "With grpc action",
action: "grpc:///perxisweb/build-site", action: "grpc:///perxisweb/build-site",
want: &URL{ want: &ActionURL{
URL: &url.URL{ URL: &url.URL{
Scheme: "grpc", Scheme: "grpc",
Path: "/perxisweb/build-site", Path: "/perxisweb/build-site",
...@@ -49,7 +49,7 @@ func TestActionURL_New(t *testing.T) { ...@@ -49,7 +49,7 @@ func TestActionURL_New(t *testing.T) {
{ {
name: "With ui action", name: "With ui action",
action: "ui:///space/env/coll", action: "ui:///space/env/coll",
want: &URL{ want: &ActionURL{
URL: &url.URL{ URL: &url.URL{
Scheme: "ui", Scheme: "ui",
Path: "/space/env/coll", Path: "/space/env/coll",
...@@ -61,7 +61,7 @@ func TestActionURL_New(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestActionURL_New(t *testing.T) {
{ {
name: "With http action", name: "With http action",
action: "https://perx.ru", action: "https://perx.ru",
want: &URL{ want: &ActionURL{
URL: &url.URL{ URL: &url.URL{
Scheme: "https", Scheme: "https",
Host: "perx.ru", Host: "perx.ru",
...@@ -79,7 +79,7 @@ func TestActionURL_New(t *testing.T) { ...@@ -79,7 +79,7 @@ func TestActionURL_New(t *testing.T) {
{ {
name: "With no action id", name: "With no action id",
action: "grpc:///perxisweb", action: "grpc:///perxisweb",
want: &URL{ want: &ActionURL{
URL: &url.URL{ URL: &url.URL{
Scheme: "grpc", Scheme: "grpc",
Path: "/perxisweb", Path: "/perxisweb",
...@@ -90,7 +90,7 @@ func TestActionURL_New(t *testing.T) { ...@@ -90,7 +90,7 @@ func TestActionURL_New(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := NewURL(tt.action) got, err := NewActionURL(tt.action)
if !tt.wantErr(t, err, fmt.Sprintf("NewURL(%v)", tt.action)) { if !tt.wantErr(t, err, fmt.Sprintf("NewURL(%v)", tt.action)) {
return return
} }
...@@ -128,7 +128,7 @@ func TestActionURL_String(t *testing.T) { ...@@ -128,7 +128,7 @@ func TestActionURL_String(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
p, _ := NewURL(tt.url) p, _ := NewActionURL(tt.url)
assert.Equalf(t, tt.url, p.String(), "String()") assert.Equalf(t, tt.url, p.String(), "String()")
}) })
} }
......
...@@ -3,7 +3,6 @@ package extension ...@@ -3,7 +3,6 @@ package extension
import ( import (
"context" "context"
"git.perx.ru/perxis/perxis-go/pkg/action"
"git.perx.ru/perxis/perxis-go/pkg/errors" "git.perx.ru/perxis/perxis-go/pkg/errors"
pb "git.perx.ru/perxis/perxis-go/proto/extensions" pb "git.perx.ru/perxis/perxis-go/proto/extensions"
) )
...@@ -80,25 +79,36 @@ func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateR ...@@ -80,25 +79,36 @@ func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateR
return &UpdateResponse{Results: res}, nil return &UpdateResponse{Results: res}, nil
} }
func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (*pb.ActionResponse, error) { func (srv *Server) getExtensionService(ctx context.Context, in *pb.ActionRequest) (Extension, error) {
actionURL, err := action.NewURL(in.Action) actionURL, err := NewActionURL(in.Action)
if err != nil { if err != nil {
return nil, err return nil, ErrInvalidAction
} }
ext := actionURL.Extension() ext := actionURL.Extension()
if ext == "" { if ext == "" {
ext = in.Extension ext = in.Extension
} }
if ext == "" { if ext == "" {
return nil, errors.New("extension ID required") return nil, ErrInvalidAction
} }
svc, ok := srv.services[ext] svc, ok := srv.services[ext]
if !ok { if ok {
return nil, ErrUnknownExtension return svc, nil
}
return nil, ErrInvalidAction
}
func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (out *pb.ActionResponse, err error) {
svc, err := srv.getExtensionService(ctx, in)
if err != nil {
return nil, err
} }
out, err := svc.Action(ctx, in) out, err = svc.Action(ctx, in)
if out == nil { if out == nil {
out = &ActionResponse{} out = &ActionResponse{}
......
...@@ -142,7 +142,7 @@ func TestServer_Action(t *testing.T) { ...@@ -142,7 +142,7 @@ func TestServer_Action(t *testing.T) {
EnvId: "env", EnvId: "env",
}, },
want: &ActionResponse{State: ResponseDone}, want: &ActionResponse{State: ResponseDone},
wantErr: "extension ID required", wantErr: "invalid action",
}, },
{ {
name: "Deprecated call", name: "Deprecated call",
...@@ -164,7 +164,7 @@ func TestServer_Action(t *testing.T) { ...@@ -164,7 +164,7 @@ func TestServer_Action(t *testing.T) {
EnvId: "env", EnvId: "env",
}, },
want: nil, want: nil,
wantErr: ErrUnknownExtension.Error(), wantErr: "invalid action",
}, },
{ {
name: "Deprecated call, without extension", name: "Deprecated call, without extension",
...@@ -175,7 +175,7 @@ func TestServer_Action(t *testing.T) { ...@@ -175,7 +175,7 @@ func TestServer_Action(t *testing.T) {
EnvId: "env", EnvId: "env",
}, },
want: nil, want: nil,
wantErr: "extension ID required", wantErr: "invalid action",
}, },
{ {
name: "Deprecated call, without action and extension)", name: "Deprecated call, without action and extension)",
...@@ -185,7 +185,7 @@ func TestServer_Action(t *testing.T) { ...@@ -185,7 +185,7 @@ func TestServer_Action(t *testing.T) {
EnvId: "env", EnvId: "env",
}, },
want: nil, want: nil,
wantErr: "extension ID required", wantErr: "invalid action",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
......
...@@ -4,10 +4,8 @@ import ( ...@@ -4,10 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"git.perx.ru/perxis/perxis-go/pkg/action"
"git.perx.ru/perxis/perxis-go/pkg/clients" "git.perx.ru/perxis/perxis-go/pkg/clients"
"git.perx.ru/perxis/perxis-go/pkg/content" "git.perx.ru/perxis/perxis-go/pkg/content"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"git.perx.ru/perxis/perxis-go/pkg/extension" "git.perx.ru/perxis/perxis-go/pkg/extension"
"git.perx.ru/perxis/perxis-go/pkg/roles" "git.perx.ru/perxis/perxis-go/pkg/roles"
"git.perx.ru/perxis/perxis-go/pkg/setup" "git.perx.ru/perxis/perxis-go/pkg/setup"
...@@ -32,6 +30,7 @@ type Extension struct { ...@@ -32,6 +30,7 @@ type Extension struct {
Content *content.Content Content *content.Content
Logger *zap.Logger Logger *zap.Logger
manager extension.Manager manager extension.Manager
router extension.ActionRouter
withClient bool withClient bool
role *roles.Role role *roles.Role
...@@ -155,25 +154,40 @@ func (s *Extension) Uninstall(ctx context.Context, in *extension.UninstallReques ...@@ -155,25 +154,40 @@ func (s *Extension) Uninstall(ctx context.Context, in *extension.UninstallReques
return s.GetSetup(in.SpaceId, in.EnvId).WithForce(in.Force).WithRemove(in.Remove).Uninstall(ctx) return s.GetSetup(in.SpaceId, in.EnvId).WithForce(in.Force).WithRemove(in.Remove).Uninstall(ctx)
} }
func (s *Extension) Action(ctx context.Context, in *extension.ActionRequest) (*extension.ActionResponse, error) { // isCorrectExtension проверяет что расширение в url совпадает с расширением расширения
actionURL, err := action.NewURL(in.Action) func (s *Extension) isCorrectExtension(ctx context.Context, url *extension.ActionURL, in *extension.ActionRequest) bool {
if err != nil { ext := url.Extension()
return nil, err
}
ext := actionURL.Extension()
if ext == "" { if ext == "" {
ext = in.Extension ext = in.Extension
} }
if ext == "" {
return nil, errors.New("extension ID required") if in.Extension == s.desc.Extension {
return true
}
return false
}
func (s *Extension) Action(ctx context.Context, in *extension.ActionRequest) (*extension.ActionResponse, error) {
// TBD: нужно ли проверять что действие установлено в пространство
// мы так и не договорились, но проверка появилась
// пусть решает каждое расширение само
//
//ok, err := extension.CheckInstalled(ctx, s.Content, in.SpaceId, in.EnvId, ext)
//if err != nil {
// return nil, errors.Wrap(err, "check extension installed")
//}
//if !ok {
// return nil, errors.New("extension not installed")
//}
if s.router == nil {
url, err := extension.NewActionURL(in.Action)
if err == nil && url != nil && s.isCorrectExtension(ctx, url, in) {
if h, err := s.router(ctx, url, in); err == nil && h != nil {
return h(ctx, url, in)
} }
ok, err := extension.CheckInstalled(ctx, s.Content, in.SpaceId, in.EnvId, ext)
if err != nil {
return nil, errors.Wrap(err, "check extension installed")
} }
if !ok {
return nil, errors.New("extension not installed")
} }
return &extension.ActionResponse{}, nil return nil, extension.ErrInvalidAction
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment