diff --git a/pkg/app/api/grpcapi/api.go b/pkg/app/api/grpcapi/api.go index c28caf3d95..eef8904f00 100644 --- a/pkg/app/api/grpcapi/api.go +++ b/pkg/app/api/grpcapi/api.go @@ -112,8 +112,51 @@ func (a *API) AddApplication(ctx context.Context, req *apiservice.AddApplication }, nil } -func (a *API) SyncApplication(ctx context.Context, _ *apiservice.SyncApplicationRequest) (*apiservice.SyncApplicationResponse, error) { - _, err := requireAPIKey(ctx, model.APIKey_READ_WRITE, a.logger) +func (a *API) SyncApplication(ctx context.Context, req *apiservice.SyncApplicationRequest) (*apiservice.SyncApplicationResponse, error) { + key, err := requireAPIKey(ctx, model.APIKey_READ_WRITE, a.logger) + if err != nil { + return nil, err + } + + app, err := getApplication(ctx, a.applicationStore, req.ApplicationId, a.logger) + if err != nil { + return nil, err + } + + if key.ProjectId != app.ProjectId { + return nil, status.Error(codes.InvalidArgument, "Requested application does not belong to your project") + } + + cmd := model.Command{ + Id: uuid.New().String(), + PipedId: app.PipedId, + ApplicationId: app.Id, + Type: model.Command_SYNC_APPLICATION, + Commander: key.Id, + SyncApplication: &model.Command_SyncApplication{ + ApplicationId: app.Id, + }, + } + if err := addCommand(ctx, a.commandStore, &cmd, a.logger); err != nil { + return nil, err + } + + return &apiservice.SyncApplicationResponse{ + CommandId: cmd.Id, + }, nil +} + +func (a *API) GetDeployment(ctx context.Context, _ *apiservice.GetDeploymentRequest) (*apiservice.GetDeploymentResponse, error) { + _, err := requireAPIKey(ctx, model.APIKey_READ_ONLY, a.logger) + if err != nil { + return nil, err + } + + return nil, status.Error(codes.Unimplemented, "") +} + +func (a *API) GetCommand(ctx context.Context, _ *apiservice.GetCommandRequest) (*apiservice.GetCommandResponse, error) { + _, err := requireAPIKey(ctx, model.APIKey_READ_ONLY, a.logger) if err != nil { return nil, err } diff --git a/pkg/app/api/grpcapi/utils.go b/pkg/app/api/grpcapi/utils.go index ea2d8511c1..8d0ef03b51 100644 --- a/pkg/app/api/grpcapi/utils.go +++ b/pkg/app/api/grpcapi/utils.go @@ -22,6 +22,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/pipe-cd/pipe/pkg/app/api/commandstore" "github.com/pipe-cd/pipe/pkg/datastore" "github.com/pipe-cd/pipe/pkg/git" "github.com/pipe-cd/pipe/pkg/model" @@ -40,6 +41,27 @@ func getPiped(ctx context.Context, store datastore.PipedStore, id string, logger return piped, nil } +func getApplication(ctx context.Context, store datastore.ApplicationStore, id string, logger *zap.Logger) (*model.Application, error) { + app, err := store.GetApplication(ctx, id) + if errors.Is(err, datastore.ErrNotFound) { + return nil, status.Error(codes.NotFound, "Application is not found") + } + if err != nil { + logger.Error("failed to get application", zap.Error(err)) + return nil, status.Error(codes.Internal, "Failed to get application") + } + + return app, nil +} + +func addCommand(ctx context.Context, store commandstore.Store, cmd *model.Command, logger *zap.Logger) error { + if err := store.AddCommand(ctx, cmd); err != nil { + logger.Error("failed to create command", zap.Error(err)) + return status.Error(codes.Internal, "Failed to create command") + } + return nil +} + // makeGitPath returns an ApplicationGitPath by adding Repository info and GitPath URL to given args. func makeGitPath(repoID, path, cfgFilename string, piped *model.Piped, logger *zap.Logger) (*model.ApplicationGitPath, error) { var repo *model.ApplicationGitRepository diff --git a/pkg/app/api/grpcapi/web_api.go b/pkg/app/api/grpcapi/web_api.go index 33e296cccf..41b3406bd2 100644 --- a/pkg/app/api/grpcapi/web_api.go +++ b/pkg/app/api/grpcapi/web_api.go @@ -526,41 +526,34 @@ func (a *WebAPI) SyncApplication(ctx context.Context, req *webservice.SyncApplic return nil, err } - app, err := a.getApplication(ctx, req.ApplicationId) + app, err := getApplication(ctx, a.applicationStore, req.ApplicationId, a.logger) if err != nil { return nil, err } - if err := a.validateAppBelongsToProject(ctx, req.ApplicationId, claims.Role.ProjectId); err != nil { - return nil, err + + if claims.Role.ProjectId != app.ProjectId { + return nil, status.Error(codes.InvalidArgument, "Requested application does not belong to your project") } - commandID := uuid.New().String() cmd := model.Command{ - Id: commandID, + Id: uuid.New().String(), PipedId: app.PipedId, ApplicationId: app.Id, Type: model.Command_SYNC_APPLICATION, Commander: claims.Subject, SyncApplication: &model.Command_SyncApplication{ - ApplicationId: req.ApplicationId, + ApplicationId: app.Id, }, } - if err := a.addCommand(ctx, &cmd); err != nil { + if err := addCommand(ctx, a.commandStore, &cmd, a.logger); err != nil { return nil, err } + return &webservice.SyncApplicationResponse{ - CommandId: commandID, + CommandId: cmd.Id, }, nil } -func (a *WebAPI) addCommand(ctx context.Context, cmd *model.Command) error { - if err := a.commandStore.AddCommand(ctx, cmd); err != nil { - a.logger.Error("failed to create command", zap.Error(err)) - return status.Error(codes.Internal, "Failed to create command") - } - return nil -} - func (a *WebAPI) GetApplication(ctx context.Context, req *webservice.GetApplicationRequest) (*webservice.GetApplicationResponse, error) { claims, err := rpcauth.ExtractClaims(ctx) if err != nil { @@ -849,7 +842,7 @@ func (a *WebAPI) CancelDeployment(ctx context.Context, req *webservice.CancelDep ForceNoRollback: req.ForceNoRollback, }, } - if err := a.addCommand(ctx, &cmd); err != nil { + if err := addCommand(ctx, a.commandStore, &cmd, a.logger); err != nil { return nil, err } return &webservice.CancelDeploymentResponse{ @@ -893,9 +886,10 @@ func (a *WebAPI) ApproveStage(ctx context.Context, req *webservice.ApproveStageR StageId: req.StageId, }, } - if err := a.addCommand(ctx, &cmd); err != nil { + if err := addCommand(ctx, a.commandStore, &cmd, a.logger); err != nil { return nil, err } + return &webservice.ApproveStageResponse{ CommandId: commandID, }, nil diff --git a/pkg/app/api/service/apiservice/service.proto b/pkg/app/api/service/apiservice/service.proto index 56274a39bb..70a9fe8d8f 100644 --- a/pkg/app/api/service/apiservice/service.proto +++ b/pkg/app/api/service/apiservice/service.proto @@ -20,12 +20,18 @@ option go_package = "github.com/pipe-cd/pipe/pkg/app/api/service/apiservice"; import "validate/validate.proto"; import "pkg/model/common.proto"; import "pkg/model/application.proto"; +import "pkg/model/deployment.proto"; +import "pkg/model/command.proto"; // APIService contains all RPC definitions for external service, pipectl. // All of these RPCs are authenticated by using API key. service APIService { rpc AddApplication(AddApplicationRequest) returns (AddApplicationResponse) {} rpc SyncApplication(SyncApplicationRequest) returns (SyncApplicationResponse) {} + + rpc GetDeployment(GetDeploymentRequest) returns (GetDeploymentResponse) {} + + rpc GetCommand(GetCommandRequest) returns (GetCommandResponse) {} } message AddApplicationRequest { @@ -48,3 +54,19 @@ message SyncApplicationRequest { message SyncApplicationResponse { string command_id = 1; } + +message GetDeploymentRequest { + string deployment_id = 1; +} + +message GetDeploymentResponse { + pipe.model.Deployment deployment = 1; +} + +message GetCommandRequest { + string command_id = 1 [(validate.rules).string.min_len = 1]; +} + +message GetCommandResponse { + pipe.model.Command command = 1; +}