Implement possibility for async command handlers (#1857)

* Implement possibility for async command handlers

Command handler can now return results asynchronously,
and those results as saved and persisted, indexed by
command message-id.

* Address feedback raised in PR comments

* Different handler codes + simplified error message
This commit is contained in:
Jan Herich 2017-09-18 15:08:06 +02:00 committed by Roman Volosovskyi
parent 0fa863d366
commit b889a07a86
15 changed files with 200 additions and 21 deletions

View File

@ -352,11 +352,26 @@ function handleSend(params, context) {
gasPrice: calculateGasPrice(params["bot-db"]["sliderValue"])
};
try {
return web3.eth.sendTransaction(data);
} catch (err) {
return {error: err.message};
}
web3.eth.sendTransaction(data, function(error, hash) {
if (error) {
status.sendSignal("handler-data", {
status: "failed",
messageId: context["message-id"],
error: error
});
} else {
status.sendSignal("handler-data", {
status: "sent",
messageId: context["message-id"],
hash: hash
});
}
});
return {
status: 'not-confirmed'
};
}
function previewSend(params, context) {
@ -435,6 +450,36 @@ function previewSend(params, context) {
markup = [firstRow];
}
if (!(context["handler-data"]
&& context["handler-data"]["status"] === "sent")) {
var pendingRow = status.components.text(
{
style: {
color: "#9199a0",
fontSize: 12,
lineHeight: 18
}
},
I18n.t('send_transaction_pending')
);
markup.push(pendingRow);
}
if (context["handler-data"]
&& context["handler-data"]["status"] === "failed") {
var errorRow = status.components.text(
{
style: {
color: "red",
fontSize: 12,
lineHeight: 18
}
},
I18n.t('send_transaction_failed')
);
markup.push(errorRow);
}
return {
markup: status.components.view(
{

View File

@ -14,6 +14,8 @@ I18n.translations = {
send_explanation_3: 'probably within 30 seconds.',
send_explanation_4: 'probably within a few seconds.',
send_sending_to: 'to ',
send_transaction_pending: 'transaction pending',
send_transaction_failed: 'transaction failed',
eth: 'ETH',

View File

@ -13,13 +13,14 @@
(defn generate-context
"Generates context for jail call"
[{:keys [chats] :accounts/keys [current-account-id]} chat-id to group-id]
(merge {:platform platform/platform
:from current-account-id
:to to
:chat {:chat-id chat-id
:group-chat (or (get-in chats [chat-id :group-chat])
(not (nil? group-id)))}}
[{:keys [chats handler-data] :accounts/keys [current-account-id]} message-id chat-id to group-id]
(merge {:platform platform/platform
:from current-account-id
:to to
:chat {:chat-id chat-id
:group-chat (or (get-in chats [chat-id :group-chat])
(not (nil? group-id)))}
:handler-data (get handler-data message-id)}
i18n/delimeters))
;;;; Coeffects
@ -69,7 +70,7 @@
[trim-v]
(fn [{:keys [db]}
[{{:keys [command content-command params type]} :content
:keys [chat-id jail-id group-id] :as message}
:keys [chat-id jail-id group-id message-id] :as message}
data-type]]
(let [{:keys [chats]
:accounts/keys [current-account-id]
@ -84,7 +85,7 @@
data-type]
to (get-in contacts [chat-id :address])
jail-params {:parameters params
:context (generate-context db chat-id to group-id)}]
:context (generate-context db message-id chat-id to group-id)}]
{:chat-fx/call-jail {:jail-id jail-id
:path path
:params jail-params

View File

@ -10,6 +10,7 @@
[status-im.data-store.chats :as chats]
[status-im.data-store.contacts :as contacts]
[status-im.data-store.messages :as messages]
[status-im.data-store.handler-data :as handler-data]
[status-im.data-store.pending-messages :as pending-messages]
[status-im.constants :refer [text-content-type
content-type-command
@ -219,6 +220,7 @@
(-> db
(assoc :chats chats')
(assoc :handler-data (handler-data/get-all))
(dissoc :loaded-chats)
(init-console-chat true))))

View File

@ -2,6 +2,7 @@
(:require [status-im.utils.handlers :refer [register-handler] :as u]
[clojure.string :as s]
[status-im.data-store.messages :as messages]
[status-im.data-store.handler-data :as handler-data]
[status-im.native-module.core :as status]
[status-im.utils.random :as random]
[status-im.utils.datetime :as time]
@ -105,9 +106,20 @@
params' (assoc params :command content')]
(dispatch [:prepare-command! wallet-chat-id params'])))))))
(register-handler ::check-preview-refetch
(fn [db [_ chat-id {:keys [message-id] :as message}]]
(let [handler-data (get-in db [:handler-data message-id])]
(if (:fetch-preview handler-data)
(do (dispatch [:request-command-data (assoc message :jail-id chat-id) :preview])
(handler-data/save-data {:message-id message-id
:data (dissoc handler-data :fetch-preview)})
(update-in db [:handler-data message-id] dissoc :fetch-preview))
db))))
(register-handler ::send-command!
(u/side-effect!
(fn [_ [_ add-to-chat-id params hidden-params]]
(dispatch [::check-preview-refetch add-to-chat-id (:command params)])
(dispatch [::add-command add-to-chat-id params])
(dispatch [::save-command! add-to-chat-id params hidden-params])
(when (not= add-to-chat-id wallet-chat-id)
@ -150,7 +162,7 @@
to (get-in contacts [chat-id :address])
identity (or owner-id bot chat-id)
bot-db (get bot-db (or bot chat-id))
params {:parameters params
jail-params {:parameters params
:context {:from address
:to to
:current-account (get accounts current-account-id)
@ -161,7 +173,7 @@
#(status/call-jail
{:jail-id identity
:path [handler-type name :handler]
:params params
:params jail-params
:callback (fn [res]
(dispatch [:command-handler! chat-id orig-params res]))})])))))

View File

@ -13,6 +13,7 @@
(s/def :chat/expandable-view-height-to-value (s/nilable number?))
(s/def :chat/global-commands (s/nilable map?)) ; {key (keyword) command (map)} atm used for browse command
(s/def :chat/loading-allowed (s/nilable boolean?)) ;;allow to load more messages
(s/def :chat/handler-data (s/nilable map?))
(s/def :chat/message-data (s/nilable map?))
(s/def :chat/message-id->transaction-id (s/nilable map?))
(s/def :chat/message-status (s/nilable map?))
@ -29,4 +30,4 @@
(s/def :chat/new-request (s/nilable map?))
(s/def :chat/raw-unviewed-messages (s/nilable vector?))
(s/def :chat/bot-db (s/nilable map?))
(s/def :chat/geolocation (s/nilable map?))
(s/def :chat/geolocation (s/nilable map?))

View File

@ -0,0 +1,31 @@
(ns status-im.commands.events.jail
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.data-store.handler-data :as handler-data]
[taoensso.timbre :as log]))
(re-frame/reg-fx
:save-handler-data
(fn [data]
(handler-data/save-data data)))
(handlers/register-handler-fx
:set-handler-data
[re-frame/trim-v]
(fn [{:keys [db]} [chat-id {:keys [messageId] :as data}]]
(let [;; this is very bad, we should refactor our db ASAP
message (->> (get-in db [:chats chat-id :messages])
(filter #(= (:message-id %) messageId))
first)
handler-data (cond-> (dissoc data :messageId)
;; message not there yet, indicate we want to re-fetch preview once it lands there
(nil? message)
(assoc :fetch-preview true))
old-handler-data (get-in db [:handler-data messageId] {})
new-handler-data (merge old-handler-data handler-data)]
(cond-> {:db (assoc-in db [:handler-data messageId] new-handler-data)
:save-handler-data {:message-id messageId
:data new-handler-data}}
;; message was already added to db, we can re-fetch preview
(not (nil? message))
(assoc :dispatch [:request-command-data (assoc message :jail-id chat-id) :preview])))))

View File

@ -10,7 +10,8 @@
[status-im.constants :refer [console-chat-id]]
[status-im.i18n :refer [get-contact-translated]]
[taoensso.timbre :as log]
[status-im.data-store.local-storage :as local-storage]))
[status-im.data-store.local-storage :as local-storage]
status-im.commands.events.jail))
(defn command-handler!
[_ [chat-id

View File

@ -0,0 +1,16 @@
(ns status-im.data-store.handler-data
(:require [cljs.reader :as reader]
[status-im.data-store.realm.handler-data :as data-store]
[taoensso.timbre :as log]))
(defn get-all []
(->> (data-store/get-all-as-list)
(map (fn [{:keys [message-id data]}]
[message-id (reader/read-string data)]))
(into {})))
(defn get-data [message-id]
(-> message-id data-store/get-by-message-id :data reader/read-string))
(defn save-data [handler-data]
(data-store/save (update handler-data :data pr-str)))

View File

@ -0,0 +1,16 @@
(ns status-im.data-store.realm.handler-data
(:require [status-im.data-store.realm.core :as realm]))
(defn get-all []
(realm/get-all @realm/account-realm :handler-data))
(defn get-all-as-list []
(realm/realm-collection->list (get-all)))
(defn get-by-message-id
[message-id]
(realm/get-one-by-field-clj @realm/account-realm :handler-data :message-id message-id))
(defn save
[handler-data]
(realm/save @realm/account-realm :handler-data handler-data true))

View File

@ -11,7 +11,7 @@
[status-im.data-store.realm.schemas.account.v10.core :as v10]
[status-im.data-store.realm.schemas.account.v11.core :as v11]
[status-im.data-store.realm.schemas.account.v12.core :as v12]
))
[status-im.data-store.realm.schemas.account.v13.core :as v13]))
;; put schemas ordered by version
(def schemas [{:schema v1/schema
@ -49,4 +49,7 @@
:migration v11/migration}
{:schema v12/schema
:schemaVersion 12
:migration v12/migration}])
:migration v12/migration}
{:schema v13/schema
:schemaVersion 13
:migration v13/migration}])

View File

@ -0,0 +1,40 @@
(ns status-im.data-store.realm.schemas.account.v13.core
(:require [status-im.data-store.realm.schemas.account.v11.chat :as chat]
[status-im.data-store.realm.schemas.account.v1.chat-contact :as chat-contact]
[status-im.data-store.realm.schemas.account.v6.command :as command]
[status-im.data-store.realm.schemas.account.v9.command-parameter :as command-parameter]
[status-im.data-store.realm.schemas.account.v7.contact :as contact]
[status-im.data-store.realm.schemas.account.v1.discover :as discover]
[status-im.data-store.realm.schemas.account.v1.kv-store :as kv-store]
[status-im.data-store.realm.schemas.account.v10.message :as message]
[status-im.data-store.realm.schemas.account.v12.pending-message :as pending-message]
[status-im.data-store.realm.schemas.account.v1.processed-message :as processed-message]
[status-im.data-store.realm.schemas.account.v1.request :as request]
[status-im.data-store.realm.schemas.account.v1.tag :as tag]
[status-im.data-store.realm.schemas.account.v1.user-status :as user-status]
[status-im.data-store.realm.schemas.account.v5.contact-group :as contact-group]
[status-im.data-store.realm.schemas.account.v5.group-contact :as group-contact]
[status-im.data-store.realm.schemas.account.v8.local-storage :as local-storage]
[status-im.data-store.realm.schemas.account.v13.handler-data :as handler-data]
[taoensso.timbre :as log]))
(def schema [chat/schema
chat-contact/schema
command/schema
command-parameter/schema
contact/schema
discover/schema
kv-store/schema
message/schema
pending-message/schema
processed-message/schema
request/schema
tag/schema
user-status/schema
contact-group/schema
group-contact/schema
local-storage/schema
handler-data/schema])
(defn migration [old-realm new-realm]
(log/debug "migrating v13 account database: " old-realm new-realm))

View File

@ -0,0 +1,8 @@
(ns status-im.data-store.realm.schemas.account.v13.handler-data)
(def schema {:name :handler-data
:primaryKey :message-id
:properties {:message-id "string"
:data {:type "string"
:default "{}"}}})

View File

@ -152,6 +152,7 @@
:chat/public-group-topic
:chat/confirmation-code-sms-listener
:chat/messages
:chat/handler-data
:chat/loaded-chats
:chat/bot-subscriptions
:chat/new-request

View File

@ -219,7 +219,7 @@
:markup data}])
"send-message" (dispatch [:send-message-from-jail {:chat-id chat_id
:message data}])
"handler-data" (dispatch [:set-handler-data chat_id data])
(log/debug "Unknown jail signal " type))))
(register-handler-fx