diff --git a/CHANGES.md b/CHANGES.md index a3074e9..ff04c65 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ## ?.?.? +### New features + +* Environments can be deleted through the API. + ### Bugfixes and minor changes * Slack notifier is now less spammy. diff --git a/README.md b/README.md index 725e614..f3c7670 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ The command endpoint is for manipulating data. | `POST` | `/api/events/feature_toggled` | `{"feature":"feature1","environment":"staging","status":true}` | Toggle a feature. | | `POST` | `/api/events/feature_deleted` | `{"name":"feature1"}` | Delete a feature. | | `POST` | `/api/events/environments_ordered`| `{"order":["dev","staging"]}` | Change env display order. | +| `POST` | `/api/events/environment_deleted` | `{"name":"staging"}` | Delete an environment. | ## Client diff --git a/models/environment_deleted.go b/models/environment_deleted.go new file mode 100644 index 0000000..efcec5f --- /dev/null +++ b/models/environment_deleted.go @@ -0,0 +1,44 @@ +package models + +import ( + "errors" + "time" + + "github.com/MEDIGO/laika/notifier" +) + +type EnvironmentDeleted struct { + Name string `json:"name"` +} + +func (e *EnvironmentDeleted) Validate(s *State) (error, error) { + if s.getEnvByName(e.Name) == nil { + return errors.New("Bad environment"), nil + } + + return nil, nil +} + +func (e *EnvironmentDeleted) Update(s *State, t time.Time) *State { + state := *s + state.Environments = []Environment{} + + for _, env := range s.Environments { + if env.Name != e.Name { + state.Environments = append(state.Environments, env) + } + } + + for _, feature := range s.Features { + delete(state.Enabled, EnvFeature{e.Name, feature.Name}) + } + + return &state +} + +func (e *EnvironmentDeleted) PrePersist(*State) (Event, error) { + return e, nil +} + +func (*EnvironmentDeleted) Notify(*State, notifier.Notifier) { +} diff --git a/models/environment_deleted_test.go b/models/environment_deleted_test.go new file mode 100644 index 0000000..395e0fc --- /dev/null +++ b/models/environment_deleted_test.go @@ -0,0 +1,40 @@ +package models + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestEnvironmentDeleted(t *testing.T) { + var time time.Time + s := NewState() + + s = requireValid(t, s, &EnvironmentCreated{Name: "e1"}).Update(s, time) + s = requireValid(t, s, &EnvironmentCreated{Name: "e2"}).Update(s, time) + s = requireValid(t, s, &FeatureCreated{Name: "f1"}).Update(s, time) + s = requireValid(t, s, &FeatureCreated{Name: "f2"}).Update(s, time) + s = requireValid(t, s, &FeatureCreated{Name: "f3"}).Update(s, time) + s = requireValid(t, s, &FeatureToggled{Environment: "e1", Feature: "f2", Status: true}).Update(s, time) + + // successful deletion + s = requireValid(t, s, &EnvironmentDeleted{Name: "e1"}).Update(s, time) + + require.Len(t, s.Environments, 1) + require.Equal(t, "e2", s.Environments[0].Name) + + _, ok := s.Enabled[EnvFeature{Env: "e1", Feature: "f2"}] + require.False(t, ok) + + // non-existing feature is invalid + fd := &EnvironmentDeleted{Name: "e1"} + valErr, err := fd.Validate(s) + require.NoError(t, err) + require.Error(t, valErr) + + // non-existing environment should not cause harm + before := *s + s = fd.Update(s, time) + require.Equal(t, before, *s) +} diff --git a/models/events.go b/models/events.go index 32da1b4..574b21c 100644 --- a/models/events.go +++ b/models/events.go @@ -20,6 +20,7 @@ type Event interface { var types = map[string](func() Event){ "environment_created": func() Event { return &EnvironmentCreated{} }, + "environment_deleted": func() Event { return &EnvironmentDeleted{} }, "environments_ordered": func() Event { return &EnvironmentsOrdered{} }, "feature_created": func() Event { return &FeatureCreated{} }, "feature_toggled": func() Event { return &FeatureToggled{} },