Skip to content

Commit

Permalink
Add DELETE support
Browse files Browse the repository at this point in the history
This patch adds support for the `DELETE` HTTP method. The adapter will now
accept the `DELETE` method and call the `Delete` method defined in the
`DeleteHandler` interface. For example, a simple implementation that
stores the object in a map in memory could look like this:

```go
func (h *MyHandler) Delete(ctx context.Context, request *AddRequest) (respone *AddResponse, err error) {
        h.storeLock.Lock()
        defer h.storeLock.Unlock()
        delete(h.storeMap, request.Variables[0])
        return
}
```

Signed-off-by: Juan Hernandez <[email protected]>
  • Loading branch information
jhernand committed Feb 21, 2024
1 parent b2f7743 commit d9002bb
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 1 deletion.
46 changes: 45 additions & 1 deletion internal/service/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Adapter struct {
listHandler ListHandler
getHandler GetHandler
addHandler AddHandler
deleteHandler DeleteHandler
includeFields []search.Path
excludeFields []search.Path
pathsParser *search.PathsParser
Expand Down Expand Up @@ -120,7 +121,8 @@ func (b *AdapterBuilder) Build() (result *Adapter, err error) {
listHandler, _ := b.handler.(ListHandler)
getHandler, _ := b.handler.(GetHandler)
addHandler, _ := b.handler.(AddHandler)
if listHandler == nil && getHandler == nil && addHandler == nil {
deleteHandler, _ := b.handler.(DeleteHandler)
if listHandler == nil && getHandler == nil && addHandler == nil && deleteHandler == nil {
err = errors.New("handler doesn't implement any of the handler interfaces")
return
}
Expand Down Expand Up @@ -200,6 +202,7 @@ func (b *AdapterBuilder) Build() (result *Adapter, err error) {
listHandler: listHandler,
getHandler: getHandler,
addHandler: addHandler,
deleteHandler: deleteHandler,
includeFields: includePaths,
excludeFields: excludePaths,
selectorParser: selectorParser,
Expand All @@ -224,6 +227,8 @@ func (a *Adapter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.serveGetMethod(w, r, pathVariables)
case http.MethodPost:
a.servePostMethod(w, r, pathVariables)
case http.MethodDelete:
a.serveDeleteMethod(w, r, pathVariables)
default:
SendError(w, http.StatusMethodNotAllowed, "Method '%s' is not allowed", r.Method)
}
Expand Down Expand Up @@ -261,6 +266,17 @@ func (a *Adapter) servePostMethod(w http.ResponseWriter, r *http.Request, pathVa
a.serveAdd(w, r, pathVariables)
}

func (a *Adapter) serveDeleteMethod(w http.ResponseWriter, r *http.Request, pathVariables []string) {
// Check that we have a compatible handler:
if a.deleteHandler == nil {
SendError(w, http.StatusMethodNotAllowed, "Method '%s' is not allowed", r.Method)
return
}

// Call the handler:
a.serveDelete(w, r, pathVariables)
}

func (a *Adapter) serveGet(w http.ResponseWriter, r *http.Request, pathVariables []string) {
// Get the context:
ctx := r.Context()
Expand Down Expand Up @@ -449,6 +465,34 @@ func (a *Adapter) serveAdd(w http.ResponseWriter, r *http.Request, pathVariables
a.sendObject(ctx, w, response.Object)
}

func (a *Adapter) serveDelete(w http.ResponseWriter, r *http.Request, pathVariables []string) {
// Get the context:
ctx := r.Context()

// Create the request:
request := &DeleteRequest{
Variables: pathVariables,
}

// Call the handler:
_, err := a.deleteHandler.Delete(ctx, request)
if err != nil {
a.logger.Error(
"Failed to delete item",
"error", err,
)
SendError(
w,
http.StatusInternalServerError,
"Failed to delete item",
)
return
}

// Send the result:
w.WriteHeader(http.StatusNoContent)
}

// extractSelector tries to extract the selector from the request. It return the selector and a
// flag indicating if it is okay to continue processing the request. When this flag is false the
// error response was already sent to the client, and request processing should stop.
Expand Down
35 changes: 35 additions & 0 deletions internal/service/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,41 @@ var _ = Describe("Adapter", func() {
})
})

Describe("Object deletion", func() {
It("Deletes an object", func() {
// Prepare the handler:
body := func(ctx context.Context,
request *DeleteRequest) (response *DeleteResponse, err error) {
response = &DeleteResponse{}
return
}
handler := NewMockDeleteHandler(ctrl)
handler.EXPECT().Delete(gomock.Any(), gomock.Any()).DoAndReturn(body)

// Create the adapter:
adapter, err := NewAdapter().
SetLogger(logger).
SetPathVariables("id").
SetHandler(handler).
Build()
Expect(err).ToNot(HaveOccurred())
router := mux.NewRouter()
router.Handle("/mycollection/{id}", adapter)

// Send the request:
request := httptest.NewRequest(
http.MethodDelete,
"/mycollection/123",
nil,
)
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)

// Verify the response:
Expect(recorder.Code).To(Equal(http.StatusNoContent))
})
})

DescribeTable(
"JSON generation",
func(items data.Stream, expected string) {
Expand Down
27 changes: 27 additions & 0 deletions internal/service/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,37 @@ type AddHandler interface {
Add(ctx context.Context, request *AddRequest) (response *AddResponse, err error)
}

// DeleteRequest represents a request to delete an object from a collection.
type DeleteRequest struct {
// Variables contains the values of the path variables. For example, if the request path is
// like this:
//
// /o2ims-infrastructureInventory/v1/resourcePools/123/resources/456
//
// Then it will contain '456' and '123'.
//
// These path variables are ordered from more specific to less specific, the opposite of
// what appears in the request path. This is intended to simplify things because most
// handlers will only be interested in the most specific identifier and therefore they
// can just use index zero.
Variables []string
}

// DeleteResponse represents the response to the request to delete an object from a collection.
type DeleteResponse struct {
}

// DeleteHandler is the interface implemented by objects that know how delete items from a
// collection of objects.
type DeleteHandler interface {
Delete(ctx context.Context, request *DeleteRequest) (response *DeleteResponse, err error)
}

// Handler aggregates all the other specific handlers. This is intended for unit/ tests, where it
// is convenient to have a single mock that implements all the operations.
type Handler interface {
ListHandler
GetHandler
AddHandler
DeleteHandler
}
53 changes: 53 additions & 0 deletions internal/service/handlers_mock.go

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

0 comments on commit d9002bb

Please sign in to comment.