-
Notifications
You must be signed in to change notification settings - Fork 18
feat: allow selecting payload encoding per value #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
b6b4c05
a707abf
037e8c4
e3e1505
e3d50d4
47f4169
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,77 @@ | ||
| (ns temporal.converter.default | ||
| (:require | ||
| [temporal.converter.encoding :as encoding] | ||
| [temporal.converter.json :as json] | ||
| [temporal.converter.nippy :as nippy]) | ||
| (:import | ||
| [io.temporal.common.converter DefaultDataConverter NullPayloadConverter PayloadConverter])) | ||
| [io.temporal.common.converter | ||
| DataConverterException | ||
| DefaultDataConverter | ||
| NullPayloadConverter | ||
| PayloadAndFailureDataConverter | ||
| PayloadConverter] | ||
| [java.lang.reflect Field] | ||
| [java.util Map])) | ||
|
|
||
| (set! *warn-on-reflection* true) | ||
|
|
||
| (def standard-converters | ||
| [(NullPayloadConverter.) | ||
| (nippy/create) | ||
| (json/create)]) | ||
|
|
||
| (defn get-field ^Field [field] | ||
| (doto (.getDeclaredField PayloadAndFailureDataConverter field) | ||
| (.setAccessible true))) | ||
|
|
||
| (def ^Field converters-field (get-field "converters")) | ||
|
|
||
| (def ^Field converters-map-field (get-field "convertersMap")) | ||
|
|
||
| (def ^Field failure-converter-field (get-field "failureConverter")) | ||
|
|
||
| (def ^Field serialization-context-field (get-field "serializationContext")) | ||
|
|
||
| (defn get-converter [^PayloadAndFailureDataConverter data-converter encoding] | ||
| (let [converters-map (.get converters-map-field data-converter) | ||
| converter (.get ^Map converters-map encoding)] | ||
| (when converter | ||
| (if-let [serialization-context (get serialization-context-field data-converter)] | ||
| (.withContext ^PayloadConverter converter serialization-context) | ||
| converter)))) | ||
|
|
||
| (defn encoded->payload | ||
| "This is a custom re-implementation of the PayloadAndFailureDataConverter/toPayload" | ||
| [^PayloadAndFailureDataConverter data-converter {:keys [value encoding]}] | ||
| (if-let [converter (get-converter data-converter encoding)] | ||
| (let [result (.toData ^PayloadConverter converter value)] | ||
| (if (.isPresent result) | ||
| result | ||
| (throw (DataConverterException. | ||
| (str "Cannot encode value with the selected converter: " value))))) | ||
| (throw (DataConverterException. | ||
| (str "No PayloadConverter is registered for this encoding: " encoding))))) | ||
|
|
||
| (defn create | ||
| (^DefaultDataConverter [] | ||
| (create standard-converters)) | ||
|
|
||
| (^DefaultDataConverter [converters source-data-converter] | ||
| (let [data-converter (create converters)] | ||
| (doseq [^Field field [converters-field converters-map-field failure-converter-field]] | ||
| (->> (.get field source-data-converter) | ||
| (.set field data-converter))) | ||
| data-converter)) | ||
|
|
||
| (^DefaultDataConverter [converters] | ||
| (DefaultDataConverter. | ||
| (into-array PayloadConverter converters)))) | ||
| (proxy [DefaultDataConverter] [(into-array PayloadConverter converters)] | ||
| (withContext [context] | ||
| (let [data-converter (create converters this)] | ||
| (.set serialization-context-field data-converter context) | ||
| data-converter)) | ||
|
|
||
| (toPayload [value] | ||
| (let [^PayloadAndFailureDataConverter this this] | ||
| (if (encoding/tagged? value) | ||
| (encoded->payload this value) | ||
| (proxy-super toPayload value))))))) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| (ns temporal.converter.encoding) | ||
|
|
||
| (defrecord Tagged [value encoding]) | ||
|
|
||
| (def mapping | ||
| {:null "binary/null" | ||
| :nippy "binary/plain" | ||
| :json "json/plain"}) | ||
|
|
||
| (defn tagged? [x] | ||
| (instance? Tagged x)) | ||
|
|
||
| (defn with [value encoding] | ||
| (->Tagged value (get mapping encoding encoding))) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,22 @@ | ||
| (ns temporal.converter.default-test | ||
| (:require | ||
| [clojure.test :refer [are deftest is]] | ||
| [clojure.template :refer [do-template]] | ||
| [clojure.test :refer [are deftest is testing]] | ||
| [jsonista.core :as json] | ||
| [taoensso.nippy :as nippy] | ||
| [temporal.converter.default :as sut] | ||
| [temporal.converter.encoding :as encoding] | ||
| [temporal.converter.metadata :refer [->metadata]] | ||
| [temporal.converter.payload :refer [->payload]]) | ||
| (:import | ||
| [io.temporal.api.common.v1 Payload] | ||
| [io.temporal.common.converter DataConverterException EncodingKeys NullPayloadConverter])) | ||
| [io.temporal.common.converter | ||
| DataConverterException | ||
| EncodingKeys | ||
| NullPayloadConverter | ||
| PayloadAndFailureDataConverter] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused import |
||
| [io.temporal.payload.context WorkflowSerializationContext] | ||
| [java.lang.reflect Field])) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused import |
||
|
|
||
| (defn payload->encoding [^Payload payload] | ||
| (.. payload | ||
|
|
@@ -23,31 +32,90 @@ | |
| (and (= "binary/plain" (payload->encoding payload)) | ||
| (= expected (-> payload | ||
| (.. (getData) (toByteArray)) | ||
| nippy/thaw))))) | ||
| (nippy/thaw)))))) | ||
|
|
||
| (defn json-payload? [expected] | ||
| (fn [^Payload payload] | ||
| (and (= "json/plain" (payload->encoding payload)) | ||
| (= expected (-> payload | ||
| (.. (getData) (toByteArray)) | ||
| (json/read-value json/keyword-keys-object-mapper)))))) | ||
|
|
||
| (def context | ||
| (WorkflowSerializationContext. "test" "test")) | ||
|
|
||
| (deftest to-payload-with-default-converters | ||
| (let [data-converter (sut/create)] | ||
| (are [predicate input-value] (-> data-converter | ||
| (.toPayload input-value) | ||
| (.get) | ||
| (predicate)) | ||
| null-payload? nil | ||
| (nippy-payload? :something) :something | ||
| (nippy-payload? "Something") "Something" | ||
| (nippy-payload? [1 2 3 4]) [1 2 3 4] | ||
| (nippy-payload? {:foo "bar"}) {:foo "bar"}))) | ||
| (do-template | ||
| [predicate input-value] | ||
| (let [data-converter (sut/create)] | ||
| (testing "withhout-context" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo |
||
| (is (predicate (-> data-converter | ||
| (.toPayload input-value) | ||
| (.get))))) | ||
|
|
||
| (testing "with-context" | ||
| (let [data-converter (.withContext data-converter context)] | ||
| (is (= context (.get sut/serialization-context-field data-converter))) | ||
| (is (predicate (-> data-converter | ||
| (.toPayload input-value) | ||
| (.get))))))) | ||
| null-payload? nil | ||
| null-payload? (encoding/with nil :null) | ||
| null-payload? (encoding/with nil "binary/null") | ||
|
|
||
| (nippy-payload? :something) :something | ||
| (nippy-payload? "Something") "Something" | ||
| (nippy-payload? [1 2 3 4]) [1 2 3 4] | ||
| (nippy-payload? {:foo "bar"}) {:foo "bar"} | ||
|
|
||
| (nippy-payload? :something) (encoding/with :something :nippy) | ||
| (nippy-payload? "Something") (encoding/with "Something" :nippy) | ||
| (nippy-payload? [1 2 3 4]) (encoding/with [1 2 3 4] :nippy) | ||
| (nippy-payload? {:foo "bar"}) (encoding/with {:foo "bar"} :nippy) | ||
|
|
||
| (nippy-payload? :something) (encoding/with :something "binary/plain") | ||
| (nippy-payload? "Something") (encoding/with "Something" "binary/plain") | ||
| (nippy-payload? [1 2 3 4]) (encoding/with [1 2 3 4] "binary/plain") | ||
| (nippy-payload? {:foo "bar"}) (encoding/with {:foo "bar"} "binary/plain") | ||
|
|
||
| (json-payload? nil) (encoding/with nil :json) | ||
| (json-payload? "Something") (encoding/with "Something" :json) | ||
| (json-payload? [1 2 3 4]) (encoding/with [1 2 3 4] :json) | ||
| (json-payload? {:foo "bar"}) (encoding/with {:foo "bar"} :json) | ||
|
|
||
| (json-payload? nil) (encoding/with nil "json/plain") | ||
| (json-payload? "Something") (encoding/with "Something" "json/plain") | ||
| (json-payload? [1 2 3 4]) (encoding/with [1 2 3 4] "json/plain") | ||
| (json-payload? {:foo "bar"}) (encoding/with {:foo "bar"} "json/plain"))) | ||
|
|
||
| (deftest to-payload-with-custom-converters | ||
| (let [data-converter (sut/create [(NullPayloadConverter.)])] | ||
| (is (null-payload? (.. data-converter (toPayload nil) (get)))) | ||
| (are [input-value] (null-payload? (.. data-converter (toPayload input-value) (get))) | ||
| nil | ||
| (encoding/with nil :null)) | ||
|
|
||
| (are [input-value] (thrown-with-msg? DataConverterException | ||
| #"No PayloadConverter is registered with this DataConverter" | ||
| (.toPayload data-converter input-value)) | ||
| :something | ||
| "Something" | ||
| [1 2 3 4] | ||
| {:foo "bar"}))) | ||
| {:foo "bar"}) | ||
|
|
||
| (are [input-value] (thrown-with-msg? DataConverterException | ||
| #"Cannot encode value with the selected converter" | ||
| (.toPayload data-converter input-value)) | ||
| (encoding/with :something :null) | ||
| (encoding/with :something "binary/null")) | ||
|
|
||
| (are [input-value] (thrown-with-msg? DataConverterException | ||
| #"No PayloadConverter is registered for this encoding" | ||
| (.toPayload data-converter input-value)) | ||
| (encoding/with nil :nippy) | ||
| (encoding/with nil "binary/plain") | ||
|
|
||
| (encoding/with nil :json) | ||
| (encoding/with nil "json/plain")))) | ||
|
|
||
| (def metadata-null | ||
| (->metadata "binary/null")) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be (.get) instead of (get)?
Coverage indicates that the true condition is never hit, which might be related to the above comment.