Skip to content

Commit

Permalink
Fix deleting function that is used as authorizer (#474)
Browse files Browse the repository at this point in the history
  • Loading branch information
mthenw committed Jun 29, 2018
1 parent 4b892e7 commit 0dd9f87
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 2 deletions.
10 changes: 10 additions & 0 deletions function/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ func (e ErrFunctionValidation) Error() string {
return fmt.Sprintf("Function doesn't validate. Validation error: %s", e.Message)
}

// ErrFunctionIsAuthorizer occurs when function cannot be deleted because is used as authorizer.
type ErrFunctionIsAuthorizer struct {
ID ID
EventType string
}

func (e ErrFunctionIsAuthorizer) Error() string {
return fmt.Sprintf("Function %s cannot be deleted because is used as an authorizer for %s event type.", e.ID, e.EventType)
}

// ErrFunctionCallFailed occurs when function call failed.
type ErrFunctionCallFailed struct {
Original error
Expand Down
2 changes: 2 additions & 0 deletions httpapi/httpapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ func (h HTTPAPI) deleteFunction(w http.ResponseWriter, r *http.Request, params h
w.WriteHeader(http.StatusNotFound)
} else if _, ok := err.(*function.ErrFunctionHasSubscriptions); ok {
w.WriteHeader(http.StatusBadRequest)
} else if _, ok := err.(*function.ErrFunctionIsAuthorizer); ok {
w.WriteHeader(http.StatusBadRequest)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
Expand Down
11 changes: 11 additions & 0 deletions httpapi/httpapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,17 @@ func TestDeleteFunction(t *testing.T) {
assert.Equal(t, "Function cannot be deleted because it's subscribed to a least one event.", httpresp.Errors[0].Message)
})

t.Run("function is authorizer", func(t *testing.T) {
functions.EXPECT().DeleteFunction(gomock.Any(), gomock.Any()).Return(&function.ErrFunctionIsAuthorizer{ID: function.ID("func1"), EventType: "test.event"})

resp := request(router, http.MethodDelete, "/v1/spaces/default/functions/func1", nil)

httpresp := &httpapi.Response{}
json.Unmarshal(resp.Body.Bytes(), httpresp)
assert.Equal(t, http.StatusBadRequest, resp.Code)
assert.Equal(t, "Function func1 cannot be deleted because is used as an authorizer for test.event event type.", httpresp.Errors[0].Message)
})

t.Run("function not found", func(t *testing.T) {
functions.EXPECT().DeleteFunction(gomock.Any(), gomock.Any()).Return(&function.ErrFunctionNotFound{ID: function.ID("testid")})

Expand Down
10 changes: 10 additions & 0 deletions libkv/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ func (service Service) DeleteFunction(space string, id function.ID) error {
}
}

eventTypes, err := service.ListEventTypes(space)
if err != nil {
return err
}
for _, eventType := range eventTypes {
if id == *eventType.AuthorizerID {
return &function.ErrFunctionIsAuthorizer{ID: id, EventType: string(eventType.Name)}
}
}

err = service.FunctionStore.Delete(FunctionKey{space, id}.String())
if err != nil {
return &function.ErrFunctionNotFound{ID: id}
Expand Down
23 changes: 21 additions & 2 deletions libkv/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,11 @@ func TestDeleteFunction(t *testing.T) {
kvs := []*store.KVPair{}
subscriptionsDB := mock.NewMockStore(ctrl)
subscriptionsDB.EXPECT().List("default/", &store.ReadOptions{Consistent: true}).Return(kvs, nil)
eventTypesDB := mock.NewMockStore(ctrl)
eventTypesDB.EXPECT().List("default/", &store.ReadOptions{Consistent: true}).Return(kvs, nil)
functionsDB := mock.NewMockStore(ctrl)
functionsDB.EXPECT().Delete("default/testid").Return(nil)
service := &Service{FunctionStore: functionsDB, SubscriptionStore: subscriptionsDB, Log: zap.NewNop()}
service := &Service{FunctionStore: functionsDB, SubscriptionStore: subscriptionsDB, EventTypeStore: eventTypesDB, Log: zap.NewNop()}

err := service.DeleteFunction("default", function.ID("testid"))

Expand All @@ -236,9 +238,11 @@ func TestDeleteFunction(t *testing.T) {
kvs := []*store.KVPair{}
subscriptionsDB := mock.NewMockStore(ctrl)
subscriptionsDB.EXPECT().List("default/", &store.ReadOptions{Consistent: true}).Return(kvs, nil)
eventTypesDB := mock.NewMockStore(ctrl)
eventTypesDB.EXPECT().List("default/", &store.ReadOptions{Consistent: true}).Return(kvs, nil)
functionsDB := mock.NewMockStore(ctrl)
functionsDB.EXPECT().Delete("default/testid").Return(errors.New("KV func not found"))
service := &Service{FunctionStore: functionsDB, SubscriptionStore: subscriptionsDB, Log: zap.NewNop()}
service := &Service{FunctionStore: functionsDB, SubscriptionStore: subscriptionsDB, EventTypeStore: eventTypesDB, Log: zap.NewNop()}

err := service.DeleteFunction("default", function.ID("testid"))

Expand All @@ -257,6 +261,21 @@ func TestDeleteFunction(t *testing.T) {

assert.Equal(t, err, &function.ErrFunctionHasSubscriptions{})
})

t.Run("function is authorizer", func(t *testing.T) {
kvs := []*store.KVPair{
{Value: []byte(`{"name":"test.event","authorizerId":"testid"}`)}}
eventTypesDB := mock.NewMockStore(ctrl)
eventTypesDB.EXPECT().List("default/", &store.ReadOptions{Consistent: true}).Return(kvs, nil)
subscriptionsDB := mock.NewMockStore(ctrl)
subscriptionsDB.EXPECT().List("default/", &store.ReadOptions{Consistent: true}).Return([]*store.KVPair{}, nil)
functionsDB := mock.NewMockStore(ctrl)
service := &Service{FunctionStore: functionsDB, SubscriptionStore: subscriptionsDB, EventTypeStore: eventTypesDB, Log: zap.NewNop()}

err := service.DeleteFunction("default", function.ID("testid"))

assert.Equal(t, err, &function.ErrFunctionIsAuthorizer{ID: function.ID("testid"), EventType: "test.event"})
})
}

func TestValidateFunction(t *testing.T) {
Expand Down

0 comments on commit 0dd9f87

Please sign in to comment.