diff --git a/README.md b/README.md index ef0fd26..b695a63 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ To use this, POST a JSON with the field `id` to the endpoint. It will return a J Next changes sorted by priority -- Gin integration +- Update http stuff to integration - Authorization - Deletion - Test docker-compose installation \ No newline at end of file diff --git a/handlers/handlers.go b/handlers/handlers.go index 9b821e5..687c9a4 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -2,12 +2,10 @@ package handlers import ( - "encoding/json" "fmt" "net/http" "github.com/gin-gonic/gin" - "github.com/julienschmidt/httprouter" "github.com/maxibanki/golang-url-shortener/store" ) @@ -38,8 +36,10 @@ func New(addr string, store store.Store) *Handler { func (h *Handler) setHandlers() { h.engine.POST("/api/v1/create", h.handleCreate) - // h.engine.POST("/api/v1/info", h.handleInfo) - // h.engine.GET("/:id", h.handleAccess) + h.engine.POST("/api/v1/info", h.handleInfo) + h.engine.StaticFile("/", "static/index.html") + h.engine.GET("/:id", h.handleAccess) + gin.SetMode(gin.ReleaseMode) } // handleCreate handles requests to create an entry @@ -53,61 +53,57 @@ func (h *Handler) handleCreate(c *gin.Context) { return } - id, err := h.store.CreateEntry(data.URL, c.Request.RemoteAddr) + id, err := h.store.CreateEntry(data.URL, c.ClientIP()) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + + data.URL = h.getSchemaAndHost(c) + "/" + id + c.JSON(http.StatusOK, data) +} + +func (h *Handler) getSchemaAndHost(c *gin.Context) string { protocol := "http" if c.Request.TLS != nil { protocol = "https" } - data.URL = fmt.Sprintf("%s://%s/%s", protocol, c.Request.Host, id) - c.JSON(http.StatusOK, data) + return fmt.Sprintf("%s://%s", protocol, c.Request.Host) } // handleInfo is the http handler for getting the infos -func (h *Handler) handleInfo(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - var req struct { - ID string +func (h *Handler) handleInfo(c *gin.Context) { + var data struct { + ID string `binding:"required"` } - err := json.NewDecoder(r.Body).Decode(&req) + err := c.ShouldBind(&data) if err != nil { - http.Error(w, fmt.Sprintf("could not decode JSON: %v", err), http.StatusBadRequest) - return - } - if req.ID == "" { - http.Error(w, "no ID provided", http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - entry, err := h.store.GetEntryByID(req.ID) + entry, err := h.store.GetEntryByID(data.ID) if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } entry.RemoteAddr = "" - w.Header().Add("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(entry) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } + c.JSON(http.StatusOK, entry) } // handleAccess handles the access for incoming requests -func (h *Handler) handleAccess(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - id := p.ByName("id") +func (h *Handler) handleAccess(c *gin.Context) { + id := c.Param("id") entry, err := h.store.GetEntryByID(id) if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) + c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } err = h.store.IncreaseVisitCounter(id) if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - http.Redirect(w, r, entry.URL, http.StatusTemporaryRedirect) + c.Redirect(http.StatusTemporaryRedirect, entry.URL) } // Listen starts the http server diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go index 0775191..4703185 100644 --- a/handlers/handlers_test.go +++ b/handlers/handlers_test.go @@ -36,7 +36,7 @@ func TestCreateEntryJSON(t *testing.T) { name: "body is nil", response: "could not decode JSON: EOF", statusCode: http.StatusBadRequest, - contentType: "text/plain; charset=utf-8", + contentType: "application/json; charset=utf-8", ignoreResponse: true, }, { @@ -45,7 +45,7 @@ func TestCreateEntryJSON(t *testing.T) { URL: "https://www.google.de/", }, statusCode: http.StatusOK, - contentType: "application/json", + contentType: "application/json; charset=utf-8", }, { name: "no valid URL", @@ -53,7 +53,7 @@ func TestCreateEntryJSON(t *testing.T) { URL: "this is really not a URL", }, statusCode: http.StatusBadRequest, - contentType: "text/plain; charset=utf-8", + contentType: "application/json; charset=utf-8", response: store.ErrNoValidURL.Error(), ignoreResponse: true, }, @@ -65,7 +65,6 @@ func TestCreateEntryJSON(t *testing.T) { defer cleanup() for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - // build body for the create URL http request var reqBody []byte if tc.requestBody.URL != "" { json, err := json.Marshal(tc.requestBody) @@ -386,7 +385,8 @@ func getBackend() (func(), error) { if err != nil { return nil, errors.Wrap(err, "could not create handler") } - server = httptest.NewServer(handler.handlers()) + + server = httptest.NewServer(handler.engine) return func() { server.Close() handler.Stop() diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..8db7268 --- /dev/null +++ b/static/index.html @@ -0,0 +1,15 @@ + + + + + + + + Great page! + + + + + + + \ No newline at end of file