diff --git a/backend/src/uxbox/api/auth.clj b/backend/src/uxbox/api/auth.clj index b124e11cd..431c2dcbc 100644 --- a/backend/src/uxbox/api/auth.clj +++ b/backend/src/uxbox/api/auth.clj @@ -9,7 +9,7 @@ [promesa.core :as p] [struct.core :as st] [uxbox.services :as sv] - [uxbox.util.http :as http] + [uxbox.http.response :as rsp] [uxbox.util.spec :as us] [uxbox.util.uuid :as uuid])) @@ -22,7 +22,7 @@ (let [data (get-in ctx [:parameters :body])] (->> (sv/novelty (assoc data :type :login)) (p/map (fn [{:keys [id] :as user}] - (-> (http/no-content) + (-> (rsp/no-content) (assoc :session {:user-id id}))))))) (defn register @@ -34,7 +34,7 @@ (let [data (get parameters :body) message (assoc data :type :register-profile)] (->> (sv/novelty message) - (p/map http/ok)))) + (p/map rsp/ok)))) (defn request-recovery {:parameters {:body {:username [st/required st/string]}}} @@ -42,7 +42,7 @@ (let [data (get parameters :body) message (assoc data :type :request-profile-password-recovery)] (->> (sv/novelty message) - (p/map (constantly (http/no-content)))))) + (p/map (constantly (rsp/no-content)))))) (defn recover-password {:parameters {:body {:token [st/required st/string] @@ -51,7 +51,7 @@ (let [data (get parameters :body) message (assoc data :type :recover-profile-password)] (->> (sv/novelty message) - (p/map (constantly (http/no-content)))))) + (p/map (constantly (rsp/no-content)))))) (defn validate-recovery-token {:parameters {:path {:token [st/required st/string]}}} @@ -61,5 +61,5 @@ (->> (sv/query message) (p/map (fn [v] (if v - (http/no-content) - (http/not-found ""))))))) + (rsp/no-content) + (rsp/not-found ""))))))) diff --git a/backend/src/uxbox/api/icons.clj b/backend/src/uxbox/api/icons.clj index 78df9dcd0..bfb2e3b56 100644 --- a/backend/src/uxbox/api/icons.clj +++ b/backend/src/uxbox/api/icons.clj @@ -8,7 +8,7 @@ (:require [struct.core :as st] [promesa.core :as p] [uxbox.services :as sv] - [uxbox.util.http :as http] + [uxbox.http.response :as rsp] [uxbox.util.spec :as us] [uxbox.util.uuid :as uuid])) @@ -23,7 +23,7 @@ (->> (sv/novelty message) (p/map (fn [result] (let [loc (str "/api/library/icons/" (:id result))] - (http/created loc result))))))) + (rsp/created loc result))))))) (defn update-collection {:parameters {:path {:id [st/required st/uuid-str]} @@ -37,7 +37,7 @@ :type :update-icon-collection :user user)] (-> (sv/novelty message) - (p/then #(http/ok %))))) + (p/then #(rsp/ok %))))) (defn delete-collection @@ -47,13 +47,13 @@ :type :delete-icon-collection :user user}] (-> (sv/novelty message) - (p/then (fn [v] (http/no-content)))))) + (p/then (fn [v] (rsp/no-content)))))) (defn list-collections [{:keys [user]}] (let [params {:user user :type :list-icon-collections}] (-> (sv/query params) - (p/then #(http/ok %))))) + (p/then #(rsp/ok %))))) ;; (def metadata-spec ;; {:width [st/number st/positive] @@ -82,7 +82,7 @@ (->> (sv/novelty message) (p/map (fn [entry] (let [loc (str "/api/library/icons/" (:id entry))] - (http/created loc entry))))))) + (rsp/created loc entry))))))) (defn update-icon {:parameters {:path {:id [st/required st/uuid-str]} @@ -96,7 +96,7 @@ :type :update-icon :user user)] (->> (sv/novelty message) - (p/map #(http/ok %))))) + (p/map rsp/ok)))) (defn copy-icon {:parameters {:path {:id [st/required st/uuid-str]} @@ -108,7 +108,7 @@ :user user :type :copy-icon}] (->> (sv/novelty message) - (p/map #(http/ok %))))) + (p/map rsp/ok)))) (defn delete-icon {:parameters {:path {:id [st/required st/uuid-str]}}} @@ -117,7 +117,7 @@ :type :delete-icon :user user}] (->> (sv/novelty message) - (p/map (fn [v] (http/no-content)))))) + (p/map (fn [v] (rsp/no-content)))))) (defn list-icons {:parameters {:query {:collection [st/uuid-str]}}} @@ -127,5 +127,5 @@ :type :list-icons :user user}] (->> (sv/query message) - (p/map http/ok)))) + (p/map rsp/ok)))) diff --git a/backend/src/uxbox/api/images.clj b/backend/src/uxbox/api/images.clj index 07f78958c..048102bb3 100644 --- a/backend/src/uxbox/api/images.clj +++ b/backend/src/uxbox/api/images.clj @@ -12,7 +12,7 @@ [uxbox.media :as media] [uxbox.images :as images] [uxbox.services :as sv] - [uxbox.util.http :as http] + [uxbox.http.response :as rsp] [uxbox.util.spec :as us] [uxbox.util.uuid :as uuid])) @@ -40,7 +40,7 @@ (->> (sv/novelty message) (p/map (fn [result] (let [loc (str "/api/library/images/" (:id result))] - (http/created loc result))))))) + (rsp/created loc result))))))) (defn update-collection {:parameters {:path {:id [st/required st/uuid-str]} @@ -54,7 +54,7 @@ :type :update-image-collection :user user)] (-> (sv/novelty message) - (p/then http/ok)))) + (p/then rsp/ok)))) (defn delete-collection {:parameters {:path {:id [st/required st/uuid-str]}}} @@ -63,13 +63,13 @@ :type :delete-image-collection :user user}] (-> (sv/novelty message) - (p/then (constantly (http/no-content)))))) + (p/then (constantly (rsp/no-content)))))) (defn list-collections [{:keys [user]}] (let [params {:user user :type :list-image-collections}] (-> (sv/query params) - (p/then http/ok)))) + (p/then rsp/ok)))) (defn retrieve-image {:parameters {:path {:id [st/required st/uuid-str]}}} @@ -82,8 +82,8 @@ (if result (-> (populate-thumbnails result) (populate-urls) - (http/ok)) - (http/not-found ""))))))) + (rsp/ok)) + (rsp/not-found ""))))))) ;; (s/def ::create-image ;; (s/keys :req-un [::file ::width ::height ::mimetype] @@ -112,7 +112,7 @@ :user user)))) (create-response [entry] (let [loc (str "/api/library/images/" (:id entry))] - (http/created loc entry)))] + (rsp/created loc entry)))] (->> (ds/save storage filename tempfile) (p/mapcat persist-image-entry) (p/map populate-thumbnails) @@ -133,7 +133,7 @@ (->> (sv/novelty message) (p/map populate-thumbnails) (p/map populate-urls) - (p/map http/ok)))) + (p/map rsp/ok)))) (defn copy-image {:parameters {:path {:id [st/required st/uuid-str]} @@ -145,7 +145,7 @@ (->> (sv/novelty message) (p/map populate-thumbnails) (p/map populate-urls) - (p/map http/ok)))) + (p/map rsp/ok)))) (defn delete-image {:parameters {:path {:id [st/required st/uuid-str]}}} @@ -154,7 +154,7 @@ :type :delete-image :user user}] (->> (sv/novelty message) - (p/map (constantly (http/no-content)))))) + (p/map (constantly (rsp/no-content)))))) ;; --- List collections @@ -168,6 +168,6 @@ (->> (sv/query message) (p/map (partial map populate-thumbnails)) (p/map (partial map populate-urls)) - (p/map http/ok)))) + (p/map rsp/ok)))) diff --git a/backend/src/uxbox/api/kvstore.clj b/backend/src/uxbox/api/kvstore.clj index dbaa575f4..5cd9c0462 100644 --- a/backend/src/uxbox/api/kvstore.clj +++ b/backend/src/uxbox/api/kvstore.clj @@ -10,7 +10,7 @@ [promesa.core :as p] [uxbox.services :as sv] [uxbox.media :as media] - [uxbox.util.http :as http] + [uxbox.http.response :as rsp] [uxbox.util.spec :as us] [uxbox.util.uuid :as uuid])) @@ -22,7 +22,7 @@ :type :retrieve-kvstore :user user}] (->> (sv/query message) - (p/map http/ok)))) + (p/map rsp/ok)))) (defn upsert {:parameters {:path {:key [st/required st/string]} @@ -38,7 +38,7 @@ :type :update-kvstore :user user}] (->> (sv/novelty message) - (p/map http/ok)))) + (p/map rsp/ok)))) (defn delete {:parameters {:path {:key [st/required st/string]}}} @@ -48,6 +48,6 @@ :type :delete-kvstore :user user}] (->> (sv/novelty message) - (p/map (constantly (http/no-content)))))) + (p/map (constantly (rsp/no-content)))))) diff --git a/backend/src/uxbox/api/pages.clj b/backend/src/uxbox/api/pages.clj index ab00b460c..8a84591be 100644 --- a/backend/src/uxbox/api/pages.clj +++ b/backend/src/uxbox/api/pages.clj @@ -9,21 +9,19 @@ [struct.core :as st] [promesa.core :as p] [uxbox.services :as sv] - [uxbox.util.http :as http] + [uxbox.http.response :as rsp] [uxbox.util.spec :as us] [uxbox.util.uuid :as uuid])) -(defn list - "List pages in a project" +(defn list-pages {:parameters {:query {:project [st/required st/uuid-str]}}} [{:keys [user parameters]}] (let [project (get-in parameters [:query :project]) message {:user user :project project :type :list-pages-by-project}] (-> (sv/query message) - (p/then #(http/ok %))))) + (p/then rsp/ok)))) -(defn create - "Create page for a project" +(defn create-page {:parameters {:body {:data [st/required] :metadata [st/required] :project [st/required st/uuid] @@ -35,10 +33,9 @@ (->> (sv/novelty message) (p/map (fn [result] (let [loc (str "/api/pages/" (:id result))] - (http/created loc result))))))) + (rsp/created loc result))))))) -(defn update - "Update page" +(defn update-page {:parameters {:path {:id [st/required st/uuid-str]} :body {:data [st/required] :metadata [st/required] @@ -51,10 +48,9 @@ data (get parameters :body) message (assoc data :id id :type :update-page :user user)] (->> (sv/novelty message) - (p/map #(http/ok %))))) + (p/map #(rsp/ok %))))) -(defn update-metadata - "Update page metadata" +(defn update-page-metadata {:parameters {:path {:id [st/required st/uuid-str]} :body {:id [st/required st/uuid] :metadata [st/required] @@ -65,17 +61,17 @@ data (get parameters :body) message (assoc data :id id :type :update-page-metadata :user user)] (->> (sv/novelty message) - (p/map #(http/ok %))))) + (p/map rsp/ok)))) -(defn delete +(defn delete-page {:parameters {:path {:id [st/required st/uuid-str]}}} [{:keys [user parameters]}] (let [id (get-in parameters [:path :id]) message {:id id :type :delete-page :user user}] (-> (sv/novelty message) - (p/then (fn [v] (http/no-content)))))) + (p/then (constantly (rsp/no-content)))))) -(defn retrieve-history +(defn retrieve-page-history "Retrieve the page history" {:parameters {:path {:id [st/required st/uuid-str]} :query {:max [st/integer-str] @@ -86,9 +82,9 @@ data (get parameters :query) message (assoc data :id id :type :list-page-history :user user)] (->> (sv/query message) - (p/map #(http/ok %))))) + (p/map rsp/ok)))) -(defn update-history +(defn update-page-history {:parameters {:path {:id [st/required st/uuid-str] :hid [st/required st/uuid-str]} :body {:label [st/required st/string] @@ -100,4 +96,4 @@ :id hid :user user)] (->> (sv/novelty message) - (p/map #(http/ok %))))) + (p/map rsp/ok)))) diff --git a/backend/src/uxbox/api/projects.clj b/backend/src/uxbox/api/projects.clj index 12c588e2b..c40081fb2 100644 --- a/backend/src/uxbox/api/projects.clj +++ b/backend/src/uxbox/api/projects.clj @@ -10,19 +10,19 @@ [struct.core :as st] [promesa.core :as p] [uxbox.services :as sv] - [uxbox.util.http :as http] + [uxbox.http.response :as rsp] [uxbox.util.spec :as us] [uxbox.util.uuid :as uuid] [uxbox.util.exceptions :as ex])) -(defn list +(defn list-projects {:description "List projects"} [{:keys [user] :as req}] (let [message {:user user :type :list-projects}] (->> (sv/query message) - (p/map #(http/ok %))))) + (p/map rsp/ok)))) -(defn create +(defn create-project "Create project" {:parameters {:body {:name [st/required st/string] :id [st/uuid-str]}}} @@ -32,9 +32,9 @@ (->> (sv/novelty message) (p/map (fn [result] (let [loc (str "/api/projects/" (:id result))] - (http/created loc result))))))) + (rsp/created loc result))))))) -(defn update +(defn update-project "Update project" {:parameters {:path {:id [st/required st/uuid-str]} :body {:name [st/required st/string] @@ -44,22 +44,22 @@ data (get parameters :body) message (assoc data :id id :type :update-project :user user)] (-> (sv/novelty message) - (p/then #(http/ok %))))) + (p/then rsp/ok)))) -(defn delete +(defn delete-project "Delete project" {:parameters {:path {:id [st/required st/uuid-str]}}} [{:keys [user parameters] :as req}] (let [id (get-in parameters [:path :id]) message {:id id :type :delete-project :user user}] (-> (sv/novelty message) - (p/then (fn [v] (http/no-content)))))) + (p/then (constantly (rsp/no-content)))))) -(defn get-by-share-token +(defn get-project-by-share-token "Get a project by shared token" {:parameters {:path {:token [st/required st/string]}}} [{:keys [user parameters] :as req}] (let [message {:token (get-in parameters [:path :token]) :type :retrieve-project-by-share-token}] (->> (sv/query message) - (p/map #(http/ok %))))) + (p/map rsp/ok)))) diff --git a/backend/src/uxbox/api.clj b/backend/src/uxbox/http.clj similarity index 73% rename from backend/src/uxbox/api.clj rename to backend/src/uxbox/http.clj index 59e16d801..43d553a0d 100644 --- a/backend/src/uxbox/api.clj +++ b/backend/src/uxbox/http.clj @@ -4,14 +4,17 @@ ;; ;; Copyright (c) 2019 Andrey Antukh -(ns uxbox.api +(ns uxbox.http (:require [mount.core :refer [defstate]] [muuntaja.core :as m] [ring.adapter.jetty :as jetty] - [reitit.ring :as ring] + [reitit.ring :as rr] [reitit.dev.pretty :as pretty] [uxbox.config :as cfg] - [uxbox.api.middleware :refer [handler middleware authorization-middleware]] + [uxbox.http.middleware :refer [handler + middleware + options-handler + authorization-middleware]] [uxbox.api.auth :as api-auth] [uxbox.api.pages :as api-pages] [uxbox.api.users :as api-users] @@ -22,23 +25,6 @@ [uxbox.api.svg :as api-svg] [uxbox.util.transit :as t])) - -;; --- Top Level Handlers - -(defn- welcome-api - "A GET entry point for the api that shows - a welcome message." - [context] - (let [body {:message "Welcome to UXBox api."}] - {:status 200 - :body {:query-params (:query-params context) - :form-params (:form-params context) - :body-params (:body-params context) - :path-params (:path-params context) - :params (:params context)}})) - -;; --- Routes - (def ^:private muuntaja-instance (m/create (update-in m/default-options [:formats "application/transit+json"] merge {:encoder-opts {:handlers t/+write-handlers+} @@ -46,16 +32,17 @@ (def ^:private router-options {;;:reitit.middleware/transform dev/print-request-diffs + ::rr/default-options-handler options-handler :exception pretty/exception :data {:muuntaja muuntaja-instance :middleware middleware}}) (def routes - (ring/router - [["/media/*" (ring/create-resource-handler {:root "public/media"})] - ["/static/*" (ring/create-resource-handler {:root "public/static"})] + (rr/router + [["/media/*" (rr/create-resource-handler {:root "public/media"})] + ["/static/*" (rr/create-resource-handler {:root "public/static"})] - ["/auth" + ["/api/auth" ["/login" {:post (handler #'api-auth/login)}] ["/register" {:post (handler #'api-auth/register)}] ["/recovery/:token" {:get (handler #'api-auth/register)}] @@ -63,8 +50,6 @@ :get (handler #'api-auth/recover-password)}]] ["/api" {:middleware [authorization-middleware]} - ["/echo" (handler #'welcome-api)] - ;; KVStore ["/kvstore/:key" {:put (handler #'api-kvstore/upsert) :get (handler #'api-kvstore/retrieve) @@ -73,20 +58,20 @@ ["/svg/parse" {:post (handler #'api-svg/parse)}] ;; Projects - ["/projects" {:get (handler #'api-projects/list) - :post (handler #'api-projects/create)}] - ["/projects/by-token/:token" {:get (handler #'api-projects/get-by-share-token)}] - ["/projects/:id" {:put (handler #'api-projects/update) - :delete (handler #'api-projects/delete)}] + ["/projects" {:get (handler #'api-projects/list-projects) + :post (handler #'api-projects/create-project)}] + ["/projects/by-token/:token" {:get (handler #'api-projects/get-project-by-share-token)}] + ["/projects/:id" {:put (handler #'api-projects/update-project) + :delete (handler #'api-projects/delete-project)}] ;; Pages - ["/pages" {:get (handler #'api-pages/list) - :post (handler #'api-pages/create)}] - ["/pages/:id" {:put (handler #'api-pages/update) - :delete (handler #'api-pages/delete)}] - ["/pages/:id/metadata" {:put (handler #'api-pages/update-metadata)}] - ["/pages/:id/history" {:get (handler #'api-pages/retrieve-history)}] - ["/pages/:id/history/:hid" {:put (handler #'api-pages/update-history)}] + ["/pages" {:get (handler #'api-pages/list-pages) + :post (handler #'api-pages/create-page)}] + ["/pages/:id" {:put (handler #'api-pages/update-page) + :delete (handler #'api-pages/delete-page)}] + ["/pages/:id/metadata" {:put (handler #'api-pages/update-page-metadata)}] + ["/pages/:id/history" {:get (handler #'api-pages/retrieve-page-history)}] + ["/pages/:id/history/:hid" {:put (handler #'api-pages/update-page-history)}] ;; Profile ["/profile/me" {:get (handler #'api-users/retrieve-profile) @@ -126,7 +111,7 @@ router-options)) (def app - (ring/ring-handler routes (ring/create-default-handler))) + (rr/ring-handler routes (rr/create-default-handler))) ;; --- State Initialization diff --git a/backend/src/uxbox/api/errors.clj b/backend/src/uxbox/http/errors.clj similarity index 96% rename from backend/src/uxbox/api/errors.clj rename to backend/src/uxbox/http/errors.clj index 9b421cc92..b2b27058f 100644 --- a/backend/src/uxbox/api/errors.clj +++ b/backend/src/uxbox/http/errors.clj @@ -4,8 +4,8 @@ ;; ;; Copyright (c) 20162019 Andrey Antukh -(ns uxbox.api.errors - "A errors handling for api.") +(ns uxbox.http.errors + "A errors handling for the http server.") (defmulti handle-exception #(:type (ex-data %))) diff --git a/backend/src/uxbox/api/middleware.clj b/backend/src/uxbox/http/middleware.clj similarity index 88% rename from backend/src/uxbox/api/middleware.clj rename to backend/src/uxbox/http/middleware.clj index e2c443fe9..24c86e6d4 100644 --- a/backend/src/uxbox/api/middleware.clj +++ b/backend/src/uxbox/http/middleware.clj @@ -4,12 +4,14 @@ ;; ;; Copyright (c) 2016-2019 Andrey Antukh -(ns uxbox.api.middleware +(ns uxbox.http.middleware (:require [promesa.core :as p] - [buddy.core.hash :as hash] - [buddy.core.codecs :as codecs] - [buddy.core.codecs.base64 :as b64] - [reitit.core :as rc] + [cuerdas.core :as str] + ;; [buddy.core.hash :as hash] + ;; [buddy.core.codecs :as codecs] + ;; [buddy.core.codecs.base64 :as b64] + [struct.core :as st] + [reitit.ring :as rr] [reitit.ring.middleware.multipart :as multipart] [reitit.ring.middleware.muuntaja :as muuntaja] [reitit.ring.middleware.parameters :as parameters] @@ -17,9 +19,8 @@ [ring.middleware.session :refer [wrap-session]] [ring.middleware.session.cookie :refer [cookie-store]] [ring.middleware.multipart-params :refer [wrap-multipart-params]] - [struct.core :as st] - [uxbox.api.errors :as api-errors] - [uxbox.util.http :as http] + [uxbox.http.errors :as errors] + [uxbox.http.response :as rsp] [uxbox.util.data :refer [normalize-attrs]] [uxbox.util.exceptions :as ex])) @@ -54,6 +55,7 @@ (try (handler (transform request) respond raise) (catch Exception e + (prn handler) (raise e))))))))}) (def ^:private multipart-params-middleware @@ -133,8 +135,8 @@ (def ^:private exception-middleware (exception/create-exception-middleware (assoc exception/default-handlers - ::exception/default api-errors/errors-handler - ::exception/wrap api-errors/wrap-print-errors))) + ::exception/default errors/errors-handler + ::exception/wrap errors/wrap-print-errors))) (def authorization-middleware {:name ::authorization-middleware @@ -143,11 +145,11 @@ ([request] (if-let [identity (get-in request [:session :user-id])] (handler (assoc request :identity identity :user identity)) - (http/forbidden nil))) + (rsp/forbidden nil))) ([request respond raise] (if-let [identity (get-in request [:session :user-id])] (handler (assoc request :identity identity :user identity) respond raise) - (respond (http/forbidden nil))))))}) + (respond (rsp/forbidden nil))))))}) (def middleware [session-middleware @@ -176,3 +178,8 @@ (cond-> hlrdata (:doc metadata) (assoc :description (:doc metadata))))) +(defn options-handler + [request respond raise] + (let [methods (->> request rr/get-match :result (keep (fn [[k v]] (if v k)))) + allow (->> methods (map (comp str/upper name)) (str/join ","))] + (respond {:status 200, :body "", :headers {"Allow" allow}}))) diff --git a/backend/src/uxbox/http/response.clj b/backend/src/uxbox/http/response.clj new file mode 100644 index 000000000..453ec71e3 --- /dev/null +++ b/backend/src/uxbox/http/response.clj @@ -0,0 +1,216 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) 2019 Andrey Antukh + +(ns uxbox.http.response) + +(defn response + "Create a response instance." + ([body] (response body 200 {})) + ([body status] (response body status {})) + ([body status headers] {:body body :status status :headers headers})) + +(defn response? + [resp] + (and (map? resp) + (integer? (:status resp)) + (map? (:headers resp)))) + +(defn continue + ([body] (response body 100)) + ([body headers] (response body 100 headers))) + +(defn ok + "HTTP 200 OK + Should be used to indicate nonspecific success. Must not be used to + communicate errors in the response body. + + In most cases, 200 is the code the client hopes to see. It indicates that + the REST API successfully carried out whatever action the client requested, + and that no more specific code in the 2xx series is appropriate. Unlike + the 204 status code, a 200 response should include a response body." + ([body] (response body 200)) + ([body headers] (response body 200 headers))) + +(defn created + "HTTP 201 Created + Must be used to indicate successful resource creation. + + A REST API responds with the 201 status code whenever a collection creates, + or a store adds, a new resource at the client's request. There may also be + times when a new resource is created as a result of some controller action, + in which case 201 would also be an appropriate response." + ([location] (response "" 201 {"location" location})) + ([location body] (response body 201 {"location" location})) + ([location body headers] (response body 201 (merge headers {"location" location})))) + +(defn accepted + "HTTP 202 Accepted + Must be used to indicate successful start of an asynchronous action. + + A 202 response indicates that the client's request will be handled + asynchronously. This response status code tells the client that the request + appears valid, but it still may have problems once it's finally processed. + A 202 response is typically used for actions that take a long while to + process." + ([body] (response body 202)) + ([body headers] (response body 202 headers))) + +(defn no-content + "HTTP 204 No Content + Should be used when the response body is intentionally empty. + + The 204 status code is usually sent out in response to a PUT, POST, or + DELETE request, when the REST API declines to send back any status message + or representation in the response message's body. An API may also send 204 + in conjunction with a GET request to indicate that the requested resource + exists, but has no state representation to include in the body." + ([] (response "" 204)) + ([headers] (response "" 204 headers))) + +(defn moved-permanently + "301 Moved Permanently + Should be used to relocate resources. + + The 301 status code indicates that the REST API's resource model has been + significantly redesigned and a new permanent URI has been assigned to the + client's requested resource. The REST API should specify the new URI in + the response's Location header." + ([location] (response "" 301 {"location" location})) + ([location body] (response body 301 {"location" location})) + ([location body headers] (response body 301 (merge headers {"location" location})))) + +(defn found + "HTTP 302 Found + Should not be used. + + The intended semantics of the 302 response code have been misunderstood + by programmers and incorrectly implemented in programs since version 1.0 + of the HTTP protocol. + The confusion centers on whether it is appropriate for a client to always + automatically issue a follow-up GET request to the URI in response's + Location header, regardless of the original request's method. For the + record, the intent of 302 is that this automatic redirect behavior only + applies if the client's original request used either the GET or HEAD + method. + + To clear things up, HTTP 1.1 introduced status codes 303 (\"See Other\") + and 307 (\"Temporary Redirect\"), either of which should be used + instead of 302." + ([location] (response "" 302 {"location" location})) + ([location body] (response body 302 {"location" location})) + ([location body headers] (response body 302 (merge headers {"location" location})))) + +(defn see-other + "HTTP 303 See Other + Should be used to refer the client to a different URI. + + A 303 response indicates that a controller resource has finished its work, + but instead of sending a potentially unwanted response body, it sends the + client the URI of a response resource. This can be the URI of a temporary + status message, or the URI to some already existing, more permanent, + resource. + Generally speaking, the 303 status code allows a REST API to send a + reference to a resource without forcing the client to download its state. + Instead, the client may send a GET request to the value of the Location + header." + ([location] (response "" 303 {"location" location})) + ([location body] (response body 303 {"location" location})) + ([location body headers] (response body 303 (merge headers {"location" location})))) + +(defn temporary-redirect + "HTTP 307 Temporary Redirect + Should be used to tell clients to resubmit the request to another URI. + + HTTP/1.1 introduced the 307 status code to reiterate the originally + intended semantics of the 302 (\"Found\") status code. A 307 response + indicates that the REST API is not going to process the client's request. + Instead, the client should resubmit the request to the URI specified by + the response message's Location header. + + A REST API can use this status code to assign a temporary URI to the + client's requested resource. For example, a 307 response can be used to + shift a client request over to another host." + ([location] (response "" 307 {"location" location})) + ([location body] (response body 307 {"location" location})) + ([location body headers] (response body 307 (merge headers {"location" location})))) + +(defn bad-request + "HTTP 400 Bad Request + May be used to indicate nonspecific failure. + + 400 is the generic client-side error status, used when no other 4xx error + code is appropriate." + ([body] (response body 400)) + ([body headers] (response body 400 headers))) + +(defn unauthorized + "HTTP 401 Unauthorized + Must be used when there is a problem with the client credentials. + + A 401 error response indicates that the client tried to operate on a + protected resource without providing the proper authorization. It may have + provided the wrong credentials or none at all." + ([body] (response body 401)) + ([body headers] (response body 401 headers))) + +(defn forbidden + "HTTP 403 Forbidden + Should be used to forbid access regardless of authorization state. + + A 403 error response indicates that the client's request is formed + correctly, but the REST API refuses to honor it. A 403 response is not a + case of insufficient client credentials; that would be 401 (\"Unauthorized\"). + REST APIs use 403 to enforce application-level permissions. For example, a + client may be authorized to interact with some, but not all of a REST API's + resources. If the client attempts a resource interaction that is outside of + its permitted scope, the REST API should respond with 403." + ([body] (response body 403)) + ([body headers] (response body 403 headers))) + +(defn not-found + "HTTP 404 Not Found + Must be used when a client's URI cannot be mapped to a resource. + + The 404 error status code indicates that the REST API can't map the + client's URI to a resource." + ([body] (response body 404)) + ([body headers] (response body 404 headers))) + +(defn method-not-allowed + ([body] (response body 405)) + ([body headers] (response body 405 headers))) + +(defn not-acceptable + ([body] (response body 406)) + ([body headers] (response body 406 headers))) + +(defn conflict + ([body] (response body 409)) + ([body headers] (response body 409 headers))) + +(defn gone + ([body] (response body 410)) + ([body headers] (response body 410 headers))) + +(defn precondition-failed + ([body] (response body 412)) + ([body headers] (response body 412 headers))) + +(defn unsupported-mediatype + ([body] (response body 415)) + ([body headers] (response body 415 headers))) + +(defn too-many-requests + ([body] (response body 429)) + ([body headers] (response body 429 headers))) + +(defn internal-server-error + ([body] (response body 500)) + ([body headers] (response body 500 headers))) + +(defn not-implemented + ([body] (response body 501)) + ([body headers] (response body 501 headers))) diff --git a/backend/src/uxbox/main.clj b/backend/src/uxbox/main.clj index 5c7478075..07032219c 100644 --- a/backend/src/uxbox/main.clj +++ b/backend/src/uxbox/main.clj @@ -9,7 +9,7 @@ [uxbox.config :as cfg] [uxbox.migrations] [uxbox.db] - [uxbox.api] + [uxbox.http] [uxbox.scheduled-jobs]) (:gen-class)) diff --git a/backend/test/uxbox/tests/test_auth.clj b/backend/test/uxbox/tests/test_auth.clj index e1c1b7805..5d0aae64b 100644 --- a/backend/test/uxbox/tests/test_auth.clj +++ b/backend/test/uxbox/tests/test_auth.clj @@ -1,10 +1,9 @@ (ns uxbox.tests.test-auth (:require [clojure.test :as t] [promesa.core :as p] - [clj-http.client :as http] [buddy.hashers :as hashers] [uxbox.db :as db] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services.users :as usu] [uxbox.services :as usv] [uxbox.tests.helpers :as th])) @@ -20,7 +19,7 @@ :email "user1@uxbox.io"} user (with-open [conn (db/connection)] (usu/create-user conn data))] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [data {:username "user1" :password "user1" :metadata "1" @@ -38,7 +37,7 @@ :email "user1@uxbox.io"} user (with-open [conn (db/connection)] (usu/create-user conn data))] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [data {:username "user1" :password "user2" :metadata "2" diff --git a/backend/test/uxbox/tests/test_icons.clj b/backend/test/uxbox/tests/test_icons.clj index db9d90706..157d16044 100644 --- a/backend/test/uxbox/tests/test_icons.clj +++ b/backend/test/uxbox/tests/test_icons.clj @@ -4,7 +4,7 @@ [suricatta.core :as sc] [uxbox.db :as db] [uxbox.sql :as sql] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services.icons :as icons] [uxbox.services :as usv] [uxbox.tests.helpers :as th])) @@ -18,7 +18,7 @@ data {:user (:id user) :name "coll1"} coll (icons/create-collection conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icon-collections") [status data] (th/http-get user uri)] ;; (println "RESPONSE:" status data) @@ -28,7 +28,7 @@ (t/deftest test-http-create-icon-collection (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icon-collections") data {:user (:id user) :name "coll1"} @@ -45,7 +45,7 @@ data {:user (:id user) :name "coll1"} coll (icons/create-collection conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icon-collections/" (:id coll)) params {:body (assoc coll :name "coll2")} [status data] (th/http-put user uri params)] @@ -61,7 +61,7 @@ :name "coll1" :data #{1}} coll (icons/create-collection conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icon-collections/" (:id coll)) [status data] (th/http-delete user uri)] (t/is (= 204 status)) @@ -72,7 +72,7 @@ (t/deftest test-http-create-icon (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icons") data {:name "sample.jpg" :content "" @@ -99,7 +99,7 @@ :metadata {} :collection nil} icon (icons/create-icon conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icons/" (:id icon)) params {:body (assoc icon :name "my stuff")} [status data] (th/http-put user uri params)] @@ -117,7 +117,7 @@ :metadata {} :collection nil} icon (icons/create-icon conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icons/" (:id icon) "/copy") body {:collection nil} params {:body body} @@ -137,7 +137,7 @@ :metadata {} :collection nil} icon (icons/create-icon conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icons/" (:id icon)) [status data] (th/http-delete user uri)] (t/is (= 204 status)) @@ -154,7 +154,7 @@ :metadata {} :collection nil} icon (icons/create-icon conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/icons") [status data] (th/http-get user uri)] ;; (println "RESPONSE:" status data) diff --git a/backend/test/uxbox/tests/test_images.clj b/backend/test/uxbox/tests/test_images.clj index a25f44f37..e418fe1d4 100644 --- a/backend/test/uxbox/tests/test_images.clj +++ b/backend/test/uxbox/tests/test_images.clj @@ -7,7 +7,7 @@ [uxbox.db :as db] [uxbox.sql :as sql] [uxbox.media :as media] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services.images :as images] [uxbox.services :as usv] [uxbox.tests.helpers :as th])) @@ -21,7 +21,7 @@ data {:user (:id user) :name "coll1"} coll (images/create-collection conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/image-collections") [status data] (th/http-get user uri)] ;; (println "RESPONSE:" status data) @@ -31,7 +31,7 @@ (t/deftest test-http-create-image-collection (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/image-collections") data {:user (:id user) :name "coll1"} @@ -48,7 +48,7 @@ data {:user (:id user) :name "coll1"} coll (images/create-collection conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/image-collections/" (:id coll)) params {:body (assoc coll :name "coll2")} [status data] (th/http-put user uri params)] @@ -64,7 +64,7 @@ :name "coll1" :data #{1}} coll (images/create-collection conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/image-collections/" (:id coll)) [status data] (th/http-delete user uri)] (t/is (= 204 status)) @@ -75,7 +75,7 @@ (t/deftest test-http-create-image (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/images") parts [{:name "sample.jpg" :part-name "upload" @@ -102,7 +102,7 @@ :mimetype "image/png" :collection nil} img (images/create-image conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/images/" (:id img)) params {:body (assoc img :name "my stuff")} [status data] (th/http-put user uri params)] @@ -126,7 +126,7 @@ :mimetype "image/jpg" :collection nil} img (images/create-image conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/images/" (:id img) "/copy") body {:id (:id img) :collection nil} @@ -149,7 +149,7 @@ :mimetype "image/png" :collection nil} img (images/create-image conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/images/" (:id img)) [status data] (th/http-delete user uri)] (t/is (= 204 status)) @@ -168,7 +168,7 @@ :mimetype "image/png" :collection nil} img (images/create-image conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/library/images") [status data] (th/http-get user uri)] ;; (println "RESPONSE:" status data) diff --git a/backend/test/uxbox/tests/test_kvstore.clj b/backend/test/uxbox/tests/test_kvstore.clj index 1b34c417d..3fae16ce0 100644 --- a/backend/test/uxbox/tests/test_kvstore.clj +++ b/backend/test/uxbox/tests/test_kvstore.clj @@ -5,7 +5,7 @@ [buddy.core.codecs :as codecs] [uxbox.db :as db] [uxbox.util.uuid :as uuid] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services.kvstore :as kvs] [uxbox.tests.helpers :as th])) @@ -21,7 +21,7 @@ (t/is (nil? (kvs/retrieve-kvstore conn {:user id :key "foo" :version -1}))) ;; Creating new one should work as expected - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/kvstore/foo") body {:value "bar" :version -1} params {:body body} @@ -37,7 +37,7 @@ (t/is (= (:value data) "bar")) ;; Overwriting should work - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/kvstore/foo") body (assoc data :value "baz") _ (prn body) @@ -53,7 +53,7 @@ (t/is (= (:value data) "baz"))) ;; Delete should work - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/kvstore/foo") [status data] (th/http-delete user uri)] ;; (println "RESPONSE:" status data) diff --git a/backend/test/uxbox/tests/test_pages.clj b/backend/test/uxbox/tests/test_pages.clj index f8d9036e2..9beff079e 100644 --- a/backend/test/uxbox/tests/test_pages.clj +++ b/backend/test/uxbox/tests/test_pages.clj @@ -4,7 +4,7 @@ [suricatta.core :as sc] [uxbox.util.uuid :as uuid] [uxbox.db :as db] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services.projects :as uspr] [uxbox.services.pages :as uspg] [uxbox.services :as usv] @@ -17,7 +17,7 @@ (with-open [conn (db/connection)] (let [user (th/create-user conn 1) proj (uspr/create-project conn {:user (:id user) :name "proj1"})] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/pages") params {:body {:project (:id proj) :name "page1" @@ -48,7 +48,7 @@ :height 200 :layout "mobil"} page (uspg/create-page conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ (str "/api/pages/" (:id page))) params {:body (assoc page :data "3")} [status page'] (th/http-put user uri params)] @@ -74,7 +74,7 @@ :height 200 :layout "mobil"} page (uspg/create-page conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ (str "/api/pages/" (:id page) "/metadata")) params {:body (assoc page :data "3")} [status page'] (th/http-put user uri params)] @@ -100,7 +100,7 @@ :height 200 :layout "mobil"} page (uspg/create-page conn data)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ (str "/api/pages/" (:id page))) [status response] (th/http-delete user uri)] ;; (println "RESPONSE:" status response) @@ -125,7 +125,7 @@ :layout "mobil"} page1 (uspg/create-page conn (assoc data :project (:id proj1))) page2 (uspg/create-page conn (assoc data :project (:id proj2)))] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ (str "/api/pages?project=" (:id proj1))) [status response] (th/http-get user uri)] ;; (println "RESPONSE:" status response) @@ -158,7 +158,7 @@ (t/is (= (count result) 101))) ;; Check retrieve all items - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/pages/" (:id page) "/history") [status result] (th/http-get user uri nil)] ;; (println "RESPONSE:" status result) @@ -201,7 +201,7 @@ result (sc/fetch conn [sql (:id data)]) item (first result)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/pages/" (:id page) "/history/" (:id item)) diff --git a/backend/test/uxbox/tests/test_projects.clj b/backend/test/uxbox/tests/test_projects.clj index c0b909c5f..f50a41718 100644 --- a/backend/test/uxbox/tests/test_projects.clj +++ b/backend/test/uxbox/tests/test_projects.clj @@ -4,7 +4,7 @@ [suricatta.core :as sc] [clj-uuid :as uuid] [uxbox.db :as db] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services.projects :as uspr] [uxbox.services.pages :as uspg] [uxbox.services :as usv] @@ -17,7 +17,7 @@ (with-open [conn (db/connection)] (let [user (th/create-user conn 1) proj (uspr/create-project conn {:user (:id user) :name "proj1"})] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/projects") [status data] (th/http-get user uri)] (t/is (= 200 status)) @@ -26,7 +26,7 @@ (t/deftest test-http-project-create (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/projects") params {:body {:name "proj1"}} [status data] (th/http-post user uri params)] @@ -39,7 +39,7 @@ (with-open [conn (db/connection)] (let [user (th/create-user conn 1) proj (uspr/create-project conn {:user (:id user) :name "proj1"})] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/projects/" (:id proj)) params {:body (assoc proj :name "proj2")} [status data] (th/http-put user uri params)] @@ -52,7 +52,7 @@ (with-open [conn (db/connection)] (let [user (th/create-user conn 1) proj (uspr/create-project conn {:user (:id user) :name "proj1"})] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/projects/" (:id proj)) [status data] (th/http-delete user uri)] (t/is (= 204 status)) @@ -76,7 +76,7 @@ :height 200 :layout "mobil"}) shares (uspr/get-share-tokens-for-project conn (:id proj))] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [token (:token (first shares)) uri (str th/+base-url+ "/api/projects/by-token/" token) [status data] (th/http-get user uri)] diff --git a/backend/test/uxbox/tests/test_svgparse.clj b/backend/test/uxbox/tests/test_svgparse.clj index 6e462e702..160e67a8d 100644 --- a/backend/test/uxbox/tests/test_svgparse.clj +++ b/backend/test/uxbox/tests/test_svgparse.clj @@ -1,7 +1,7 @@ (ns uxbox.tests.test-svgparse (:require [clojure.test :as t] [clojure.java.io :as io] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services :as usv] [uxbox.services.svgparse :as svg] [uxbox.tests.helpers :as th])) @@ -59,7 +59,7 @@ ;; (let [image (slurp (io/resource "uxbox/tests/_files/sample2.svg")) ;; path "/api/svg/parse" ;; user (th/create-user conn 1)] - ;; (th/with-server {:handler uapi/app} + ;; (th/with-server {:handler http/app} ;; (let [rsp (th/request {:method :post ;; :path path ;; :body image diff --git a/backend/test/uxbox/tests/test_users.clj b/backend/test/uxbox/tests/test_users.clj index f5c4e52b0..47d1b3e15 100644 --- a/backend/test/uxbox/tests/test_users.clj +++ b/backend/test/uxbox/tests/test_users.clj @@ -3,10 +3,9 @@ [clojure.java.io :as io] [promesa.core :as p] [buddy.hashers :as hashers] - [clj-http.client :as http] [suricatta.core :as sc] [uxbox.db :as db] - [uxbox.api :as uapi] + [uxbox.http :as http] [uxbox.services.users :as usu] [uxbox.services :as usv] [uxbox.tests.helpers :as th])) @@ -17,7 +16,7 @@ (t/deftest test-http-retrieve-profile (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/profile/me") [status data] (th/http-get user uri)] ;; (println "RESPONSE:" status data) @@ -31,7 +30,7 @@ (t/deftest test-http-update-profile (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/profile/me") data (assoc user :fullname "Full Name" @@ -50,7 +49,7 @@ (t/deftest test-http-update-profile-photo (with-open [conn (db/connection)] (let [user (th/create-user conn 1)] - (th/with-server {:handler uapi/app} + (th/with-server {:handler http/app} (let [uri (str th/+base-url+ "/api/profile/me/photo") params [{:name "sample.jpg" :part-name "file"