Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions api/handlers/default_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package handlers

import (
"net/http"

"github.com/gorilla/mux"
)

func NotFound(w http.ResponseWriter, r *http.Request) {
writeJSONError(w, http.StatusNotFound, mux.ErrNotFound.Error())
}

func MethodNotAllowed(w http.ResponseWriter, r *http.Request) {
writeJSONError(w, http.StatusMethodNotAllowed, mux.ErrMethodMismatch.Error())
}
43 changes: 43 additions & 0 deletions api/handlers/default_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package handlers_test

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/odpf/columbus/api/handlers"
)

func TestNotFoundHandler(t *testing.T) {
handler := http.HandlerFunc(handlers.NotFound)
rr := httptest.NewRequest("GET", "/xxx", nil)
rw := httptest.NewRecorder()
handler.ServeHTTP(rw, rr)
if rw.Code != 404 {
t.Errorf("expected handler to respond with HTTP 404, got HTTP %d instead", rw.Code)
return
}
expectedResponse := "{\"reason\":\"no matching route was found\"}\n"
actualResponse := rw.Body.String()
if actualResponse != expectedResponse {
t.Errorf("expected handler response to be %q, was %q instead", expectedResponse, actualResponse)
return
}
}

func TestMethodNotAllowedHandler(t *testing.T) {
handler := http.HandlerFunc(handlers.MethodNotAllowed)
rr := httptest.NewRequest("POST", "/ping", nil)
rw := httptest.NewRecorder()
handler.ServeHTTP(rw, rr)
if rw.Code != 405 {
t.Errorf("expected handler to respond with HTTP 405, got HTTP %d instead", rw.Code)
return
}
expectedResponse := "{\"reason\":\"method is not allowed\"}\n"
actualResponse := rw.Body.String()
if actualResponse != expectedResponse {
t.Errorf("expected handler response to be %q, was %q instead", expectedResponse, actualResponse)
return
}
}
1 change: 0 additions & 1 deletion api/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ func decodeURLMiddleware(logger logrus.FieldLogger) mux.MiddlewareFunc {

newVars[key] = decodedVal
}

r = mux.SetURLVars(r, newVars)
h.ServeHTTP(rw, r)
})
Expand Down
98 changes: 33 additions & 65 deletions api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ type Config struct {
LineageProvider handlers.LineageProvider
}

func RegisterRoutes(router *mux.Router, config Config) {
// By default mux will decode url and then match the decoded url against the route
// we reverse the steps by telling mux to use encoded path to match the url
// then we manually decode via custom middleware (decodeURLMiddleware).
//
// This is to allow urn that has "/" to be matched correctly to the route
router.UseEncodedPath()
router.Use(decodeURLMiddleware(config.Logger))
type Handlers struct {
Type *handlers.TypeHandler
Record *handlers.RecordHandler
Search *handlers.SearchHandler
Lineage *handlers.LineageHandler
Tag *handlers.TagHandler
TagTemplate *handlers.TagTemplateHandler
}

func initHandlers(config Config) *Handlers {
typeHandler := handlers.NewTypeHandler(
config.Logger.WithField("reporter", "type-handler"),
config.TypeRepository,
Expand Down Expand Up @@ -58,68 +59,35 @@ func RegisterRoutes(router *mux.Router, config Config) {
config.TagTemplateService,
)

router.PathPrefix("/ping").Handler(handlers.NewHeartbeatHandler())
setupV1TypeRoutes(router, typeHandler, recordHandler)
setupV1TagRoutes(router, "/v1/tags", tagHandler, tagTemplateHandler)

router.Path("/v1/search").
Methods(http.MethodGet).
HandlerFunc(searchHandler.Search)

router.Path("/v1/search/suggest").
Methods(http.MethodGet).
HandlerFunc(searchHandler.Suggest)

router.PathPrefix("/v1/lineage/{type}/{id}").
Methods(http.MethodGet).
HandlerFunc(lineageHandler.GetLineage)

router.PathPrefix("/v1/lineage").
Methods(http.MethodGet).
HandlerFunc(lineageHandler.ListLineage)
return &Handlers{
Type: typeHandler,
Record: recordHandler,
Search: searchHandler,
Lineage: lineageHandler,
Tag: tagHandler,
TagTemplate: tagTemplateHandler,
}
}

func setupV1TypeRoutes(router *mux.Router, th *handlers.TypeHandler, rh *handlers.RecordHandler) {
typeURL := "/v1/types"

router.Path(typeURL).
Methods(http.MethodGet, http.MethodHead).
HandlerFunc(th.Get)

recordURL := "/v1/types/{name}/records"
router.Path(recordURL).
Methods(http.MethodPut, http.MethodHead).
HandlerFunc(rh.UpsertBulk)

router.Path(recordURL).
Methods(http.MethodGet, http.MethodHead).
HandlerFunc(rh.GetByType)

router.Path(recordURL+"/{id}").
Methods(http.MethodGet, http.MethodHead).
HandlerFunc(rh.GetOneByType)

router.Path(recordURL+"/{id}").
Methods(http.MethodDelete, http.MethodHead).
HandlerFunc(rh.Delete)

}
func RegisterRoutes(router *mux.Router, config Config) {
// By default mux will decode url and then match the decoded url against the route
// we reverse the steps by telling mux to use encoded path to match the url
// then we manually decode via custom middleware (decodeURLMiddleware).
//
// This is to allow urn that has "/" to be matched correctly to the route
router.UseEncodedPath()
router.Use(decodeURLMiddleware(config.Logger))

func setupV1TagRoutes(router *mux.Router, baseURL string, th *handlers.TagHandler, tth *handlers.TagTemplateHandler) {
router.Methods(http.MethodPost).Path(baseURL).HandlerFunc(th.Create)
handlerCollection := initHandlers(config)

url := baseURL + "/types/{type}/records/{record_urn}/templates/{template_urn}"
router.Methods(http.MethodGet).Path(url).HandlerFunc(th.FindByRecordAndTemplate)
router.Methods(http.MethodPut).Path(url).HandlerFunc(th.Update)
router.Methods(http.MethodDelete).Path(url).HandlerFunc(th.Delete)
router.PathPrefix("/ping").Handler(handlers.NewHeartbeatHandler())

router.Methods(http.MethodGet).Path(baseURL + "/types/{type}/records/{record_urn}").HandlerFunc(th.GetByRecord)
v1Beta1SubRouter := router.PathPrefix("/v1beta1").Subrouter()
setupV1Beta1Router(v1Beta1SubRouter, handlerCollection)

templateURL := baseURL + "/templates"
router.Methods(http.MethodGet).Path(templateURL).HandlerFunc(tth.Index)
router.Methods(http.MethodPost).Path(templateURL).HandlerFunc(tth.Create)
router.Methods(http.MethodGet).Path(templateURL + "/{template_urn}").HandlerFunc(tth.Find)
router.Methods(http.MethodPut).Path(templateURL + "/{template_urn}").HandlerFunc(tth.Update)
router.Methods(http.MethodDelete).Path(templateURL + "/{template_urn}").HandlerFunc(tth.Delete)
v1SubRouter := router.PathPrefix("/v1").Subrouter()
setupV1Beta1Router(v1SubRouter, handlerCollection)

router.NotFoundHandler = http.HandlerFunc(handlers.NotFound)
router.MethodNotAllowedHandler = http.HandlerFunc(handlers.MethodNotAllowed)
}
76 changes: 76 additions & 0 deletions api/v1beta1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package api

import (
"net/http"

"github.com/gorilla/mux"
"github.com/odpf/columbus/api/handlers"
)

func setupV1Beta1Router(router *mux.Router, handlers *Handlers) *mux.Router {
setupV1Beta1TypeRoutes(router, handlers.Type, handlers.Record)
setupV1Beta1TagRoutes(router, "/tags", handlers.Tag, handlers.TagTemplate)

router.Path("/search").
Methods(http.MethodGet).
HandlerFunc(handlers.Search.Search)

router.Path("/search/suggest").
Methods(http.MethodGet).
HandlerFunc(handlers.Search.Suggest)

router.PathPrefix("/lineage/{type}/{id}").
Methods(http.MethodGet).
HandlerFunc(handlers.Lineage.GetLineage)

router.PathPrefix("/lineage").
Methods(http.MethodGet).
HandlerFunc(handlers.Lineage.ListLineage)

return router
}

func setupV1Beta1TypeRoutes(router *mux.Router, th *handlers.TypeHandler, rh *handlers.RecordHandler) {
typeURL := "/types"

router.Path(typeURL).
Methods(http.MethodGet, http.MethodHead).
HandlerFunc(th.Get)

recordURL := "/types/{name}/records"
router.Path(recordURL).
Methods(http.MethodPut, http.MethodHead).
HandlerFunc(rh.UpsertBulk)

router.Path(recordURL).
Methods(http.MethodGet, http.MethodHead).
HandlerFunc(rh.GetByType)

router.Path(recordURL+"/{id}").
Methods(http.MethodGet, http.MethodHead).
HandlerFunc(rh.GetOneByType)

router.Path(recordURL+"/{id}").
Methods(http.MethodDelete, http.MethodHead).
HandlerFunc(rh.Delete)

}

func setupV1Beta1TagRoutes(router *mux.Router, baseURL string, th *handlers.TagHandler, tth *handlers.TagTemplateHandler) {
router.Methods(http.MethodPost).Path(baseURL).HandlerFunc(th.Create)

url := baseURL + "/types/{type}/records/{record_urn}/templates/{template_urn}"
router.Methods(http.MethodGet).Path(url).HandlerFunc(th.FindByRecordAndTemplate)
router.Methods(http.MethodPut).Path(url).HandlerFunc(th.Update)
router.Methods(http.MethodDelete).Path(url).HandlerFunc(th.Delete)

router.Methods(http.MethodGet).Path(baseURL + "/types/{type}/records/{record_urn}").HandlerFunc(th.GetByRecord)

templateURL := baseURL + "/templates"
router.Methods(http.MethodGet).Path(templateURL).HandlerFunc(tth.Index)
router.Methods(http.MethodPost).Path(templateURL).HandlerFunc(tth.Create)
router.Methods(http.MethodGet).Path(templateURL + "/{template_urn}").HandlerFunc(tth.Find)
router.Methods(http.MethodPut).Path(templateURL + "/{template_urn}").HandlerFunc(tth.Update)
router.Methods(http.MethodDelete).Path(templateURL + "/{template_urn}").HandlerFunc(tth.Delete)

}
2 changes: 1 addition & 1 deletion docs/concepts/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The script filter is designed to match a document if:
To demonstrate, the following API call:

```text
curl http://localhost:3000/v1/search?text=log&filter.landscape=id
curl http://localhost:3000/v1beta1/search?text=log&filter.landscape=id
```

is internally translated to the following elasticsearch query
Expand Down
24 changes: 12 additions & 12 deletions swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ info:
description: "Data Discovery and Lineage Service"
version: 0.1.0
paths:
"/v1/lineage":
"/v1beta1/lineage":
get:
tags:
- Lineage
Expand All @@ -28,7 +28,7 @@ paths:
description: record not found
schema:
$ref: "#/definitions/Error"
"/v1/lineage/{type}/{record}":
"/v1beta1/lineage/{type}/{record}":
get:
tags:
- Lineage
Expand All @@ -49,7 +49,7 @@ paths:
description: invalid type requested
schema:
$ref: "#/definitions/Error"
"/v1/types":
"/v1beta1/types":
get:
tags:
- Type
Expand All @@ -70,7 +70,7 @@ paths:
count:
type: number
example: 1800
"/v1/types/{name}/records":
"/v1beta1/types/{name}/records":
put:
tags:
- Record
Expand Down Expand Up @@ -132,7 +132,7 @@ paths:
description: not found
schema:
$ref: "#/definitions/Error"
"/v1/types/{name}/records/{id}":
"/v1beta1/types/{name}/records/{id}":
delete:
tags:
- Record
Expand All @@ -158,7 +158,7 @@ paths:
description: type or record cannot be found
schema:
$ref: "#/definitions/Error"
"/v1/types/{name}/{id}":
"/v1beta1/types/{name}/{id}":
get:
tags:
- Record
Expand All @@ -183,7 +183,7 @@ paths:
description: document or type does not exist
schema:
$ref: "#/definitions/Error"
"/v1/search":
"/v1beta1/search":
get:
tags:
- Search
Expand Down Expand Up @@ -236,7 +236,7 @@ paths:
description: misconfigured request parameters
schema:
$ref: "#/definitions/Error"
"/v1/tags":
"/v1beta1/tags":
post:
tags:
- Tag
Expand Down Expand Up @@ -273,7 +273,7 @@ paths:
description: internal server error
schema:
$ref: "#/definitions/Error"
"/v1/tags/templates":
"/v1beta1/tags/templates":
get:
tags:
- Tag
Expand Down Expand Up @@ -327,7 +327,7 @@ paths:
description: internal server error
schema:
$ref: "#/definitions/Error"
"/v1/tags/templates/{template_urn}":
"/v1beta1/tags/templates/{template_urn}":
get:
tags:
- Tag
Expand Down Expand Up @@ -417,7 +417,7 @@ paths:
description: internal server error
schema:
$ref: "#/definitions/Error"
"/v1/tags/types/{type}/records/{record_urn}":
"/v1beta1/tags/types/{type}/records/{record_urn}":
get:
tags:
- Tag
Expand Down Expand Up @@ -445,7 +445,7 @@ paths:
description: internal server error
schema:
$ref: "#/definitions/Error"
"/v1/tags/types/{type}/records/{record_urn}/templates/{template_urn}":
"/v1beta1/tags/types/{type}/records/{record_urn}/templates/{template_urn}":
get:
tags:
- Tag
Expand Down