Skip to content

Commit d8705f9

Browse files
authored
Add DELETE /v3/service_offerings/:guid (#3614)
* Add DELETE /v3/service_offerings/:guid * Implement delete handler and DeleteOffering in repo * Implement purging for Service Offerings - is purge is set to true, delete all related service plans, instances and bindings without contacting the service broker
1 parent 88f8ae5 commit d8705f9

File tree

10 files changed

+516
-8
lines changed

10 files changed

+516
-8
lines changed

api/handlers/fake/cfservice_offering_repository.go

Lines changed: 78 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/handlers/service_offering.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const (
2626
type CFServiceOfferingRepository interface {
2727
GetServiceOffering(context.Context, authorization.Info, string) (repositories.ServiceOfferingRecord, error)
2828
ListOfferings(context.Context, authorization.Info, repositories.ListServiceOfferingMessage) ([]repositories.ServiceOfferingRecord, error)
29+
DeleteOffering(context.Context, authorization.Info, repositories.DeleteServiceOfferingMessage) error
2930
}
3031

3132
type ServiceOffering struct {
@@ -103,6 +104,23 @@ func (h *ServiceOffering) list(r *http.Request) (*routing.Response, error) {
103104
return routing.NewResponse(http.StatusOK).WithBody(presenter.ForList(presenter.ForServiceOffering, serviceOfferingList, h.serverURL, *r.URL, includedResources...)), nil
104105
}
105106

107+
func (h *ServiceOffering) delete(r *http.Request) (*routing.Response, error) {
108+
authInfo, _ := authorization.InfoFromContext(r.Context())
109+
logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.service-offering.delete")
110+
111+
payload := new(payloads.ServiceOfferingDelete)
112+
if err := h.requestValidator.DecodeAndValidateURLValues(r, payload); err != nil {
113+
return nil, apierrors.LogAndReturn(logger, err, "Unable to decode request query parameters")
114+
}
115+
116+
serviceOfferingGUID := routing.URLParam(r, "guid")
117+
if err := h.serviceOfferingRepo.DeleteOffering(r.Context(), authInfo, payload.ToMessage(serviceOfferingGUID)); err != nil {
118+
return nil, apierrors.LogAndReturn(logger, err, "failed to delete service offering: %s", serviceOfferingGUID)
119+
}
120+
121+
return routing.NewResponse(http.StatusNoContent), nil
122+
}
123+
106124
func (h *ServiceOffering) UnauthenticatedRoutes() []routing.Route {
107125
return nil
108126
}
@@ -111,5 +129,6 @@ func (h *ServiceOffering) AuthenticatedRoutes() []routing.Route {
111129
return []routing.Route{
112130
{Method: "GET", Pattern: ServiceOfferingPath, Handler: h.get},
113131
{Method: "GET", Pattern: ServiceOfferingsPath, Handler: h.list},
132+
{Method: "DELETE", Pattern: ServiceOfferingPath, Handler: h.delete},
114133
}
115134
}

api/handlers/service_offering_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66
"net/http"
77

8+
apierrors "code.cloudfoundry.org/korifi/api/errors"
89
. "code.cloudfoundry.org/korifi/api/handlers"
910
"code.cloudfoundry.org/korifi/api/handlers/fake"
1011
"code.cloudfoundry.org/korifi/api/payloads"
@@ -271,4 +272,61 @@ var _ = Describe("ServiceOffering", func() {
271272
})
272273
})
273274
})
275+
276+
Describe("DELETE /v3/service_offerings/:guid", func() {
277+
JustBeforeEach(func() {
278+
req, err := http.NewRequestWithContext(ctx, "DELETE", "/v3/service_offerings/offering-guid", nil)
279+
Expect(err).NotTo(HaveOccurred())
280+
281+
routerBuilder.Build().ServeHTTP(rr, req)
282+
})
283+
284+
It("deletes the service offering", func() {
285+
Expect(serviceOfferingRepo.DeleteOfferingCallCount()).To(Equal(1))
286+
_, actualAuthInfo, actualDeleteMessage := serviceOfferingRepo.DeleteOfferingArgsForCall(0)
287+
Expect(actualAuthInfo).To(Equal(authInfo))
288+
Expect(actualDeleteMessage.GUID).To(Equal("offering-guid"))
289+
Expect(actualDeleteMessage.Purge).To(BeFalse())
290+
291+
Expect(rr).To(HaveHTTPStatus(http.StatusNoContent))
292+
})
293+
294+
When("deleting the service offering fails with not found", func() {
295+
BeforeEach(func() {
296+
serviceOfferingRepo.DeleteOfferingReturns(apierrors.NewNotFoundError(nil, repositories.ServiceOfferingResourceType))
297+
})
298+
299+
It("returns 404 Not Found", func() {
300+
expectNotFoundError("Service Offering")
301+
})
302+
})
303+
304+
When("deleting the service offering fails", func() {
305+
BeforeEach(func() {
306+
serviceOfferingRepo.DeleteOfferingReturns(errors.New("boom"))
307+
})
308+
309+
It("returns 500 Internal Server Error", func() {
310+
expectUnknownError()
311+
})
312+
})
313+
314+
When("purging is set to true", func() {
315+
BeforeEach(func() {
316+
requestValidator.DecodeAndValidateURLValuesStub = decodeAndValidateURLValuesStub(&payloads.ServiceOfferingDelete{
317+
Purge: true,
318+
})
319+
})
320+
321+
It("purges the service offering", func() {
322+
Expect(serviceOfferingRepo.DeleteOfferingCallCount()).To(Equal(1))
323+
_, actualAuthInfo, actualDeleteMessage := serviceOfferingRepo.DeleteOfferingArgsForCall(0)
324+
Expect(actualAuthInfo).To(Equal(authInfo))
325+
Expect(actualDeleteMessage.GUID).To(Equal("offering-guid"))
326+
Expect(actualDeleteMessage.Purge).To(BeTrue())
327+
328+
Expect(rr).To(HaveHTTPStatus(http.StatusNoContent))
329+
})
330+
})
331+
})
274332
})

api/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func main() {
244244
)
245245
metricsRepo := repositories.NewMetricsRepo(userClientFactory)
246246
serviceBrokerRepo := repositories.NewServiceBrokerRepo(userClientFactory, cfg.RootNamespace)
247-
serviceOfferingRepo := repositories.NewServiceOfferingRepo(userClientFactory, cfg.RootNamespace, serviceBrokerRepo)
247+
serviceOfferingRepo := repositories.NewServiceOfferingRepo(userClientFactory, cfg.RootNamespace, serviceBrokerRepo, nsPermissions)
248248
servicePlanRepo := repositories.NewServicePlanRepo(userClientFactory, cfg.RootNamespace, orgRepo)
249249

250250
processStats := actions.NewProcessStats(processRepo, appRepo, metricsRepo)

api/payloads/service_offering.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,27 @@ func (l *ServiceOfferingList) DecodeFromURLValues(values url.Values) error {
9595
l.IncludeResourceRules = append(l.IncludeResourceRules, params.ParseFields(values)...)
9696
return nil
9797
}
98+
99+
type ServiceOfferingDelete struct {
100+
Purge bool
101+
}
102+
103+
func (d *ServiceOfferingDelete) SupportedKeys() []string {
104+
return []string{"purge"}
105+
}
106+
107+
func (d *ServiceOfferingDelete) DecodeFromURLValues(values url.Values) error {
108+
var err error
109+
if d.Purge, err = getBool(values, "purge"); err != nil {
110+
return err
111+
}
112+
113+
return nil
114+
}
115+
116+
func (d *ServiceOfferingDelete) ToMessage(guid string) repositories.DeleteServiceOfferingMessage {
117+
return repositories.DeleteServiceOfferingMessage{
118+
GUID: guid,
119+
Purge: d.Purge,
120+
}
121+
}

api/payloads/service_offering_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,24 @@ var _ = Describe("ServiceOfferingList", func() {
7272
})
7373
})
7474
})
75+
76+
var _ = Describe("ServiceOfferingDelete", func() {
77+
DescribeTable("valid query",
78+
func(query string, expectedServiceOfferingDelete payloads.ServiceOfferingDelete) {
79+
actualServiceOfferingDelete, decodeErr := decodeQuery[payloads.ServiceOfferingDelete](query)
80+
81+
Expect(decodeErr).NotTo(HaveOccurred())
82+
Expect(*actualServiceOfferingDelete).To(Equal(expectedServiceOfferingDelete))
83+
},
84+
Entry("purge", "purge=true", payloads.ServiceOfferingDelete{Purge: true}),
85+
)
86+
87+
DescribeTable("invalid query",
88+
func(query string, expectedErrMsg string) {
89+
_, decodeErr := decodeQuery[payloads.ServiceOfferingDelete](query)
90+
Expect(decodeErr).To(HaveOccurred())
91+
},
92+
Entry("unsuported param", "foo=bar", "unsupported query parameter: foo"),
93+
Entry("invalid value for purge", "purge=foo", "invalid syntax"),
94+
)
95+
})

0 commit comments

Comments
 (0)