diff --git a/pkg/extension/action_handler.go b/pkg/extension/action_handler.go new file mode 100644 index 0000000000000000000000000000000000000000..3079b88582a8c392b948bb5d46ff2bbbb7d91e06 --- /dev/null +++ b/pkg/extension/action_handler.go @@ -0,0 +1,39 @@ +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 + } +} diff --git a/pkg/action/action.go b/pkg/extension/action_url.go similarity index 68% rename from pkg/action/action.go rename to pkg/extension/action_url.go index 4233d986f5cf0ecd199f64c42b5f6453e0533fce..463ac68fd41b7978f82fe21665cdd3e435a6e278 100644 --- a/pkg/action/action.go +++ b/pkg/extension/action_url.go @@ -1,4 +1,4 @@ -package action +package extension import ( "net/url" @@ -6,20 +6,20 @@ import ( ) // URL структура для хранения данных о переданном действии. -type URL struct { +type ActionURL struct { *url.URL } // NewURL возвращает структуру ActionURL -func NewURL(action string) (*URL, error) { +func NewActionURL(action string) (*ActionURL, error) { u, err := url.Parse(action) if err != nil { 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" { splitPath := strings.Split(strings.TrimLeft(u.Path, "/"), "/") if len(splitPath) >= 2 { @@ -29,12 +29,12 @@ func (u *URL) actionParts() (string, string) { return "", "" } -func (u *URL) Action() string { +func (u *ActionURL) Action() string { _, action := u.actionParts() return action } -func (u *URL) Extension() string { +func (u *ActionURL) Extension() string { ext, _ := u.actionParts() return ext } diff --git a/pkg/action/action_test.go b/pkg/extension/action_url_test.go similarity index 90% rename from pkg/action/action_test.go rename to pkg/extension/action_url_test.go index 2988744f4b88b734c4085b5aa20ceb95fe0657da..be9a924b9478c316dc80d8deb629e044664df237 100644 --- a/pkg/action/action_test.go +++ b/pkg/extension/action_url_test.go @@ -1,4 +1,4 @@ -package action +package extension import ( "fmt" @@ -12,13 +12,13 @@ func TestActionURL_New(t *testing.T) { tests := []struct { name string action string - want *URL + want *ActionURL url string wantErr assert.ErrorAssertionFunc }{ { name: "Without action", - want: &URL{ + want: &ActionURL{ URL: &url.URL{}, }, wantErr: assert.NoError, @@ -26,7 +26,7 @@ func TestActionURL_New(t *testing.T) { { name: "Without deprecated action call", action: "build-site", - want: &URL{ + want: &ActionURL{ URL: &url.URL{ Path: "build-site", }, @@ -37,7 +37,7 @@ func TestActionURL_New(t *testing.T) { { name: "With grpc action", action: "grpc:///perxisweb/build-site", - want: &URL{ + want: &ActionURL{ URL: &url.URL{ Scheme: "grpc", Path: "/perxisweb/build-site", @@ -49,7 +49,7 @@ func TestActionURL_New(t *testing.T) { { name: "With ui action", action: "ui:///space/env/coll", - want: &URL{ + want: &ActionURL{ URL: &url.URL{ Scheme: "ui", Path: "/space/env/coll", @@ -61,7 +61,7 @@ func TestActionURL_New(t *testing.T) { { name: "With http action", action: "https://perx.ru", - want: &URL{ + want: &ActionURL{ URL: &url.URL{ Scheme: "https", Host: "perx.ru", @@ -79,7 +79,7 @@ func TestActionURL_New(t *testing.T) { { name: "With no action id", action: "grpc:///perxisweb", - want: &URL{ + want: &ActionURL{ URL: &url.URL{ Scheme: "grpc", Path: "/perxisweb", @@ -90,7 +90,7 @@ func TestActionURL_New(t *testing.T) { } for _, tt := range tests { 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)) { return } @@ -128,7 +128,7 @@ func TestActionURL_String(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p, _ := NewURL(tt.url) + p, _ := NewActionURL(tt.url) assert.Equalf(t, tt.url, p.String(), "String()") }) } diff --git a/pkg/extension/server.go b/pkg/extension/server.go index f9b603f50ffb6109f89551a7e3b92b5c355761d4..467b4396c743d1d6bbdbcf3cf444199a6ecaf192 100644 --- a/pkg/extension/server.go +++ b/pkg/extension/server.go @@ -3,7 +3,6 @@ package extension import ( "context" - "git.perx.ru/perxis/perxis-go/pkg/action" "git.perx.ru/perxis/perxis-go/pkg/errors" pb "git.perx.ru/perxis/perxis-go/proto/extensions" ) @@ -80,25 +79,36 @@ func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateR return &UpdateResponse{Results: res}, nil } -func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (*pb.ActionResponse, error) { - actionURL, err := action.NewURL(in.Action) +func (srv *Server) getExtensionService(ctx context.Context, in *pb.ActionRequest) (Extension, error) { + actionURL, err := NewActionURL(in.Action) + if err != nil { - return nil, err + return nil, ErrInvalidAction } + ext := actionURL.Extension() if ext == "" { ext = in.Extension } if ext == "" { - return nil, errors.New("extension ID required") + return nil, ErrInvalidAction } svc, ok := srv.services[ext] - if !ok { - return nil, ErrUnknownExtension + if ok { + 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 { out = &ActionResponse{} diff --git a/pkg/extension/server_test.go b/pkg/extension/server_test.go index bee68b1a8e3f8ca90beae259ceda2a933e3f6b3e..64f10e98705bc6e299e0c957d1deaa337e12f22d 100644 --- a/pkg/extension/server_test.go +++ b/pkg/extension/server_test.go @@ -142,7 +142,7 @@ func TestServer_Action(t *testing.T) { EnvId: "env", }, want: &ActionResponse{State: ResponseDone}, - wantErr: "extension ID required", + wantErr: "invalid action", }, { name: "Deprecated call", @@ -164,7 +164,7 @@ func TestServer_Action(t *testing.T) { EnvId: "env", }, want: nil, - wantErr: ErrUnknownExtension.Error(), + wantErr: "invalid action", }, { name: "Deprecated call, without extension", @@ -175,7 +175,7 @@ func TestServer_Action(t *testing.T) { EnvId: "env", }, want: nil, - wantErr: "extension ID required", + wantErr: "invalid action", }, { name: "Deprecated call, without action and extension)", @@ -185,7 +185,7 @@ func TestServer_Action(t *testing.T) { EnvId: "env", }, want: nil, - wantErr: "extension ID required", + wantErr: "invalid action", }, } for _, tt := range tests { diff --git a/pkg/extension/service/extension.go b/pkg/extension/service/extension.go index 6d9608ab6877acdc94bfcad0bd62e5fa21eef181..a92d16235642f9b63c7960952414bb427c985eb0 100644 --- a/pkg/extension/service/extension.go +++ b/pkg/extension/service/extension.go @@ -4,10 +4,8 @@ import ( "context" "fmt" - "git.perx.ru/perxis/perxis-go/pkg/action" "git.perx.ru/perxis/perxis-go/pkg/clients" "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/roles" "git.perx.ru/perxis/perxis-go/pkg/setup" @@ -32,6 +30,7 @@ type Extension struct { Content *content.Content Logger *zap.Logger manager extension.Manager + router extension.ActionRouter withClient bool role *roles.Role @@ -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) } -func (s *Extension) Action(ctx context.Context, in *extension.ActionRequest) (*extension.ActionResponse, error) { - actionURL, err := action.NewURL(in.Action) - if err != nil { - return nil, err - } - ext := actionURL.Extension() +// isCorrectExtension проверяет что расширение в url совпадает с расширением расширения +func (s *Extension) isCorrectExtension(ctx context.Context, url *extension.ActionURL, in *extension.ActionRequest) bool { + ext := url.Extension() if ext == "" { ext = in.Extension } - if ext == "" { - return nil, errors.New("extension ID required") - } - ok, err := extension.CheckInstalled(ctx, s.Content, in.SpaceId, in.EnvId, ext) - if err != nil { - return nil, errors.Wrap(err, "check extension installed") + + if in.Extension == s.desc.Extension { + return true } - if !ok { - return nil, errors.New("extension not installed") + 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) + } + } } - return &extension.ActionResponse{}, nil + return nil, extension.ErrInvalidAction }