From 435ca50f7db16ba6aa406da420961a01fdedc43c Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Thu, 27 Sep 2018 13:20:34 +0000 Subject: [PATCH 01/25] Usage --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/README.md b/README.md index 04c13ff..eb2937f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,89 @@ An MQTT-library for ClojureScript. _NOTE: this is pre-alpha software with an API that will change_ +## installation + +Leiningen: +```clojure +[eval/otarta "0.3.0-SNAPSHOT"] +``` + +Deps: +```clojure +eval/otarta {:mvn/version "0.3.0-SNAPSHOT"} +``` + +## Usage + +The following code assumes: +- to be run in a browser (ie `js/WebSockets` exists) +- a websocket-enabled MQTT-broker runs on `localhost:9001` (eg via `docker run --rm -ti -p 9001:9001 toke/mosquitto`) + +```clojure +(ns example.core + (:require-macros [cljs.core.async.macros :refer [go go-loop]]) + (:require [cljs.core.async :as a :refer [> buff (mqtt-fmt/read mqtt-fmt/string) extract-temperature)) + (write [_fmt v] + (->> v (mqtt-fmt/write mqtt-fmt/string))))) +``` + ## CLI -- GitLab From 9b763cfacb53726b90481ff7b288c14b0d759e63 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Thu, 27 Sep 2018 13:21:42 +0000 Subject: [PATCH 02/25] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb2937f..38b6d79 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ An MQTT-library for ClojureScript. _NOTE: this is pre-alpha software with an API that will change_ -## installation +## Installation Leiningen: ```clojure -- GitLab From 54488450d6bfc4c7575d9fc0fa661f60b6bd53af Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Thu, 27 Sep 2018 13:28:43 +0000 Subject: [PATCH 03/25] Prepare for new release --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 38b6d79..6adf3f0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Otarta +[![pipeline status](https://gitlab.com/eval/otarta/badges/master/pipeline.svg)](https://gitlab.com/eval/otarta/commits/master) +[![Clojars Project](https://img.shields.io/clojars/v/eval/otarta.svg)](https://clojars.org/eval/otarta) + + An MQTT-library for ClojureScript. _NOTE: this is pre-alpha software with an API that will change_ @@ -8,12 +12,12 @@ _NOTE: this is pre-alpha software with an API that will change_ Leiningen: ```clojure -[eval/otarta "0.3.0-SNAPSHOT"] +[eval/otarta "0.3.0"] ``` Deps: ```clojure -eval/otarta {:mvn/version "0.3.0-SNAPSHOT"} +eval/otarta {:mvn/version "0.3.0"} ``` ## Usage -- GitLab From e331ff3630998609f233c46a23cc2f7524a8368c Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Thu, 27 Sep 2018 13:33:05 +0000 Subject: [PATCH 04/25] Wording --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6adf3f0..6e470c1 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ eval/otarta {:mvn/version "0.3.0"} ## Usage The following code assumes: -- to be run in a browser (ie `js/WebSockets` exists) -- a websocket-enabled MQTT-broker runs on `localhost:9001` (eg via `docker run --rm -ti -p 9001:9001 toke/mosquitto`) +- being in a browser (ie `js/WebSockets` exists) +- a websocket-enabled MQTT-broker on `localhost:9001` (eg via `docker run --rm -ti -p 9001:9001 toke/mosquitto`) ```clojure (ns example.core @@ -51,10 +51,14 @@ The following code assumes: (mqtt/publish client "temperature/current" "12.1")) ``` +### client + The `broker-url` should be of the form `ws(s):://(user:pass@)host.org:1234/path(#some/root/topic)`. The fragment-part is called the `root-topic` and indicates the topic relative to which the client publishes and subscribes. This allows for easy configuration of your client (e.g. "ws://some-broker/mqtt#staging/sensor1"). +### formats + When publishing or subscribing you can specify a format. Available formats are: `string` (default), `json`, `edn` and `transit`: ```clojure (go -- GitLab From 940156388188a9ab32f36e3e16debf0440d13480 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Thu, 27 Sep 2018 13:36:25 +0000 Subject: [PATCH 05/25] Remove CLI for now --- README.md | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 6e470c1..f2fe0cb 100644 --- a/README.md +++ b/README.md @@ -71,11 +71,11 @@ When publishing or subscribing you can specify a format. Available formats are: (mqtt/publish client "temperature/current" {:created-at (js/Date.) :value 12.1} {:format :transit})) ``` -Incoming messages with a payload that is not formattable, won't appear on the subscription-channel. +Incoming messages with a payload that is not readable, won't appear on the subscription-channel. Similarly, when formatting fails when publishing, you'll receive an error: ```clojure -(let [[err _] ( Date: Thu, 27 Sep 2018 13:58:50 +0000 Subject: [PATCH 06/25] Document usage with Node --- README.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f2fe0cb..f0b457f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ eval/otarta {:mvn/version "0.3.0"} ## Usage The following code assumes: -- being in a browser (ie `js/WebSockets` exists) +- being in a browser (ie `js/WebSockets` exists. For Node.js [see below](README.md#nodejs).) - a websocket-enabled MQTT-broker on `localhost:9001` (eg via `docker run --rm -ti -p 9001:9001 toke/mosquitto`) ```clojure @@ -82,11 +82,15 @@ Similarly, when formatting fails when publishing, you'll receive an error: You can provide your own format: ```clojure -(:require [otarta.payload-format :as mqtt-fmt]) +(ns example.core + (:require [otarta.payload-format :as mqtt-fmt])) + +(defn extract-temperature [] + ...) ;; this format piggybacks on the string-format ;; after which extract-temperature will get the relevant data. -;; otarta will catch any exceptions that occur when reading/writing. +;; Otarta will catch any exceptions that occur when reading/writing. (def custom-format (reify mqtt-fmt/PayloadFormat (read [_fmt buff] @@ -95,6 +99,19 @@ You can provide your own format: (->> v (mqtt-fmt/write mqtt-fmt/string))))) ``` +### Node.js + +You should provide a W3C compatible websocket when running via Node.js. +I've had good experience with [this websocket-library (>= v1.0.28)](https://www.npmjs.com/package/websocket). + +With the library included in your project (see https://clojurescript.org/guides/webpack for details), the following will initialize `js/WebSocket`: + +```clojure +(ns example.core + (:require [websocket])) + +(set! js/WebSocket (.-w3cwebsocket websocket)) +``` ## Development -- GitLab From 58b0d8eef90ac465c9db1912ed6e621c8d36df3b Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 11:42:50 +0000 Subject: [PATCH 07/25] Document changes to formats --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f0b457f..7fb7e56 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ The fragment-part is called the `root-topic` and indicates the topic relative to ### formats -When publishing or subscribing you can specify a format. Available formats are: `string` (default), `json`, `edn` and `transit`: +When publishing or subscribing you can specify a format. Available formats are: `string` (default), `raw`, `json`, `edn` and `transit`: ```clojure (go (let [[err {sub-ch :ch}] (> buff (mqtt-fmt/read mqtt-fmt/string) extract-temperature)) - (write [_fmt v] - (->> v (mqtt-fmt/write mqtt-fmt/string))))) + (-read [_fmt buff] + (->> buff (mqtt-fmt/-read mqtt-fmt/string) extract-temperature)) + (-write [_fmt v] + (->> v (mqtt-fmt/-write mqtt-fmt/string))))) ``` ### Node.js -- GitLab From 1d363b3df9d436bf7d35eb61be8b6ffeedeecd4d Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 13:02:54 +0000 Subject: [PATCH 08/25] Document changes from !9 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7fb7e56..f673f15 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The following code assumes: (:require [cljs.core.async :as a :refer [ Date: Mon, 1 Oct 2018 15:03:12 +0000 Subject: [PATCH 09/25] Document shape of message --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index f673f15..c2a5a51 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ The following code assumes: (defn subscription-handler [ch] (go-loop [] (when-let [m ( Date: Mon, 1 Oct 2018 16:00:38 +0000 Subject: [PATCH 10/25] Document limitations --- README.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c2a5a51..2cc4dd8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ An MQTT-library for ClojureScript. -_NOTE: this is pre-alpha software with an API that will change_ +_NOTE: this is pre-alpha software with an API that will change (see the [CHANGELOG](./CHANGELOG.md) for breaking changes)_ ## Installation @@ -32,12 +32,12 @@ The following code assumes: (:require [cljs.core.async :as a :refer [ (figwheel/cljs-repl) ;; prompt changes to: cljs.user> +;; to quickly see what otarta can do +;; you could evaluate the otarta.main namespace +;; and eval the comment-section at the bottom line by line. ``` See [CIDER docs](https://cider.readthedocs.io/en/latest/interactive_programming/) what you can do. -- GitLab From 50f9f437193659a2d50e1e2c1ab426ccbb313a77 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 16:06:12 +0000 Subject: [PATCH 11/25] Document examples --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 2cc4dd8..a6eee14 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Deps: eval/otarta {:mvn/version "0.3.0"} ``` +## Examples + +* [CI-Dashboard](https://eval.gitlab.io/ci-dashboard/) (source: https://gitlab.com/eval/ci-dashboard) + ## Usage The following code assumes: -- GitLab From 17f5b26f96707004c343d003c9e7a887241584a4 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 16:11:35 +0000 Subject: [PATCH 12/25] Copyright updated --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a6eee14..2471a25 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,8 @@ See [CIDER docs](https://cider.readthedocs.io/en/latest/interactive_programming/ ## License -Copyright (c) 2018 Alliander N.V. See [LICENSE](./LICENSE). +Copyright (c) 2018 Gert Goet, ThinkCreate +Copyright (c) 2018 Alliander N.V. +See [LICENSE](./LICENSE). For licenses of third-party software that this software uses, see [LICENSE-3RD-PARTY](./LICENSE-3RD-PARTY). -- GitLab From 12c93fe7e41d3b66e7b3468be5c3bbc8e41417d1 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Sun, 30 Sep 2018 13:39:58 +0200 Subject: [PATCH 13/25] Move formatting out of core --- src/otarta/core.cljs | 41 +---------- src/otarta/payload_format.cljs | 131 ++++++++++++++++++++++++++++----- 2 files changed, 115 insertions(+), 57 deletions(-) diff --git a/src/otarta/core.cljs b/src/otarta/core.cljs index e113aee..c772784 100644 --- a/src/otarta/core.cljs +++ b/src/otarta/core.cljs @@ -26,21 +26,6 @@ (js/Uint8Array. (.-buffer (packet/encode v)))))) -(def payload-formats - {:edn payload-fmt/edn - :raw payload-fmt/raw - :string payload-fmt/string - :transit payload-fmt/transit - :json payload-fmt/json}) - - -(defn- find-payload-format [fmt] - (info :find-payload-format :fmt fmt) - (cond - (satisfies? PayloadFormat fmt) fmt - (contains? payload-formats fmt) (get payload-formats fmt))) - - (defn- app-topic->broker-topic [{{root-topic :root-topic} :config} app-topic] (if root-topic (str root-topic "/" app-topic) @@ -259,26 +244,6 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli (start-pinger))))) -(defn generate-payload-formatter [read-write format] - (if-let [payload-format (find-payload-format format)] - (let [rw (get {:read payload-fmt/read :write payload-fmt/write} read-write) - empty-fmt (reify PayloadFormat - (read [_ _] "") - (write [_ _] (js/Uint8Array.))) - formatter (fn [{e? :empty? :as to-send}] - (info :formatter) - (let [try-format #(try (rw payload-format %) - (catch js/Error _ - (error :format-error) - nil)) - update-fn (if e? (partial rw empty-fmt) try-format) - formatted-payload (-> to-send :payload update-fn)] - (if (nil? formatted-payload) - [:format-error nil] - [nil (assoc to-send :payload formatted-payload)])))] - [nil formatter]) - [:unkown-format nil])) - (defn- publish* [{stream :stream :as client} app-topic msg {:keys [format] :or {format :string}}] (info :publish :client client :app-topic app-topic :msg msg :format format) @@ -288,9 +253,7 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli to-publish {:topic (app-topic->broker-topic client app-topic) :payload msg :empty? empty-msg?} - [fmt-err formatted] (err->> format - (generate-payload-formatter :write) - (#(apply % (list to-publish))))] + [fmt-err formatted] (payload-fmt/write format to-publish)] (if fmt-err [fmt-err nil] (do (>! sink (packet/publish formatted)) @@ -338,7 +301,7 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli (let [{:keys [sink source]} @stream topic-filter (app-topic->broker-topic client app-topic-filter) [sub-err sub-ch] (err->> format - (generate-payload-formatter :read) + (payload-fmt/read) (subscription-chan client topic-filter)) pktid (next-packet-identifier client) sub-pkt (packet/subscribe {:topic-filter topic-filter diff --git a/src/otarta/payload_format.cljs b/src/otarta/payload_format.cljs index e793a53..7ddba54 100644 --- a/src/otarta/payload_format.cljs +++ b/src/otarta/payload_format.cljs @@ -1,8 +1,10 @@ (ns otarta.payload-format + (:refer-clojure :exclude [empty -write]) (:require [cljs.reader :as reader] [cognitect.transit :as transit] [goog.crypt :as crypt] + [otarta.util :refer-macros [err-> err->>]] [huon.log :refer [debug info warn error]])) @@ -13,22 +15,28 @@ - you don't have take the empty-message (\"\") into account - an error should be thrown when reading/writings fails (or would write non-readable data), this signals to the caller that the formatting failed." - (read [format arraybuffer]) - (write [format value])) + (-read [format arraybuffer]) + (-write [format value])) + + +(def empty + (reify PayloadFormat + (-read [_ _] "") + (-write [_ _] (js/Uint8Array.)))) (def raw (reify PayloadFormat - (read [_ buff] buff) - (write [_ v] v))) + (-read [_ buff] buff) + (-write [_ v] v))) (def string (reify PayloadFormat - (read [_ buff] + (-read [_ buff] (info :read-string) (crypt/utf8ByteArrayToString buff)) - (write [_ v] + (-write [_ v] (info :write-string {:value v}) (assert (string? v)) (.from js/Uint8Array (crypt/stringToUtf8ByteArray v))))) @@ -37,14 +45,14 @@ (def json "Read and write data encoded in json" (reify PayloadFormat - (read [_ buff] + (-read [_ buff] (info :read-json) - (let [s (read string buff)] + (let [s (-read string buff)] (info :read-json {:string s}) (->> s js/JSON.parse js->clj))) - (write [_ v] + (-write [_ v] (info :write-json {:value v}) - (->> v clj->js js/JSON.stringify (write string))))) + (->> v clj->js js/JSON.stringify (-write string))))) (def edn @@ -52,29 +60,116 @@ `write` is strict in that it checks whether what it will write is actually readable. This makes writing records impossible." (reify PayloadFormat - (read [_ buff] + (-read [_ buff] (info :read-edn) - (let [s (read string buff)] + (let [s (-read string buff)] (debug :read-edn {:string s}) (reader/read-string s))) - (write [_ v] + (-write [_ v] (info :write-edn {:value v}) (let [to-write (prn-str v) readable? (partial reader/read-string)] (readable? to-write) - (write string to-write))))) + (-write string to-write))))) (def transit "Read and write data encoded in transit+json." (reify PayloadFormat - (read [_ buff] + (-read [_ buff] (info :read-transit) (->> buff - (read string) + (-read string) (transit/read (transit/reader :json)))) - (write [_ v] + (-write [_ v] (info :write-transit) (->> v (transit/write (transit/writer :json)) - (write string))))) + (-write string))))) + + +(def payload-formats + {:edn edn + :raw raw + :string string + :transit transit + :json json}) + + +(defn find-payload-format [fmt] + (info :find-payload-format :fmt fmt) + (cond + (satisfies? PayloadFormat fmt) fmt + (contains? payload-formats fmt) (get payload-formats fmt))) + + +(defn msg-formatter [read-write format] + (if-let [payload-format (find-payload-format format)] + (let [rw (get {:read -read :write -write} read-write) + formatter (fn [{e? :empty? :as msg}] + (info :formatter) + (let [try-format #(try (rw payload-format %) + (catch js/Error _ + (error :format-error) + nil)) + format-fn (if e? (partial rw empty) try-format) + formatted-payload (-> msg :payload format-fn)] + (if (nil? formatted-payload) + [:format-error nil] + [nil (assoc msg :payload formatted-payload)])))] + [nil formatter]) + [:unkown-format nil])) + + +(defn write + "Applies `format` to :payload of `msg` or yields a writer when no `msg` given. + When `msg` has :empty?, `format` is ignored. + + `format` can be one of `payload-formats`, or a reify of PayloadFormat. + + Yields [err msg-with-formatted-payload] + Possible err's: + - :unknown-format + - :format-error + + Examples: + (write :json {:payload \"a\") + ;; => [nil {:payload #object[Uint8Array 34,97,34]}] + + (write :json {:empty? true :payload \"a\"} + ;; => [nil {:payload #object[Uint8Array ]}] +" + ([format] + (err->> format + (msg-formatter :write))) + ([format msg] + (err-> format + (write) + (apply (list msg))))) + + +(defn read + "Applies `format` to :payload of `msg` or yields a reader when no `msg` given. + When `msg` has :empty?, `format` is ignored. + + `format` can be one of payload-formats, or a reify of PayloadFormat. + + Yields [err msg-with-formatted-payload] + Possible err's: + - :unknown-format + - :format-error + + Examples: + (read :json {:payload #js [34,97,34]) + ;; => [nil {:payload \"a\"}] + + (read :json {:empty? true :payload #js [34,97,34]) + ;; => [nil {:empty? true :payload \"\"}] +" + ([format] + (err->> format + (msg-formatter :read))) + ([format msg] + (err-> format + read + (apply (list msg))))) -- GitLab From 464fffb80dedb8edc180ee3b05d2d6cfeec0d624 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Sun, 30 Sep 2018 13:48:07 +0200 Subject: [PATCH 14/25] Just pas a read/write-fn --- src/otarta/payload_format.cljs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/otarta/payload_format.cljs b/src/otarta/payload_format.cljs index 7ddba54..60d5b96 100644 --- a/src/otarta/payload_format.cljs +++ b/src/otarta/payload_format.cljs @@ -9,7 +9,7 @@ (defprotocol PayloadFormat - "Implement read and write operation for reading and writing data to MQTT. + "Implement read and write operation for reading and writing data to Uint8Array. When implementing these functions keep in mind that: - you don't have take the empty-message (\"\") into account @@ -103,10 +103,9 @@ This makes writing records impossible." (contains? payload-formats fmt) (get payload-formats fmt))) -(defn msg-formatter [read-write format] +(defn msg-formatter [rw format] (if-let [payload-format (find-payload-format format)] - (let [rw (get {:read -read :write -write} read-write) - formatter (fn [{e? :empty? :as msg}] + (let [formatter (fn [{e? :empty? :as msg}] (info :formatter) (let [try-format #(try (rw payload-format %) (catch js/Error _ @@ -141,7 +140,7 @@ This makes writing records impossible." " ([format] (err->> format - (msg-formatter :write))) + (msg-formatter -write))) ([format msg] (err-> format (write) @@ -168,7 +167,7 @@ This makes writing records impossible." " ([format] (err->> format - (msg-formatter :read))) + (msg-formatter -read))) ([format msg] (err-> format read -- GitLab From 48d2292135e8fe6751fea7abb3fc52445d50d200 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Sun, 30 Sep 2018 13:56:35 +0200 Subject: [PATCH 15/25] Rename payload-format --- src/otarta/core.cljs | 10 +++++----- src/otarta/{payload_format.cljs => format.cljs} | 2 +- .../{payload_format_test.cljs => format_test.cljs} | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename src/otarta/{payload_format.cljs => format.cljs} (99%) rename test/otarta/{payload_format_test.cljs => format_test.cljs} (97%) diff --git a/src/otarta/core.cljs b/src/otarta/core.cljs index c772784..62c9a8c 100644 --- a/src/otarta/core.cljs +++ b/src/otarta/core.cljs @@ -8,7 +8,7 @@ [haslett.format :as ws-fmt] [huon.log :refer [debug info warn error]] [lambdaisland.uri :as uri] - [otarta.payload-format :as payload-fmt :refer [PayloadFormat]] + [otarta.format :as fmt :refer [PayloadFormat]] [otarta.packet :as packet] [otarta.util :as util :refer-macros [ err-> err->>]])) @@ -253,7 +253,7 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli to-publish {:topic (app-topic->broker-topic client app-topic) :payload msg :empty? empty-msg?} - [fmt-err formatted] (payload-fmt/write format to-publish)] + [fmt-err formatted] (fmt/write format to-publish)] (if fmt-err [fmt-err nil] (do (>! sink (packet/publish formatted)) @@ -273,7 +273,7 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli (publish* topic msg opts)))) -(defn- subscription-chan [{stream :stream :as client} topic-filter payload-formatter] +(defn- subscription-chan [{stream :stream :as client} topic-filter msg-reader] (let [{source :source} @stream pkts-for-topic-filter (packet-filter {[:remaining-bytes :topic] @@ -289,7 +289,7 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli :topic (broker-topic->app-topic client topic)}) subscription-xf (comp pkts-for-topic-filter (map pkt->msg) - (map (comp second payload-formatter)) + (map (comp second msg-reader)) (remove nil?))] [nil (capture-all-packets source subscription-xf)])) @@ -301,7 +301,7 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli (let [{:keys [sink source]} @stream topic-filter (app-topic->broker-topic client app-topic-filter) [sub-err sub-ch] (err->> format - (payload-fmt/read) + fmt/read (subscription-chan client topic-filter)) pktid (next-packet-identifier client) sub-pkt (packet/subscribe {:topic-filter topic-filter diff --git a/src/otarta/payload_format.cljs b/src/otarta/format.cljs similarity index 99% rename from src/otarta/payload_format.cljs rename to src/otarta/format.cljs index 60d5b96..bb14c03 100644 --- a/src/otarta/payload_format.cljs +++ b/src/otarta/format.cljs @@ -1,4 +1,4 @@ -(ns otarta.payload-format +(ns otarta.format (:refer-clojure :exclude [empty -write]) (:require [cljs.reader :as reader] diff --git a/test/otarta/payload_format_test.cljs b/test/otarta/format_test.cljs similarity index 97% rename from test/otarta/payload_format_test.cljs rename to test/otarta/format_test.cljs index ff687d6..0627e45 100644 --- a/test/otarta/payload_format_test.cljs +++ b/test/otarta/format_test.cljs @@ -1,10 +1,10 @@ -(ns otarta.payload-format-test +(ns otarta.format-test (:require [cljs.test :refer [deftest is testing are]] [goog.crypt :as crypt] [goog.object] [huon.log :as log :refer [debug info warn error]] - [otarta.payload-format :as sut])) + [otarta.format :as sut])) (comment ;; handy to create assertions: -- GitLab From 5f3afbc1edd91bb910df280b6bdb5719f7cfc174 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Sun, 30 Sep 2018 13:59:34 +0200 Subject: [PATCH 16/25] Fix failing format-tests --- test/otarta/core_test.cljs | 2 +- test/otarta/format_test.cljs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/otarta/core_test.cljs b/test/otarta/core_test.cljs index 7c93014..5e55050 100644 --- a/test/otarta/core_test.cljs +++ b/test/otarta/core_test.cljs @@ -7,7 +7,7 @@ [goog.crypt :as crypt] [huon.log :as log :refer [debug info warn error]] [otarta.core :as sut] - [otarta.payload-format :as payload-fmt :refer [PayloadFormat]] + [otarta.format :as fmt :refer [PayloadFormat]] [otarta.packet :as pkt] [otarta.util :refer-macros [err-> err->>]] [otarta.test-helpers :as helpers :refer [test-async sub?]])) diff --git a/test/otarta/format_test.cljs b/test/otarta/format_test.cljs index 0627e45..1b42462 100644 --- a/test/otarta/format_test.cljs +++ b/test/otarta/format_test.cljs @@ -13,7 +13,7 @@ (defn- write-and-read [fmt] - #(->> % (sut/write fmt) (sut/read fmt))) + #(->> % (sut/-write fmt) (sut/-read fmt))) (deftest string-test @@ -25,14 +25,14 @@ "some long string"))) (testing "handling non-strings" - (let [fut (partial sut/write sut/string)] + (let [fut (partial sut/-write sut/string)] (is (thrown? js/Error (fut 1))) (is (thrown? js/Error (fut [])))))) (deftest json-test (testing "reading" - (are [buff expected] (= expected (sut/read sut/json buff)) + (are [buff expected] (= expected (sut/-read sut/json buff)) #js [123 34 97 34 58 49 125] {"a" 1} @@ -40,13 +40,13 @@ {"underscored_key" 1})) (testing "reading non-json throws error" - (are [s] (thrown? js/Error (sut/read sut/json (sut/write sut/string s))) + (are [s] (thrown? js/Error (sut/-read sut/json (sut/-write sut/string s))) ";; comment" "hello")) (testing "writing" (are [expected value] (.equals goog.object - expected (sut/write sut/json value)) + expected (sut/-write sut/json value)) ;; stringified and keywordize gets lost in translation #js [123 34 97 34 58 49 125] {"a" 1} @@ -68,13 +68,13 @@ (testing "writing non-edn" (defrecord Foo [a]) - (are [s] (thrown? js/Error (sut/write sut/edn s)) + (are [s] (thrown? js/Error (sut/-write sut/edn s)) #"regex" (fn []) (->Foo 1))) (testing "reading non-edn" - (are [s] (thrown? js/Error (sut/read sut/edn (sut/write sut/string s))) + (are [s] (thrown? js/Error (sut/-read sut/edn (sut/-write sut/string s))) "#\"regex\"" "/nonsense/"))) @@ -90,12 +90,12 @@ (testing "writing non-transit" (defrecord Bar [a]) - (are [s] (thrown? js/Error (sut/write sut/transit s)) + (are [s] (thrown? js/Error (sut/-write sut/transit s)) #"regex" (fn []) (->Bar 1))) (testing "reading non-transit" - (are [s] (thrown? js/Error (sut/read sut/transit (sut/write sut/string s))) + (are [s] (thrown? js/Error (sut/-read sut/transit (sut/-write sut/string s))) "#\"regex\"" "/nonsense/"))) -- GitLab From dc380ca6a6430d861e5e0222ae9d94ce2b5c8f0f Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 11:03:48 +0200 Subject: [PATCH 17/25] Test format/read --- test/otarta/core_test.cljs | 39 +------------------------------ test/otarta/format_test.cljs | 45 ++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/test/otarta/core_test.cljs b/test/otarta/core_test.cljs index 5e55050..02f5950 100644 --- a/test/otarta/core_test.cljs +++ b/test/otarta/core_test.cljs @@ -116,7 +116,7 @@ :topic topic :payload (str->uint8array msg)}))) subscribe! #(-> %3 - (err->> (sut/generate-payload-formatter :read) + (err->> (fmt/read) (sut/subscription-chan %1 %2)) second) messages-received (fn [ch] @@ -235,40 +235,3 @@ (test-async (go (is (= [true false] (->> sub messages-received uint8array "anything")}))) - (is (.equals goog.object (js/Uint8Array.) - (:payload (second (wfut {:empty? true - :payload nil}))))))) - - - (testing "yields :error when formatter fails" - (let [[_ read-json] (sut/generate-payload-formatter :read :json) - [_ write-edn] (sut/generate-payload-formatter :write :edn)] - (is (sub? [:format-error] - (read-json {:payload (str->uint8array "all but json")}))) - (is (sub? [:format-error] - (write-edn {:payload #"no edn"})))))) diff --git a/test/otarta/format_test.cljs b/test/otarta/format_test.cljs index 1b42462..76b6750 100644 --- a/test/otarta/format_test.cljs +++ b/test/otarta/format_test.cljs @@ -4,14 +4,16 @@ [goog.crypt :as crypt] [goog.object] [huon.log :as log :refer [debug info warn error]] - [otarta.format :as sut])) + [otarta.format :as sut :refer [PayloadFormat]] + [otarta.test-helpers :refer [sub?]])) (comment ;; handy to create assertions: (println (crypt/stringToUtf8ByteArray "{\"a\":1}")) ) - +;; formats +;; (defn- write-and-read [fmt] #(->> % (sut/-write fmt) (sut/-read fmt))) @@ -99,3 +101,42 @@ (are [s] (thrown? js/Error (sut/-read sut/transit (sut/-write sut/string s))) "#\"regex\"" "/nonsense/"))) + +;; read message +(deftest read-test + (testing "yields error for unknown format" + (is (sub? [:unkown-format] + (sut/read :foo)))) + + (testing "yields no error for known formats" + (is (sub? [nil] + (sut/read :json))) + (is (sub? [nil] + (sut/read :transit)))) + + (testing "custom format" + (let [my-fmt (reify PayloadFormat + (-read [_ _] "READ") + (-write [_ _] "WRITTEN"))] + (testing "is an acceptable format" + (is (some? (-> my-fmt sut/read second)))) + + (testing "is applied to message's payload" + (is (sub? [nil {:payload "READ"}] + (-> my-fmt (sut/read {:payload #js []}))))) + + (testing "is bypassed when messsage is empty" + (is (sub? [nil {:payload ""}] + (-> my-fmt (sut/read {:empty? true :payload #js []}))))))) + + (testing "yields :format-error for messages with unreadable payloads" + (let [msg-with-payload (fn [s] {:payload (crypt/stringToUtf8ByteArray s)})] + (are [fmt pl error?] (= error? + (-> fmt + (sut/read (msg-with-payload pl)) + first + (= :format-error))) + :json "no json!" true + :json "{\"a\":1}" false + :edn "{\"a\":1}" false + :edn "#\"no edn!\"" true)))) -- GitLab From 1a891d12dd6c9b7ada98ffe4602dcabbf9e0fef0 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 11:17:01 +0200 Subject: [PATCH 18/25] Test writer --- test/otarta/format_test.cljs | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/otarta/format_test.cljs b/test/otarta/format_test.cljs index 76b6750..4808bf7 100644 --- a/test/otarta/format_test.cljs +++ b/test/otarta/format_test.cljs @@ -140,3 +140,47 @@ :json "{\"a\":1}" false :edn "{\"a\":1}" false :edn "#\"no edn!\"" true)))) + +(deftest write-test + (testing "yields error for unknown format" + (is (sub? [:unkown-format] + (sut/write :foo)))) + + (testing "yields no error for known formats" + (is (sub? [nil] + (sut/write :json))) + (is (sub? [nil] + (sut/write :transit)))) + + (testing "custom format" + (let [my-fmt (reify PayloadFormat + (-read [_ _] "READ") + (-write [_ _] "WRITTEN"))] + (testing "is an acceptable format" + (is (some? (-> my-fmt sut/write second)))) + + (testing "is applied to message's payload" + (is (sub? [nil {:payload "WRITTEN"}] + (-> my-fmt (sut/write {:payload "anything"}))))) + + (testing "is bypassed when messsage is empty" + (is (.equals goog.object + (js/Uint8Array.) + (-> my-fmt + (sut/write {:empty? true :payload "anything"}) + second + :payload)))))) + + (testing "yields :format-error for messages with unwriteable payloads" + (let [msg-with-payload (fn [s] {:payload s}) + some-record (defrecord Baz [a])] + (are [fmt pl error?] (= error? + (-> fmt + (sut/write (msg-with-payload pl)) + first + (= :format-error))) + :string 1 true + :string "some string" false + :edn #"no edn!" true + :edn (->Baz 1) true + :edn "real edn" false)))) -- GitLab From a33d45ffbea4317885901bf28dbdfdd7bc5c89fd Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 12:46:53 +0200 Subject: [PATCH 19/25] Add empty-format and document --- src/otarta/format.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/otarta/format.cljs b/src/otarta/format.cljs index bb14c03..35a784f 100644 --- a/src/otarta/format.cljs +++ b/src/otarta/format.cljs @@ -12,9 +12,8 @@ "Implement read and write operation for reading and writing data to Uint8Array. When implementing these functions keep in mind that: - - you don't have take the empty-message (\"\") into account - - an error should be thrown when reading/writings fails (or would write non-readable data), - this signals to the caller that the formatting failed." + - the format is bypassed for messages containing {:empty? true} (ie no need to handle writing/reading \"\") + - an error should be thrown when reading/writings fails (or would write non-readable data). This signals to the caller that the formatting failed." (-read [format arraybuffer]) (-write [format value])) @@ -90,6 +89,7 @@ This makes writing records impossible." (def payload-formats {:edn edn + :empty empty :raw raw :string string :transit transit @@ -122,7 +122,7 @@ This makes writing records impossible." (defn write "Applies `format` to :payload of `msg` or yields a writer when no `msg` given. - When `msg` has :empty?, `format` is ignored. + When `msg` has [:empty? true], the empty-format is used instead of `format`. `format` can be one of `payload-formats`, or a reify of PayloadFormat. @@ -149,7 +149,7 @@ This makes writing records impossible." (defn read "Applies `format` to :payload of `msg` or yields a reader when no `msg` given. - When `msg` has :empty?, `format` is ignored. + When `msg` contains [:empty? true], the empty-format is used instead of `format`. `format` can be one of payload-formats, or a reify of PayloadFormat. -- GitLab From 498769e52036cfcd51d7183bb6023ae666e66612 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 13:12:35 +0200 Subject: [PATCH 20/25] Determine empty of payload --- src/otarta/core.cljs | 36 +++++++++++++++++++++++++++--------- test/otarta/format_test.cljs | 3 ++- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/otarta/core.cljs b/src/otarta/core.cljs index 62c9a8c..09c95eb 100644 --- a/src/otarta/core.cljs +++ b/src/otarta/core.cljs @@ -13,6 +13,25 @@ [otarta.util :as util :refer-macros [ err-> err->>]])) +(extend-type js/Uint8Array + ICounted + (-count [uia] + (.-length uia))) + + +(defn- empty-payload? + "Examples: + ;; when receiving: + (empty-payload? (js/Uint8Array.) ;; => true + + ;; when sending: + (empty-payload? nil) ;; => true + (empty-payload? \"\") ;; => true + (empty-payload? \" \") ;; => false" + [pl] + (zero? (count pl))) + + (def mqtt-format "Read and write mqtt-packets" (reify ws-fmt/Format @@ -245,14 +264,13 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli -(defn- publish* [{stream :stream :as client} app-topic msg {:keys [format] :or {format :string}}] - (info :publish :client client :app-topic app-topic :msg msg :format format) +(defn- publish* [{stream :stream :as client} app-topic payload {:keys [format] :or {format :string}}] + (info :publish :client client :app-topic app-topic :payload payload :format format) (go (let [{sink :sink} @stream - empty-msg? (or (nil? msg) (= "" msg)) to-publish {:topic (app-topic->broker-topic client app-topic) - :payload msg - :empty? empty-msg?} + :payload payload + :empty? (empty-payload? payload)} [fmt-err formatted] (fmt/write format to-publish)] (if fmt-err [fmt-err nil] @@ -266,11 +284,11 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli Currently `err` is always nil as no check is done whether the underlying connection is active, nor whether the broker received the message (ie qos 0)." - ([client topic msg] (publish client topic msg {})) - ([client topic msg opts] + ([client topic payload] (publish client topic payload {})) + ([client topic payload opts] ( client connect - (publish* topic msg opts)))) + (publish* topic payload opts)))) (defn- subscription-chan [{stream :stream :as client} topic-filter msg-reader] @@ -282,7 +300,7 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli {topic :topic} :remaining-bytes {payload :payload} :extra}] {:dup? dup? - :empty? (-> payload .-byteLength zero?) + :empty? (empty-payload? payload) :payload payload :qos qos :retained? retain? diff --git a/test/otarta/format_test.cljs b/test/otarta/format_test.cljs index 4808bf7..df932d0 100644 --- a/test/otarta/format_test.cljs +++ b/test/otarta/format_test.cljs @@ -48,7 +48,8 @@ (testing "writing" (are [expected value] (.equals goog.object - expected (sut/-write sut/json value)) + (js/Uint8Array. expected) + (sut/-write sut/json value)) ;; stringified and keywordize gets lost in translation #js [123 34 97 34 58 49 125] {"a" 1} -- GitLab From 056b162c88d4c0f3a4eb83ec9b57e24934e97d57 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 13:13:04 +0200 Subject: [PATCH 21/25] Document running specific tests --- README.md | 9 +++++++++ test/otarta/format_test.cljs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2471a25..d8e3a6e 100644 --- a/README.md +++ b/README.md @@ -154,12 +154,21 @@ With the library included in your project (see https://clojurescript.org/guides/ ### Testing +Via [cljs-test-runner](https://github.com/Olical/cljs-test-runner/): + ```bash # once $ clojure -Atest # watching $ clojure -Atest-watch + +# specific tests +(deftest ^{:focus true} only-this-test ...) +$ clojure -Atest-watch -i :focus + +# more options: +$ clojure -Atest-watch --help ``` ### Figwheel diff --git a/test/otarta/format_test.cljs b/test/otarta/format_test.cljs index df932d0..bde071d 100644 --- a/test/otarta/format_test.cljs +++ b/test/otarta/format_test.cljs @@ -32,7 +32,7 @@ (is (thrown? js/Error (fut [])))))) -(deftest json-test +(deftest ^{:focus true} json-test (testing "reading" (are [buff expected] (= expected (sut/-read sut/json buff)) #js [123 34 97 34 58 49 125] -- GitLab From 317510521d6433b438a7249849b8dfb1f95a094e Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 11:33:34 +0000 Subject: [PATCH 22/25] Add CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0804908 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.3.0 / UNRELEASED + +### Breaking + -- GitLab From 653c828484e29706d98712803d1c6cf7c90202c7 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 11:36:33 +0000 Subject: [PATCH 23/25] Document changes to formats --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0804908..157aff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,3 +2,5 @@ ### Breaking +* Formats: `PayloadFormat` moved to `otarta.format` (was `otarta.payload-format`) +* Formats: Implement `-read` and `-write` (was `read` and `write`) \ No newline at end of file -- GitLab From 1094516d616530a796704501cb9551174a2a7226 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 14:41:30 +0200 Subject: [PATCH 24/25] API changes - (breaking) (client "broker" {}) instead of (client {:broker-url ...}) - (breaking) received messages have :retain? instead of :retained? - client-id-prefix added as client-opt - retain? as publish-opt --- src/otarta/core.cljs | 63 ++++++++++++++++++++++++++------------ src/otarta/main.cljs | 6 ++-- test/otarta/core_test.cljs | 19 ++++++++++-- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/otarta/core.cljs b/src/otarta/core.cljs index 09c95eb..d37c329 100644 --- a/src/otarta/core.cljs +++ b/src/otarta/core.cljs @@ -229,22 +229,44 @@ [nil client])) +(defn- client-id + "Yields `client-id` if provided. Otherwise generated of `client-id-prefix` (default \"otarta\") and random characters. The generated id is compliant with [MQTT-3.1.3-5] (ie matches [0-9a-zA-Z]{1-23})." + [{:keys [client-id client-id-prefix]}] + (if (some? client-id) + client-id + (let [prefix (or client-id-prefix "otarta")] + (-> prefix + (str (random-uuid)) + (string/replace #"-" "") + (subs 0 23))))) + + (defn client - "Accepts the following parameters: - - broker-url (required) - url of the form ws(s)://(user:password@)host:12345/path(#some/root-topic). -The root-topic is prepended to all subscribes/publishes and ensures that the client only needs to care about topics that are relevant for the application, e.g. \"temperature/current\" (instead of \"staging/sensor0/temperature/current\"). You can provide a default-topic-root. + "Initialize a client for publish/subscribe. + + Arguments: + - broker-url - url of the form ws(s)://(user:password@)host:12345/path(#some/root-topic). + The root-topic is prepended to all subscribes/publishes and ensures that the client only needs to care about topics that are relevant for the application, e.g. \"temperature/current\" (instead of \"staging/sensor0/temperature/current\"). You can provide a default-root-topic. + + Accepts the following options: - default-root-topic - root-topic used when broker-url does not contain one. This e.g. allows the client-logic to subscribe to \"#\" knowing that it won't subscribe to the root of a broker. - keep-alive (default 60) - maximum seconds between pings. - - client-id (default \"otarta-\") - Client Identifier used to connect to broker. + - client-id (default \"\" (max. 23 characters as per MQTT-spec)) - Client Identifier used to connect to broker. This should be unique accross all connected clients. +WARNING: Connecting with a client-id that's already in use results in the existing client being disconnected. + - client-id-prefix (default \"otarta\") - convenient to see in the logs of your broker where the client originates from without running the risk of clashing with existing client-id's. " - [{:keys [broker-url default-root-topic] :as opts}] - {:pre [broker-url]} - (let [default-opts {:keep-alive 60 :client-id (str "otarta-" (random-uuid))} - config (-> broker-url - (parse-broker-url {:default-root-topic default-root-topic}) - (merge default-opts) - (merge (select-keys opts [:client-id :keep-alive])))] - {:config config :stream (atom nil) :pinger (atom nil) :packet-identifier (atom 0)})) + ([broker-url] (client broker-url {})) + ([broker-url {:keys [default-root-topic] :as opts}] + (let [default-opts {:keep-alive 60} + config (-> broker-url + (parse-broker-url {:default-root-topic default-root-topic}) + (assoc :client-id (client-id opts)) + (merge default-opts) + (merge (select-keys opts [:keep-alive])))] + {:config config + :stream (atom nil) + :pinger (atom nil) + :packet-identifier (atom 0)}))) (defn connect @@ -264,11 +286,12 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli -(defn- publish* [{stream :stream :as client} app-topic payload {:keys [format] :or {format :string}}] +(defn- publish* [{stream :stream :as client} app-topic payload {:keys [format retain?] :or {format :string retain? false}}] (info :publish :client client :app-topic app-topic :payload payload :format format) (go (let [{sink :sink} @stream to-publish {:topic (app-topic->broker-topic client app-topic) + :retain? retain? :payload payload :empty? (empty-payload? payload)} [fmt-err formatted] (fmt/write format to-publish)] @@ -292,19 +315,19 @@ The root-topic is prepended to all subscribes/publishes and ensures that the cli (defn- subscription-chan [{stream :stream :as client} topic-filter msg-reader] - (let [{source :source} @stream + (let [{source :source} @stream pkts-for-topic-filter (packet-filter {[:remaining-bytes :topic] (partial topic-filter-matches-topic? topic-filter)}) pkt->msg (fn [{{:keys [retain? dup? qos]} :first-byte {topic :topic} :remaining-bytes {payload :payload} :extra}] - {:dup? dup? - :empty? (empty-payload? payload) - :payload payload - :qos qos - :retained? retain? - :topic (broker-topic->app-topic client topic)}) + {:dup? dup? + :empty? (empty-payload? payload) + :payload payload + :qos qos + :retain? retain? + :topic (broker-topic->app-topic client topic)}) subscription-xf (comp pkts-for-topic-filter (map pkt->msg) (map (comp second msg-reader)) diff --git a/src/otarta/main.cljs b/src/otarta/main.cljs index fc91e58..14e9273 100644 --- a/src/otarta/main.cljs +++ b/src/otarta/main.cljs @@ -17,7 +17,7 @@ (defn handle-sub [broker-url topic-filter] (info :handle-sub :broker-url broker-url :topic-filter topic-filter) (go - (reset! client (mqtt/client {:broker-url broker-url})) + (reset! client (mqtt/client broker-url)) (let [[err {sub-ch :ch}] ( "ws://localhost" (sut/client) :config :client-id)))) + (testing "when prefix provided has correct chars and length" + (is (re-find #"origin[a-zA-Z0-9]{17}$" + (-> "ws://localhost" + (sut/client {:client-id-prefix "origin"}) + :config + :client-id)))) + (testing "when client-id provided it's used as-is" + (is (= "custom-client" + (-> "ws://localhost" + (sut/client {:client-id "custom-client"}) + :config + :client-id))))))) (deftest topic-filter-matches-topic?-test -- GitLab From 348f49d9631bd2d2e0652c101a74797f3e74ba2e Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Mon, 1 Oct 2018 18:27:49 +0200 Subject: [PATCH 25/25] Wording --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d8e3a6e..a3b4860 100644 --- a/README.md +++ b/README.md @@ -186,9 +186,9 @@ $ node target/app.js user> (figwheel/cljs-repl) ;; prompt changes to: cljs.user> -;; to quickly see what otarta can do -;; you could evaluate the otarta.main namespace -;; and eval the comment-section at the bottom line by line. +;; to quickly see what otarta can do: +;; - evaluate the otarta.main namespace +;; - then eval the comment-section of otarta.main line by line ``` See [CIDER docs](https://cider.readthedocs.io/en/latest/interactive_programming/) what you can do. -- GitLab