Skip to content

Commit

Permalink
Implement server errors and add validation errors (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
goncalo-rodrigues authored Mar 24, 2022
1 parent d3db15b commit 6b7c627
Show file tree
Hide file tree
Showing 12 changed files with 286 additions and 29 deletions.
6 changes: 5 additions & 1 deletion api/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/multycloud/multy/api/converter"
"github.com/multycloud/multy/api/errors"
"github.com/multycloud/multy/api/proto/common"
"github.com/multycloud/multy/api/proto/config"
"github.com/multycloud/multy/api/proto/resources"
Expand Down Expand Up @@ -146,7 +147,10 @@ func Translate(c *config.Config, prev *config.Resource, curr *config.Resource) (
Providers: provider,
}

hclOutput := encoder.Encode(&decodedResources)
hclOutput, errs := encoder.Encode(&decodedResources)
if errs != nil {
return hclOutput, errors.ValidationErrors(errs)
}

return hclOutput, nil
}
Expand Down
47 changes: 47 additions & 0 deletions api/errors/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package errors

import (
"fmt"
pberr "github.com/multycloud/multy/api/proto/errors"
"github.com/multycloud/multy/util"
"github.com/multycloud/multy/validate"
"google.golang.org/genproto/googleapis/rpc/code"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/anypb"
)

func PermissionDenied(msg string) error {
return status.ErrorProto(&spb.Status{
Code: int32(code.Code_PERMISSION_DENIED),
Message: msg,
Details: nil,
})
}

func InternalServerError(err error) error {
if _, ok := status.FromError(err); ok {
return err
}

return status.ErrorProto(&spb.Status{
Code: int32(code.Code_INTERNAL),
Message: err.Error(),
Details: nil,
})
}

func ValidationErrors(errs []validate.ValidationError) error {
return status.ErrorProto(&spb.Status{
Code: int32(code.Code_INVALID_ARGUMENT),
Message: fmt.Sprintf("%d validation errors found", len(errs)),
Details: util.MapSliceValues(errs, func(e validate.ValidationError) *anypb.Any {
a, _ := anypb.New(&pberr.ResourceValidationError{
ResourceId: e.ResourceId,
ErrorMessage: e.ErrorMessage,
FieldName: e.FieldName,
})
return a
}),
})
}
170 changes: 170 additions & 0 deletions api/proto/errors/errors.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions api/proto/errors/errors.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
syntax = "proto3";

option go_package = "github.com/multycloud/multy/api/proto/errors";
option java_multiple_files = true;
option java_package = "dev.multy.api.errors";
option java_outer_classname = "MultyProto";

package dev.multy.config;

message ResourceValidationError {
string resource_id = 1;
string error_message = 2;
// this is tentative, it might not be populated or might have a different name from the proto request
string field_name = 3;
}
28 changes: 27 additions & 1 deletion api/services/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"github.com/multycloud/multy/api/converter"
"github.com/multycloud/multy/api/deploy"
"github.com/multycloud/multy/api/errors"
"github.com/multycloud/multy/api/proto/common"
"github.com/multycloud/multy/api/proto/config"
"github.com/multycloud/multy/api/proto/resources"
Expand Down Expand Up @@ -33,15 +34,28 @@ type Service[Arg proto.Message, OutT proto.Message] struct {
Converters converter.ResourceConverters[Arg, OutT]
}

func WrappingErrors[InT any, OutT any](f func(context.Context, InT) (OutT, error)) func(context.Context, InT) (OutT, error) {
return func(ctx context.Context, in InT) (OutT, error) {
out, err := f(ctx, in)
if err != nil {
return out, errors.InternalServerError(err)
}
return out, nil
}
}

func (s Service[Arg, OutT]) Create(ctx context.Context, in CreateRequest[Arg]) (OutT, error) {
return WrappingErrors(s.create)(ctx, in)
}

func (s Service[Arg, OutT]) create(ctx context.Context, in CreateRequest[Arg]) (OutT, error) {
fmt.Println("Service create")
userId, err := util.ExtractUserId(ctx)
if err != nil {
return *new(OutT), err
}
c, err := s.Db.LoadUserConfig(userId)
if err != nil {

return *new(OutT), err
}
resource, err := util.InsertIntoConfig(in.GetResources(), c)
Expand All @@ -63,6 +77,10 @@ func (s Service[Arg, OutT]) Create(ctx context.Context, in CreateRequest[Arg]) (
}

func (s Service[Arg, OutT]) Read(ctx context.Context, in WithResourceId) (OutT, error) {
return WrappingErrors(s.read)(ctx, in)
}

func (s Service[Arg, OutT]) read(ctx context.Context, in WithResourceId) (OutT, error) {
fmt.Printf("Service read: %s\n", in.GetResourceId())
userId, err := util.ExtractUserId(ctx)
if err != nil {
Expand Down Expand Up @@ -95,6 +113,10 @@ func (s Service[Arg, OutT]) Read(ctx context.Context, in WithResourceId) (OutT,
}

func (s Service[Arg, OutT]) Update(ctx context.Context, in UpdateRequest[Arg]) (OutT, error) {
return WrappingErrors(s.update)(ctx, in)
}

func (s Service[Arg, OutT]) update(ctx context.Context, in UpdateRequest[Arg]) (OutT, error) {
fmt.Printf("Service update: %s\n", in.GetResourceId())
userId, err := util.ExtractUserId(ctx)
if err != nil {
Expand Down Expand Up @@ -123,6 +145,10 @@ func (s Service[Arg, OutT]) Update(ctx context.Context, in UpdateRequest[Arg]) (
}

func (s Service[Arg, OutT]) Delete(ctx context.Context, in WithResourceId) (*common.Empty, error) {
return WrappingErrors(s.delete)(ctx, in)
}

func (s Service[Arg, OutT]) delete(ctx context.Context, in WithResourceId) (*common.Empty, error) {
fmt.Printf("Service delete: %s\n", in.GetResourceId())
userId, err := util.ExtractUserId(ctx)
if err != nil {
Expand Down
20 changes: 4 additions & 16 deletions api/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"encoding/base64"
"fmt"
"github.com/google/uuid"
"github.com/multycloud/multy/api/errors"
"github.com/multycloud/multy/api/proto/common"
"github.com/multycloud/multy/api/proto/config"
"golang.org/x/exp/slices"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"strings"
)

func ConvertCommonParams(parameters *common.CloudSpecificResourceCommonArgs) *common.CloudSpecificCommonResourceParameters {
Expand All @@ -25,29 +27,15 @@ func ConvertCommonParams(parameters *common.CloudSpecificResourceCommonArgs) *co
func ExtractUserId(ctx context.Context) (string, error) {
md, _ := metadata.FromIncomingContext(ctx)
userIds := md.Get("user_id")
// fix me why
userIds = removeDuplicateStr(userIds)
if len(userIds) == 0 {
return "", fmt.Errorf("user id must be set")
return "", errors.PermissionDenied(fmt.Sprintf("user id must be set"))
}
if len(userIds) > 1 {
return "", fmt.Errorf("only expected 1 user id, found %d", len(userIds))
return "", errors.PermissionDenied(fmt.Sprintf("only expected 1 user id, found %d: %s", len(userIds), strings.Join(userIds, ", ")))
}
return userIds[0], nil
}

func removeDuplicateStr(strSlice []string) []string {
allKeys := make(map[string]bool)
list := []string{}
for _, item := range strSlice {
if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
}
}
return list
}

func InsertIntoConfig[Arg proto.Message](in []Arg, c *config.Config) (*config.Resource, error) {
args, err := convert(in)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion cli/check_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/multycloud/multy/encoder"
"github.com/multycloud/multy/parser"
"github.com/multycloud/multy/resources"
"github.com/multycloud/multy/validate"
flag "github.com/spf13/pflag"
"io/ioutil"
"log"
Expand Down Expand Up @@ -54,7 +55,10 @@ func (c *CheckCommand) Execute(ctx context.Context) error {
r := decoder.Decode(parsedConfig)
mctx := resources.MultyContext{Resources: r.Resources, Location: r.GlobalConfig.Location}

_ = encoder.TranslateResources(r, mctx)
_, errs := encoder.TranslateResources(r, mctx)
if errs != nil {
validate.PrintAllAndExit(errs)
}

fmt.Println("no validation errors found")
return nil
Expand Down
Loading

0 comments on commit 6b7c627

Please sign in to comment.