I created an API using the following code:
["/environments/:env-name/nodes"
{:swagger {:tags ["Nodes"]}
:parameters {:path {:env-name ::vt-vali/name}}}
[""
{:get {:summary "Retrieve the nodes from this environment"
:parameters {:query {:date ::vt-vali/timestamp}}
:responses {200 {:body map?}}
:handler (fn [{{{:keys [env-name]} :path
{:keys [date]} :query} :parameters}]
(let [result (vt-data/ret-nodes env-name date)]
(if (s/valid? map? result)
{:status 200
:body result}
{:status 500
:body result})))}}]]
This works perfectly. However, I want to make the query parameter optional.
Can anyone help me with this?
I found an answer by searching through the examples in metosin/reitit.
It is possible to use clojure.spec.alpha. Add [clojure.spec.alpha :as s] to the required dependencies of the namespace and you can use:
:parameters {:query (s/keys :opt-un [::date])}
See this file for the example in metosin/reitit http-swagger example
I don't think that can be done. You can add an extra route:
(defn handler [{{{:keys [env-name]} :path
{:keys [date]} :query} :parameters}]
(let [result (vt-data/ret-nodes env-name date)]
(if (s/valid? map? result)
{:status 200
:body result}
{:status 500
:body result})))
["/environments/nodes"
{:swagger {:tags ["Nodes"]}
:parameters {:path {:env-name ::vt-vali/name}}}
[""
{:get {:summary "Retrieve the nodes from this environment"
:parameters {:query {:date ::vt-vali/timestamp}}
:responses {200 {:body map?}}
:handler handler}}]
"/environments/:env-name/nodes"
{:swagger {:tags ["Nodes"]}
:parameters {:path {:env-name ::vt-vali/name}}}
[""
{:get {:summary "Retrieve the nodes from this environment"
:parameters {:query {:date ::vt-vali/timestamp}}
:responses {200 {:body map?}}
:handler handler}}]]
Related
Following my last question, but to keep the code homogeneous I want to use httpkit to make requests instead of clj-http as I did before.
I have the following to try and receive my profile from the Spotify API:
(defn get-me [token]
#(client/request
{:url "https://api.spotify.com/v1/me"
:method :get
:headers {"Authorization" token
"Accept" "application/json"
"Content-Type" "application/json"}}))
(defn get-me-handler [req]
(let [res (:token #TOKEN)]
(if (= res "")
{:status 404
:headers {"Content-Type" "text/html"}
:body "Not logged in"}
{:status 200
:headers {"Content-Type" "text/html"}
:body (get-me (get (json/read-str res) "access_token"))})))
a long with a "localhost:{port}/me" route. When I access this route, I get back:
{"status": 400, "message": "Only valid bearer authentication supported" }}"
As the error message. Does anyone know how to fix this in httpkit, as it is not a problem I had in clj-http?
I'm calling a route with axios that I want to really call as a post request. However, when calling with a post request like so:
export const uploadFeatured = (mediaName, youtubeLink, description) => async dispatch => {
console.log("uploading", mediaName, youtubeLink, description);
const res = await axios.post(domain + '/api/uploadFeatured');
}
I'm getting an error:
rror: Request failed with status code 403
createError#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:156601:26
settle#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:156591:25
handleLoad#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:156491:15
dispatchEvent#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:33005:31
setReadyState#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:32074:27
__didCompleteResponse#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:31905:29
emit#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:7758:42
__callFunction#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:3387:36
http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:3119:31
__guard#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:3341:15
callFunctionReturnFlushedQueue#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:3118:21
callFunctionReturnFlushedQueue#[native code]
However, get request works without any error. Here's my clojure code for the routes and the server for request handling:
(ns humboiserver.routes.home
(:require
[humboiserver.layout :as layout]
[clojure.java.io :as io]
[humboiserver.middleware :as middleware]
[ring.util.response]
[ring.util.http-response :as response]
[humboiserver.routes.featured :as featured]))
(defn home-page [request]
(layout/render request "home.html" {:docs (-> "docs/docs.md" io/resource slurp)}))
(defn about-page [request]
(layout/render request "about.html"))
(defn home-routes []
[""
{:middleware [middleware/wrap-csrf
middleware/wrap-formats]}
["/" {:get home-page}]
["/api"
["/about" {:get about-page}]
["/featured" featured/get-featured]
["/invest" featured/invest]
["/connect" featured/connect]
["/uploadFeatured" featured/upload-featured]]])
and
(defn response [data & [status]]
{:status (or status 200)
:headers {"Content-Type" "application/edn"
"Access-Control-Allow-Headers" "Content-Type"
"Access-Control-Request-Method" "GET, OPTIONS, POST"
"Access-Control-Allow-Origin" "*"
"Access-Control-Allow-Credentials" true
}
:body (generate-string data)})
(defn upload-featured [req]
(prn "request is " (:params req))
;;(db/insert "featured" (:params req))
(response "uploaded")
)
How to fix this error and what am I doing wrong?
You seem to be getting a 403 Forbidden response when POST-ing, GET is fine, but POST seems to be forbidden. When the server/client do not run on the same host/origin, some CORS restrictions may apply. Somewhere you define response headers:
"Access-Control-Request-Method" "GET, OPTIONS"
Maybe adding 'POST' to this response header might resolve the situation.
I'm running a server with clojure on localhost:3000. The server talks to the client, which is a react native app that's using axios to talk to the server. However the communication with the server is returning a 403 error.
Call to the server:
export const invest = (itemid, amount) => async dispatch => {
console.log("investing in actions")
const domain = 'localhost:3000'
const res = axios.post(domain + '/api/invest', {itemid: itemid, amount: amount});
console.log("response is", res)
dispatch({ type: INVESTED, payload:res.data});
}
Server contains a reitit route called "/api/invest", and this route will call a function called featured/invest. But instead I'm getting a 403 error in the client:
(ns humboiserver.routes.home
(:require
[humboiserver.layout :as layout]
[clojure.java.io :as io]
[humboiserver.middleware :as middleware]
[ring.util.response]
[ring.util.http-response :as response]
[humboiserver.routes.featured :as featured]))
(defn home-page [request]
(layout/render request "home.html" {:docs (-> "docs/docs.md" io/resource slurp)}))
(defn about-page [request]
(layout/render request "about.html"))
(defn home-routes []
[""
{:middleware [middleware/wrap-csrf
middleware/wrap-formats]}
["/" {:get home-page}]
["/api"
["/about" {:get about-page}]
["/featured" featured/get-featured]
["/invest" featured/invest]
]
])
Even the invested prn statement isn't printed in the logs.
;; featured/invest function.
(defn response [data & [status]]
{:status (or status 200)
:headers {"Content-Type" "application/json"
"Access-Control-Allow-Headers" "Content-Type"
"Access-Control-Request-Method" "GET, OPTIONS"
"Access-Control-Allow-Origin" "*"
"Access-Control-Allow-Credentials" true
}
:body (pr-str data)})
(defn invest [req]
(prn "invested")
(response (db/find "featured" {})))
403 error:
[Warning] Possible Unhandled Promise Rejection (id: 0): (AppEntry.bundle, line 42288)
Error: Request failed with status code 403
createError#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:156390:26
settle#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:156380:25
handleLoad#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:156280:15
dispatchEvent#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:32753:31
setReadyState#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:31822:27
__didCompleteResponse#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:31653:29
emit#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:7566:42
__callFunction#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:3195:36
http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:2927:31
__guard#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:3149:15
callFunctionReturnFlushedQueue#http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:2926:21
callFunctionReturnFlushedQueue#[native code]
http://localhost:19000/debugger-ui/debuggerWorker.aca173c4.js:4:907
How to fix this error?
Hi I think you need to send an Anti-forgery token if I'm not mistaken...
https://github.com/ring-clojure/ring-anti-forgery
You can use curl to test accessing your server via the command line
I'm trying to serve a clj-http generated document directly via ring/compojure.
I thought ring.util/piped-output-stream would work, but it seems I'm not understanding something here...
This:
(defn laminat-pdf-t
[natno]
(piped-input-stream
(fn [output-stream])
(pdf
[ {:title (str "Omanimali-Kuscheltierpass" natno)
:orientation :landscape
:size :a6
:author "Omanimali - Stefanie Tuschen"
:register-system-fonts true
}
;; [:svg {} (clojure.java.io/file
;; (str "/einbuergern/" natno "/svg" ))]
[:paragraph "Some Text"] ]
output-stream)))
(defn laminat-pdf
"generate individualized cuddly toy passport page"
[natno]
{:headers {"Content-Type" "application/pdf"}
:body (laminat-pdf-t natno)})
leads to an empty response...
What do I need to do differently?
Thanks,
Mathias
I think you may have a bracket out of place in your code (look at the laminat-pdf-t function below - I tweaked it slightly).
Here's exactly what I did (first creating a project with leiningen 2.3.4 called pdf-play) and it displayed a PDF correctly in IE 11.0.9600.16521, Firefox 28.0 and Chrome 33.0.1750.154 (all on Windows - sorry these are the only browsers that I have installed and I don't have a Linux or Mac box but I don't think the browser makes any difference):
project.clj
(defproject pdf-play "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.5.1"]
[compojure "1.1.6"]
[clj-pdf "1.11.15"]]
:plugins [[lein-ring "0.8.10"]]
:ring {:handler pdf-play.handler/app})
src/pdf_play/handler.clj
(ns pdf-play.handler
(:use compojure.core
ring.util.io
clj-pdf.core)
(:require [compojure.handler :as handler]
[compojure.route :as route]))
(defn laminat-pdf-t
[natno]
(piped-input-stream
(fn [output-stream]
(pdf
[{:title (str "Omanimali-Kuscheltierpass" natno)
:orientation :landscape
:size :a6
:author "Omanimali - Stefanie Tuschen"
:register-system-fonts true
}
;; [:svg {} (clojure.java.io/file
;; (str "/einbuergern/" natno "/svg" ))]
[:paragraph "Some Text"]]
output-stream))))
(defn laminat-pdf
"generate individualized cuddly toy passport page"
[natno]
{:headers {"Content-Type" "application/pdf"}
:body (laminat-pdf-t natno)})
(defroutes app-routes
(GET "/" [] (laminat-pdf 1234))
(route/resources "/")
(route/not-found "Not Found"))
(def app (handler/site app-routes))
Then started it at the command prompt like so:
lein ring server
and had a look in the browser and there was a PDF with "Some Text" printed in it.
This is the API call Im attempting:
http://developers.facebook.com/docs/reference/rest/video.upload
(Video upload is not available in the new Graph API.)
I have tried many variations on the parameters. The code below is my best guess. If I modify the params to be obviously incorrect, change to http (not https) or try to use api.facebook.com for video, I get proper errors back.
However, my code below just waits a few minutes before reporting:
ETIMEDOUT: Connection timed out
Also included is working code to upload a photo - which is almost identical.
Ruby:
# Facebook Old-API method - testing only - this works.
def post_photo
url = "https://api.facebook.com/method/photos.upload"
body = {
nil => File.new(self.media.media_files.first.source_file, 'rb'),
:access_token => self.session.auth_data[:access_token],
:callback => "none",
:aid => "Test Photos",
:caption => "Test",
:uid => self.session.auth_data[:uid],
}
response = RestClient.post url, body
end
# Facebook Old-API method - doesn't work - connection timeout.
def post_video
url = "https://api-video.facebook.com/method/video.upload"
body = {
:nil => File.new(self.media.media_files.first.source_file, 'rb'),
:access_token => self.session.auth_data[:access_token],
:callback => "none",
:title => "Test title",
:description => "Test description",
:privacy => "{ value: 'EVERYONE' }",
:uid => self.session.auth_data[:uid],
}
response = RestClient.post url, body
end
PS: Im in Australia - is the API limited to eg the USA?
Thanks