diff --git a/integration-tests/registry_integration_test.go b/integration-tests/registry_integration_test.go index d84a5c1..7675c9e 100644 --- a/integration-tests/registry_integration_test.go +++ b/integration-tests/registry_integration_test.go @@ -350,7 +350,6 @@ func TestRegistry(t *testing.T) { assert.Equal(t, githubUrl, *createNodeResponse.(drip.CreateNode201JSONResponse).Repository) assert.Equal(t, drip.NodeStatusActive, *createNodeResponse.(drip.CreateNode201JSONResponse).Status) real_node_id = createNodeResponse.(drip.CreateNode201JSONResponse).Id - }) t.Run("Get Node", func(t *testing.T) { @@ -577,6 +576,35 @@ func TestRegistry(t *testing.T) { assert.Equal(t, http.StatusBadRequest, err.(*echo.HTTPError).Code, "should return 400 bad request") }) + t.Run("Create Node Version with invalid node id", func(t *testing.T) { + for _, suffix := range []string{"LOWERCASEONLY", "invalidCharacter&"} { + nodeId := nodeId + suffix + res, err := withMiddleware(authz, impl.PublishNodeVersion)(ctx, drip.PublishNodeVersionRequestObject{ + PublisherId: publisherId, + NodeId: nodeId, + Body: &drip.PublishNodeVersionJSONRequestBody{ + Node: drip.Node{ + Id: &nodeId, + Description: &nodeDescription, + Author: &nodeAuthor, + License: &nodeLicense, + Name: &nodeName, + Tags: &nodeTags, + Repository: &source_code_repo, + }, + NodeVersion: drip.NodeVersion{ + Version: &nodeVersionLiteral, + Changelog: &changelog, + Dependencies: &dependencies, + }, + PersonalAccessToken: *createPersonalAccessTokenResponse.(drip.CreatePersonalAccessToken201JSONResponse).Token, + }, + }) + require.NoError(t, err) + require.IsType(t, drip.PublishNodeVersion400JSONResponse{}, res) + } + }) + t.Run("Create Node Version", func(t *testing.T) { mockStorageService.On("GenerateSignedURL", mock.Anything, mock.Anything).Return("test-url", nil) mockStorageService.On("GetFileUrl", mock.Anything, mock.Anything, mock.Anything).Return("test-url", nil) diff --git a/mapper/error.go b/mapper/error.go new file mode 100644 index 0000000..2fc3faf --- /dev/null +++ b/mapper/error.go @@ -0,0 +1,41 @@ +package mapper + +import "errors" + +type errCode uint8 + +const ( + errCodeUnknown errCode = iota + errCodeBadRequest +) + +type codedError struct { + code errCode + msg string + err []error +} + +func (e codedError) Error() string { + return e.msg +} + +func (e codedError) Unwrap() []error { + return e.err +} + +func isCodedError(err error, code errCode) bool { + var e codedError + if !errors.As(err, &e) { + return false + } + + return e.code == code +} + +func NewErrorBadRequest(msg string, err ...error) error { + return codedError{code: errCodeBadRequest, msg: msg, err: err} +} + +func IsErrorBadRequest(err error) (y bool) { + return isCodedError(err, errCodeBadRequest) +} diff --git a/mapper/node.go b/mapper/node.go index 6bff70e..1a5aecf 100644 --- a/mapper/node.go +++ b/mapper/node.go @@ -1,7 +1,6 @@ package mapper import ( - "fmt" "regexp" "registry-backend/drip" "registry-backend/ent" @@ -77,16 +76,16 @@ func ApiUpdateNodeToUpdateFields(nodeID string, node *drip.Node, client *ent.Cli func ValidateNode(node *drip.Node) error { if node.Id != nil { if len(*node.Id) > 100 { - return fmt.Errorf("node id is too long") + return NewErrorBadRequest("node id is too long") } - isValid, err := IsValidNodeID(*node.Id) + isValid, msg := IsValidNodeID(*node.Id) if !isValid { - return fmt.Errorf(err) + return NewErrorBadRequest(msg) } } if node.Description != nil { if len(*node.Description) > 1000 { - return fmt.Errorf("description is too long") + return NewErrorBadRequest("description is too long") } } return nil diff --git a/server/implementation/registry.go b/server/implementation/registry.go index 1efc969..ba60887 100644 --- a/server/implementation/registry.go +++ b/server/implementation/registry.go @@ -200,7 +200,7 @@ func (s *DripStrictServerImplementation) CreateNode( log.Ctx(ctx).Info().Msgf("CreateNode called with publisher ID: %s", request.PublisherId) node, err := s.RegistryService.CreateNode(ctx, s.Client, request.PublisherId, request.Body) - if ent.IsConstraintError(err) { + if mapper.IsErrorBadRequest(err) || ent.IsConstraintError(err) { log.Ctx(ctx).Error().Msgf( "Failed to create node for publisher ID %s w/ err: %v", request.PublisherId, err) return drip.CreateNode400JSONResponse{Message: "The node already exists", Error: err.Error()}, err @@ -473,6 +473,10 @@ func (s *DripStrictServerImplementation) PublishNodeVersion( return drip.PublishNodeVersion500JSONResponse{}, err } else if err != nil { node, err = s.RegistryService.CreateNode(ctx, s.Client, request.PublisherId, &request.Body.Node) + if mapper.IsErrorBadRequest(err) || ent.IsConstraintError(err) { + log.Ctx(ctx).Error().Msgf("Node creation failed w/ err: %v", err) + return drip.PublishNodeVersion400JSONResponse{Message: "Failed to create node", Error: err.Error()}, nil + } if err != nil { log.Ctx(ctx).Error().Msgf("Node creation failed w/ err: %v", err) return drip.PublishNodeVersion500JSONResponse{Message: "Failed to create node", Error: err.Error()}, nil diff --git a/server/server.go b/server/server.go index f61e5f5..10e1a01 100644 --- a/server/server.go +++ b/server/server.go @@ -89,7 +89,7 @@ func (s *Server) Start() error { } slackService := gateway.NewSlackService(s.Config) - algoliaService, err := algolia.NewFromEnv() + algoliaService, err := algolia.NewFromEnvOrNoop() if err != nil { return err }