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.
Related
Please comment how to start backend and frontend with Calva Jack-in.
There is a clojurescript project for a web-application, I started REPL with calva jack-in (deps.edn + shadow-cljs), and tried to evaluate the function start!/stop! in the REPL. Even though there was no error message, I didn't find a corresponding response with "localhost:3000/", it was expected that "Hello, world" should be shown on browser.
To compile of both frontend and backend successfully:
$ npm install && npx shadow-cljs watch frontend backend
Start the server server with node.js successfully
$ node target/main.js
Clojurescript backend code for reference.
(ns mern.backend.core
(:require ["express" :as express]))
;; currently broken in shadow-cljs
(set! *warn-on-infer* true)
(defonce server (atom nil))
(def port 3000)
(defn start-server []
(println "Starting server")
(let [app (express)]
(.get app "/" (fn [_req res] (.send res "Hello, world")))
(.listen app port (fn [] (println "Example app listening on port 3000!")))))
(defn start! []
;; called by main and after reloading code
(reset! server (start-server)))
(defn stop! []
;; called before reloading code
(.close #server)
(reset! server nil))
(defn main []
;; executed once, on startup, can do one time setup here
(start!))
I am just getting started with Clojurescript. I wrote some clojurescript code to use the shared aws credentials file to initialize S3 client and list buckets . However my code does not work.
(defn -main [arg1 arg2]
(println "hello")
(let[ creds (new AWS/SharedIniFileCredentials #js{:profile "superman"})
_ (AWS/config.update creds)
; dump out the accesskey to check if it has the correct profile
_ (AWS/config.getCredentials (fn [err] (if (nil? err) (do
(println "its good")
(println AWS/config.credentials.accessKeyId)))))
s3 (new (.-S3 AWS ))
] (.listBuckets s3 (fn[err buckets] (println "err: " err) (println buckets) )) ))
The AWS/config.getCredentials in the above does pick up the correct profile as seen from (println AWS/config.credentials.accessKeyId). The listbuckets code throws the following error:
#object[NodeError TypeError [ERR_INVALID_ARG_TYPE]: The "key" argument must be of type string or an instance of Buffer, TypedArray, DataView, or KeyObject. Received undefined]
I have Google AWS SDK S3 clojuresript AND is the only link I found . I used that to configure the S3 client but that does not seem to work
I would appreciate any help.
I checked it and the problem seems to be that SDK expects the credentials to be set before anything else, before instantiating the S3 client.
The following works for me on a minimal project with shadow-cljs:
(ns server.main
(:require ["aws-sdk" :as aws]))
(defn main! []
(println "App loaded...")
(let [creds (aws/SharedIniFileCredentials. #js {:profile "example-profile"})
_ (set! (.-credentials aws/config) creds)
s3 (aws/S3.)]
(.listBuckets s3 (fn [err data]
(if err
(println "ERROR:" err)
(println "OK:" data))))))
when I run it:
$ node target/main.js
App loaded...
OK: #js {:Buckets #js [#js {:Name dummy-test-bucket, :CreationDate #inst "2019-05-05T17:32:17.000-00:00"} #js {:Name mydomain.com, :CreationDate #inst "2018-06-19T04:16:10.000-00:00"}], :Owner #js {:DisplayName username, :ID f63f30bc25ab3609b8d3b5be6b3a872dd2c9f7947b2d509e2338357d93e74f2}}
The key was on this answer: https://stackoverflow.com/a/33175424/483566
Please bear with this contrived example but it was the simplest thing I could think of to recreate the issue.
(ns something.core)
(defn call-foo [something & args]
(let [a-foo (:foo (eval (:quux something)))]
(apply a-foo args)))
(def Something {
:foo (fn [& args] args)
:bar (fn [something] (call-foo something))
})
(defn make-something []
{:quux 'Something})
Running the following in the REPL or with lein run works well.
(let [subject (make-something)
actual (call-foo subject "hello" "greetings")]
(println actual))
;;=> (hello greetings)
The problem occurs only during this test and executing lein test:
(ns something.core-test
(:require [clojure.test :refer :all]
[something.core :refer :all]))
(deftest a-test
(let [subject (make-something)
actual (call-foo subject "hello" "greetings")]
(is (= ["hello" "greetings"] actual))))
This throws an error. An example output:
ERROR in (a-test) (Compiler.java:6464)
Uncaught exception, not in assertion.
expected: nil
actual: clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to resolve symbol: Something in this context, compiling:(/private/var/folders/0n/c7q7860j34xfc2r1x4q51jrh0000gn/T/form-init9215140948330409114.clj:1:6436)
The line "Unable to resolve symbol: Something in this context" makes me think Something is not in context for some reason while I eval in call-foo. But why is this the case only in the test?
The problem is that eval does not see context. Your 'Something resolves in something.core and something.core-test since you have refered all. It won't resolve from whatever namespace where lein test runs its tests.
To fix the immediate problem change
'Something
to
`Something
so that it is namespace-qualified. The test will then run (and fail), but that's another issue (println returns nil for one thing).
I am trying to write a test for the compojure static content route.
I am testing the routes by examining the ring response directly.
A minimal working example is as follows:
;; src/testing-webapps.core.clj
(ns testing-webapps.core
(:use [compojure.core]
[compojure.route :as route]))
(defroutes web-app
(route/resources "/")
(route/not-found "404"))
;; test/testing-webapps.core_test.clj
(ns testing-webapps.core-test
(:require [clojure.test :refer :all]
[testing-webapps.core :refer :all]))
(defn request [resource web-app & params]
(web-app {:request-method :get :uri resource :params (first params)}))
(deftest test-routes
(is (= 404 (:status (request "/flubber" web-app))))
(is (= "404" (:body (request "/flubber" web-app))))
(is (= 200 (:status (request "/test.txt" web-app)))))
Testing the 404 route works fine but calling (request "/test.txt" web-app) leads to an unexpected NullPointerException in ring.middleware.file-info/not-modified-since?.
Here is the top part of the stacktrace:
ERROR in (test-routes) (file_info.clj:27)
Uncaught exception, not in assertion.
expected: nil
actual: java.lang.NullPointerException: null
at ring.middleware.file_info$not_modified_since_QMARK_.invoke (file_info.clj:27)
ring.middleware.file_info$file_info_response.doInvoke (file_info.clj:44)
clojure.lang.RestFn.invoke (RestFn.java:442)
ring.middleware.file_info$wrap_file_info$fn__917.invoke (file_info.clj:64)
[...]
The static route works fine in the browser but not when called through my request function.
Is there an easier way to test a static route in compojure and why do I get a NullPointerException when calling the static route with my own request map?
Looking at the source for not-modified-since?, I think the issue is you have no headers in your request map, so it throws a NPE on this expr: (headers "if-modified-since"). Try changing your request method like so:
(defn request [resource web-app & params]
(web-app {:request-method :get
:headers {"content-type" "text/plain"} ; added a header
:uri resource
:params (first params)}))
You might also consider using ring-mock to create requests for testing. It insulates you a little bit from stuff like this.
I am currently using
- firefox 13
- selenium-server-standalone-2.23.1.jar
- selenium-client (1.2.18)
- rspec 1.2.8
Selenium stops here even if page has fully loaded
08:49:43.888 INFO - Command request: waitForPageToLoad[300000, ] on session 2718493e6d4640eea76d6cb3ab1a6fc3
require 'rubygems'
require "selenium/client"
require "selenium/rspec/spec_helper"
describe "Google Search" do
attr_reader :selenium_driver
alias :page :selenium_driver
before(:all) do
#selenium_driver = Selenium::Client::Driver.new \
:host => "localhost",
:port => 4444,
:browser => "*firefox",
:url => "http://www.google.com",
:timeout_in_second => 10
end
before(:each) do
selenium_driver.start_new_browser_session
end
# The system capture need to happen BEFORE closing the Selenium session
append_after(:each) do
#selenium_driver.close_current_browser_session
end
it "can find Selenium" do
page.open "/"
page.title.should eql("Google")
page.type "q", "Selenium seleniumhq"
page.click "btnG", :wait_for => :page
page.value("q").should eql("Selenium seleniumhq")
page.text?("seleniumhq.org").should be_true
page.title.should eql("Selenium seleniumhq - Google Search")
page.text?("seleniumhq.org").should be_true
page.element?("link=Cached").should be_true
end
end
I wonder if the google page is using AJAX which is confusing the page load event.
When I went to http://google.com I noticed that submitting the search only added #q=selenium to the url.
This could likely be confusing selenium. Maybe instead of wait for pageload you can wait for page element
Some search element xpath (for example) /html/body/div[1]/div[3]/div[3]/div[6]/div[2]/div[3]/div/div[2]/div[2]/div/div/ol/div[2]/li[1]/div (ugly I know)