Skip to content

Commit

Permalink
Clojure sdk: Fixes, clojure snippets (#672)
Browse files Browse the repository at this point in the history
* Clojure sdk: Fixes, new website snippets

Docs: corrected the readme's example
Fix: changed return value in the jetty implementation
Chore: Changed dependency coordinate of http-kit to a newer maven dependency
Fix: using setTimeout in the redirect sugar function
Fix: mistake in http-kit management in examples
Docs: redirect example
Feature: added another testing utility
Docs: added missing snippets for the Datastar website

* Fix: typo
  • Loading branch information
JeremS authored Feb 17, 2025
1 parent d20737a commit f500e93
Show file tree
Hide file tree
Showing 20 changed files with 415 additions and 12 deletions.
7 changes: 4 additions & 3 deletions sdk/clojure/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
!.clj-kondo/config.edn
!.clj-kondo/hooks**




test-resources/test.config.edn
/.lsp-root
/.nfnl.fnl
/.nvim.fnl
/.nvim.lua
20 changes: 19 additions & 1 deletion sdk/clojure/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
# Release notes for the Clojure SDK

## 2025-02-15

### Added

- `starfederation.datastar.clojure.adapter.test/->sse-response`. This is a mock
for a SSE ring response that records the SSE events sent with it.
- Example snippets for the main site, ie, polling and redirection. These
examples are runnable from the development examples.
- Development example of the usage of the redirect sugar.

### Fixed

- Fixed the main readme example (wrong arity of `:on-close` callback using http-kit)
- The jetty adapter now returns a harmless value when sending an event. It used
to return the write buffer which shouldn't be used directly.
- The `starfederation.datastar.clojure.api/redirect!` helper function uses a js
timeout for redirection

## 2025-02-03

### Changed

- The ring adapter for the SKD is now a generic ring adapter. This adapter
- The ring adapter for the SDK is now a generic ring adapter. This adapter
depends solely on the ring core protocols, the dependency to the ring
jetty adapter has been removed.

Expand Down
3 changes: 2 additions & 1 deletion sdk/clojure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ somewhere and use it later:
(fn [sse-gen]
(swap! !connections conj sse-gen))
:on-close
(fn [sse-gen]
(fn [sse-gen status]
(swap! !connections disj sse-gen))}))


Expand Down Expand Up @@ -184,6 +184,7 @@ Ring adapters are not made equals. Here are some the differences for the ones we

## TODO:

- Consider adding an option to adapter to be able to control the output stream (usefull to enable compression)
- Streamlined release process (cutting releases and publish jar to a maven repo)
- Consider uniformizing the adapters behavior on connection closing (throwing in all adapters?)
- Review the etoin tests, there are some race conditions
Expand Down
2 changes: 1 addition & 1 deletion sdk/clojure/adapter-http-kit/deps.edn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
;; NOTE: Track the next release of http-kit to switch to maven dep
{:paths ["src/main"]
:deps {http-kit/http-kit {:git/url "https://github.com/http-kit/http-kit" :git/sha "76b869fc34536ad0c43afa9a98d971a0fc32c644"}}}
:deps {http-kit/http-kit {:mvn/version "2.9.0-alpha2"}}}

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
(doto ^BufferedWriter @writer
(sse/write-event! event-type data-lines opts)
(.flush))
:sent
(catch Exception e
(throw (ex-info "Error sending SSE event"
{:sse-gen this
Expand All @@ -42,8 +43,9 @@
(u/lock! lock
(when-let [^BufferedWriter w @writer]
(.close w)
(when on-close
(on-close this)))))
(if on-close
(on-close this)
nil))))

Closeable
(close [this]
Expand Down
13 changes: 13 additions & 0 deletions sdk/clojure/doc/maintainers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ In the sdk code proper `sdk/clojure`:

## Test

### Running tests

- `bb run test:all`: run all core tests and smoke tests with ring jetty and
Http-kit
- `bb run test:rj9a`: run all core tests and smoke tests with rj9a
- for the generic bash sdk tests
1. go to `sdk/clojure/sdk-tests/`
2. run `clojure -M -m starfederation.datastar.clojure.sdk-test.main`
3. go to `sdk/test/`
4. run `./test-all.sh localhost:8080`

### webdriver config

- Tests resources contains a test.config.edn file. It contains a map whose keys are:
- `:drivers`: [etaoin](https://github.com/clj-commons/etaoin) webdriver types to run
- `:webdriver-opts`: a map of webdriver type to webriver specific options
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
(ns starfederation.datastar.clojure.adapter.test
(:require
[starfederation.datastar.clojure.api.sse :as sse]
[starfederation.datastar.clojure.protocols :as p]))
[starfederation.datastar.clojure.protocols :as p]
[starfederation.datastar.clojure.utils :as u])
(:import
[java.util.concurrent.locks ReentrantLock]
[java.io Closeable]))



(deftype ReturnMsgGen []
Expand All @@ -18,3 +23,39 @@
(defn ->sse-gen [& _]
(->ReturnMsgGen))




(deftype RecordMsgGen [lock !rec !open?]
p/SSEGenerator
(send-event! [_ event-type data-lines opts]
(u/lock! lock
(vswap! !rec conj (-> (StringBuilder.)
(sse/write-event! event-type data-lines opts)
str))))

(close-sse! [_]
(u/lock! lock
(vreset! !open? false)))

Closeable
(close [this]
(p/close-sse! this)))

(java.util.ArrayList. 1)

(defn ->sse-response
"Fake a sse-response, the events sent with sse-gen during the
`on-open` callback are recorded in a vector stored in an atom returned as the
body of the response."
[req {:keys [status headers on-open]}]
(let [!rec (volatile! [])
sse-gen (->RecordMsgGen (ReentrantLock.)
!rec
(volatile! true))]
(on-open sse-gen)
{:status (or status 200)
:headers (merge headers (sse/headers req))
:body !rec}))


Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,10 @@
([sse-gen url]
(redirect! sse-gen url {}))
([sse-gen url opts]
(execute-script! sse-gen (str "window.location.href = \""url"\";") opts)))
(execute-script! sse-gen
(str "setTimeout(() => window.location.href =\"" url "\")")
opts)))


;; -----------------------------------------------------------------------------
;; Misc
Expand Down
66 changes: 66 additions & 0 deletions sdk/clojure/src/dev/examples/redirect.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
(ns examples.redirect
(:require
[examples.common :as c]
[examples.utils :as u]
[dev.onionpancakes.chassis.core :refer [html]]
[reitit.ring :as rr]
[ring.util.response :as ruresp]
[starfederation.datastar.clojure.api :as d*]
[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response]]))


(def home-page
(html
(c/page-scaffold
[[:h1 "Test page"]
[:div.#indicator
[:button {:data-on-click (d*/sse-get "/redirect-me")}
"Start redirect"]]])))


(defn home [_]
(ruresp/response home-page))


(def guide-page
(html
(c/page-scaffold
[[:h1 "You have been redirected"]
[:a {:href "/" } "Home"]])))


(defn guide [_]
(ruresp/response guide-page))


(defn redirect-handler [ring-request]
(->sse-response ring-request
{:on-open
(fn [sse]
(d*/merge-fragment! sse
(html [:div#indicator "Redirecting in 3 seconds..."]))
(Thread/sleep 3000)
(d*/redirect! sse "/guide")
(d*/close-sse! sse))}))




(def router (rr/router
[["/" {:handler home}]
["/guide" {:handler guide}]
["/redirect-me" {:handler redirect-handler}]]))


(def default-handler (rr/create-default-handler))


(def handler
(rr/ring-handler router default-handler))



(comment
(u/reboot-hk-server! #'handler))


32 changes: 32 additions & 0 deletions sdk/clojure/src/dev/examples/snippets/polling1.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(ns examples.snippets.polling1
(:require
[dev.onionpancakes.chassis.core :refer [html]]
[starfederation.datastar.clojure.api :as d*]
[starfederation.datastar.clojure.adapter.test :refer [->sse-response]]))


(comment
(require
'[starfederation.datastar.clojure.api :as d*]
'[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response]]
'[some.hiccup.library :refer [html]]))

(import
'java.time.format.DateTimeFormatter
'java.time.LocalDateTime)

(def formatter (DateTimeFormatter/ofPattern "YYYY-MM-DD HH:mm:ss"))

(defn handler [ring-request]
(->sse-response ring-request
{:on-open
(fn [sse]
(d*/merge-fragment! sse
(html [:div#time {:data-on-interval__duration.5s (d*/sse-get "/endpoint")}
(LocalDateTime/.format (LocalDateTime/now) formatter)]))
(d*/close-sse! sse))}))

(comment
(handler {}))


42 changes: 42 additions & 0 deletions sdk/clojure/src/dev/examples/snippets/polling2.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(ns examples.snippets.polling2
(:require
[dev.onionpancakes.chassis.core :refer [html]]
[starfederation.datastar.clojure.api :as d*]
[starfederation.datastar.clojure.adapter.test :as at :refer [->sse-response]]))


(comment
(require
'[starfederation.datastar.clojure.api :as d*]
'[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response]]
'[some.hiccup.library :refer [html]]))

(import
'java.time.format.DateTimeFormatter
'java.time.LocalDateTime)

(def date-time-formatter (DateTimeFormatter/ofPattern "YYYY-MM-DD HH:mm:ss"))
(def seconds-formatter (DateTimeFormatter/ofPattern "ss"))

(defn handler [ring-request]
(->sse-response ring-request
{:on-open
(fn [sse]
(let [now (LocalDateTime/now)
current-time (LocalDateTime/.format now date-time-formatter)
seconds (LocalDateTime/.format now seconds-formatter)
duration (if (neg? (compare seconds "50"))
"5"
"1")]
(d*/merge-fragment! sse
(html [:div#time {(str "data-on-interval__duration." duration "s")
(d*/sse-get "/endpoint")}
current-time]))

(d*/close-sse! sse)))}))


(comment
(handler {}))


28 changes: 28 additions & 0 deletions sdk/clojure/src/dev/examples/snippets/redirect1.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
(ns examples.snippets.redirect1
(:require
[dev.onionpancakes.chassis.core :refer [html]]
[starfederation.datastar.clojure.api :as d*]
[starfederation.datastar.clojure.adapter.test :refer [->sse-response]]))


(comment
(require
'[starfederation.datastar.clojure.api :as d*]
'[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response]]
'[some.hiccup.library :refer [html]]))


(defn handler [ring-request]
(->sse-response ring-request
{:on-open
(fn [sse]
(d*/merge-fragment! sse
(html [:div#indicator "Redirecting in 3 seconds..."]))
(Thread/sleep 3000)
(d*/execute-script! sse "window.location = \"/guide\"")
(d*/close-sse! sse))}))

(comment
(handler {}))


30 changes: 30 additions & 0 deletions sdk/clojure/src/dev/examples/snippets/redirect2.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
(ns examples.snippets.redirect2
(:require
[dev.onionpancakes.chassis.core :refer [html]]
[starfederation.datastar.clojure.api :as d*]
[starfederation.datastar.clojure.adapter.test :refer [->sse-response]]))


(comment
(require
'[starfederation.datastar.clojure.api :as d*]
'[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response]]
'[some.hiccup.library :refer [html]]))


(defn handler [ring-request]
(->sse-response ring-request
{:on-open
(fn [sse]
(d*/merge-fragment! sse
(html [:div#indicator "Redirecting in 3 seconds..."]))
(Thread/sleep 3000)
(d*/execute-script! sse
"setTimeout(() => window.location = \"/guide\"")
(d*/close-sse! sse))}))


(comment
(handler {}))


Loading

0 comments on commit f500e93

Please sign in to comment.