Skip to content
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

[Fix #15] Move the :initial-ctx to the config and rename runtime to FondaContext #16

Merged
merged 1 commit into from
Nov 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ It is possible to redefine what an anomaly is by passing a predicate, `anomaly?`
(ns my-namespace.core
(:require [fonda.core :as fonda]))

(fonda/execute config steps initial-ctx on-success on-anomaly on-exception)
(fonda/execute config steps ctx on-success on-anomaly on-exception)
```

The parameter order makes it easy to partially apply `execute` for leaner call sites.
Expand All @@ -38,9 +38,10 @@ The parameter order makes it easy to partially apply `execute` for leaner call s
| Key | Optional? | Notes |
|---|---|---|
| `:anomaly?` | Yes | A function that gets a map and determines if it is an anomaly |
| `:log-exception` | Yes | A function that gets called with the [runtime context](#runtimeContext) when there is an exception |
| `:log-anomaly` | Yes | A function that gets called with the [runtime context](#runtimeContext) when a step returns an anomaly |
| `:log-exception` | Yes | A function that gets called with the [fonda context](#fonda-context) when there is an exception |
| `:log-anomaly` | Yes | A function that gets called with the [fonda context](#fonda-context) when a step returns an anomaly |
| `:log-success` | Yes | A function that gets called after all the steps succeed |
| `:initial-ctx` | Yes | The data that initializes the context. Must be a map |

- **steps**: Each item on the steps collection must be either a Tap or a Processor

Expand All @@ -60,19 +61,18 @@ The parameter order makes it easy to partially apply `execute` for leaner call s
| `:name` | No | The name of the step |


- **initial-ctx** The context data that gets passed to the steps functions. Must be a map.

- **on-success** Callback that gets called with the context if all steps succeeded.
- **ctx** The runtime context, merged to the initial context. Must be a map.
- **on-success** Callback that gets called with the context if all steps succeeded.
- **on-anomaly** Callback that gets called with an anomaly when any step returns one.
- **on-exception** Callback that gets called with an exception when any step triggers one.

### Error and Anomaly Handling

- If any step returns an anomaly, or triggers an exception, the execution of the steps stops and the global taps will be called.

- If any step returns an anomaly, the log-anomaly will be called with the RuntimeContext, and then the on-anomaly callback
- If any step returns an anomaly, the log-anomaly will be called with the [`FondaContext`](#fonda-context) and then the on-anomaly callback

- If any step triggers an exception, the log-exception will be called with the RuntimeContext, and then on-exception callback.
- If any step triggers an exception, the log-exception will be called with the [`FondaContext`](#fonda-context) and then on-exception callback.

#### Processor steps

Expand All @@ -91,9 +91,11 @@ Taps are maps with the following keys:
It will still block the steps processing if it is asynchronous, and it will interrupt the steps execution if it returns an anomaly, or it triggers an exception
- **:name** A descriptive name for the step

### <a name="runtimeContext"></a>Fonda Runtime Context
### <a name="fonda-context></a>Fonda Context

Log functions are called with the internal context. This is different from the context passed to the steps and it is only exposed to the these functions for logging purposes.

Log functions are called with the runtime internal context. This is different from the context passed to the steps and it is only exposed to the these functions for logging purposes. The runtime context is a map that contains:
The `FondaContext` is a record that contains:

- **:ctx** The context that was threaded through the steps.
- **:step-log** A collection of step logs. By default only step names are logged.
Expand All @@ -104,7 +106,9 @@ Log functions are called with the runtime internal context. This is different fr

```clojure
(fonda/execute
{:log-exception (fn [{:keys [ctx exception step-log]}]
{:initial-ctx {:env-var-xyz "value"}

:log-exception (fn [{:keys [ctx exception step-log]}]
(println "Oh noes! An exception happened:" exception))

:log-anomaly (fn [{:keys [ctx anomaly step-log]}]
Expand Down
63 changes: 31 additions & 32 deletions src/fonda/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
(s/keys :opt-un [::r/anomaly?
::r/log-exception
::r/log-anomaly
::r/log-step-fn]))

;;(s/def ::config-spell (ss/keys :opt-un config-keys))
::r/log-step-fn
::r/initial-ctx]))

(s/def ::steps (s/coll-of ::st/step))

Expand All @@ -22,58 +21,58 @@
(s/fdef execute
:args (s/cat :config ::config
:steps ::steps
:initial-ctx ::r/ctx
:context ::r/ctx
:on-success ::r/on-success
:on-anomaly ::r/on-anomaly
:on-exception ::r/on-exception))
(defn execute
"Sequentially executes the series of given `steps`
Each step tap of processor function can be synchronous or asynchronous.
.
"Sequentially executes the series of given `steps`.

Each step function - tap or processor - can be synchronous or asynchronous.

- `config`: A map with:
- [opt] anomaly? A function that gets a map and determines if it is an anomaly
- [opt] log-exception A function gets called with the runtime-context when there is an exception
- [opt] log-anomaly A function that gets called with the runtime-context when a step returns an anomaly
- [opt] log-success A function that gets called with the runtime-context after all steps succeeded
- [opt] anomaly? A function that gets a map and determines if it is an anomaly.
- [opt] log-exception A function gets called with the FondaContext record when there is an exception.
- [opt] log-anomaly A function that gets called with the FondaContext record when a step returns an anomaly.
- [opt] log-success A function that gets called with the FondaContext record after all steps succeeded.
- [opt] initial-ctx The context data initializes the context. Must be a map.

- `steps`: Each item on the `steps` collection must be either a Tap, or a Processor
- `steps`: Each item on the `steps` collection must be either a Tap, or a Processor.

Tap:
- tap: A function that gets the context but doesn't augment it
- tap: A function that gets the context but doesn't augment it.
- name: The name of the step

Processor:
- processor: A function that gets the context and assocs the result into it on the given path
- path: Path where to assoc the result of the processor
- name: The name of the step

- `initial-ctx` The context data that gets passed to the steps functions. Must be a map

- `on-sucess' Callback that gets called with the context if all the steps succeeded
- `on-anomaly` Callback that gets called with an anomaly when any step returns one
- `on-exception` Callback that gets called with an exception when any step triggers one
- `ctx` The runtime context, merged to the initial context. Must be a map.
- `on-success` Callback that gets called with the context if all the steps succeeded.
- `on-anomaly` Callback that gets called with an anomaly when any step returns one.
- `on-exception` Callback that gets called with an exception when any step triggers one.
"
([config steps initial-ctx on-sucess on-anomaly on-exception]
([config steps ctx on-sucess on-anomaly on-exception]

(let [{:keys [anomaly?
log-exception
log-anomaly
log-success
log-step-fn]} config]

(-> (r/map->RuntimeContext
{:anomaly? (or anomaly? fonda.anomaly/anomaly?)
:log-exception (or log-exception l/default-log-exception)
:log-anomaly (or log-anomaly l/default-log-anomaly)
:log-success log-success
:ctx (or initial-ctx nil)
:on-success on-sucess
:on-anomaly on-anomaly
:on-exception on-exception
:queue (st/steps->queue steps)
:step-log []
:log-step-fn (or log-step-fn e/default-log-step-fn)})
(-> (r/map->FondaContext
{:anomaly? (or anomaly? fonda.anomaly/anomaly?)
:log-exception (or log-exception l/default-log-exception)
:log-anomaly (or log-anomaly l/default-log-anomaly)
:log-success log-success
:ctx (merge (:initial-ctx config) ctx)
:on-success on-sucess
:on-anomaly on-anomaly
:on-exception on-exception
:queue (st/steps->queue steps)
:step-log []
:log-step-fn (or log-step-fn e/default-log-step-fn)})
(e/execute-steps)
(e/execute-loggers)
(e/deliver-result)))))
(e/deliver-result)))))
84 changes: 43 additions & 41 deletions src/fonda/execute.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,84 @@
[cljs.spec.alpha :as s]))

(s/fdef assoc-tap-result
:args (s/cat :runtime-ctx ::r/runtime-context
:args (s/cat :fonda-ctx ::r/fonda-context
:res any?))

(defn assoc-tap-result [{:as runtime-ctx :keys [anomaly?]} res]
(defn assoc-tap-result [{:as fonda-ctx :keys [anomaly?]} res]
(if (anomaly? res)
(assoc runtime-ctx :anomaly res)
runtime-ctx))
(assoc fonda-ctx :anomaly res)
fonda-ctx))



(s/fdef assoc-processor-result
:args (s/cat :runtime-ctx ::r/runtime-context
:args (s/cat :fonda-ctx ::r/fonda-context
:path ::st/path
:res any?))

(defn assoc-processor-result
[{:as runtime-ctx :keys [anomaly?]} path res]
[{:as fonda-ctx :keys [anomaly?]} path res]
(if (anomaly? res)
(assoc runtime-ctx :anomaly res)
(assoc-in runtime-ctx (concat [:ctx] path) res)))
(assoc fonda-ctx :anomaly res)
(assoc-in fonda-ctx (concat [:ctx] path) res)))


(s/fdef try-step
:args (s/cat :runtime-ctx ::r/runtime-context
:args (s/cat :fonda-ctx ::r/fonda-context
:step ::st/step))

(defn- try-step
"Tries running the given step (a tap step, or a processor step).
If an exception gets triggerd, an exception is added on the context.
If an anomaly is returned, an anomaly is added to the context"
[{:as runtime-ctx :keys [ctx log-step-fn]}
[{:as fonda-ctx :keys [ctx log-step-fn]}
{:as step :keys [path name processor tap]}]
(try
(let [res (if processor (processor ctx) (tap ctx))
assoc-result-fn (cond
(not (nil? tap)) (partial assoc-tap-result runtime-ctx)
(not (nil? processor)) (partial assoc-processor-result runtime-ctx path))
(not (nil? tap)) (partial assoc-tap-result fonda-ctx)
(not (nil? processor)) (partial assoc-processor-result fonda-ctx path))
assoc-result #(-> (assoc-result-fn %) (update :step-log log-step-fn step res))]

(if (a/async? res)
(a/continue res assoc-result #(assoc runtime-ctx :exception %))
(a/continue res assoc-result #(assoc fonda-ctx :exception %))
(assoc-result res)))

(catch :default e
(assoc runtime-ctx :exception e))))
(assoc fonda-ctx :exception e))))



(s/fdef try-global-tap
:args (s/cat :tap-fn ::st/tap
:runtime-ctx ::r/runtime-context))
:fonda-ctx ::r/fonda-context))

(defn- try-logger
"Runs a global tap function.
If the tap function triggers any exception, adds an exception to the runtime-context"
[tap-fn runtime-ctx]
If the tap function triggers any exception, adds an exception to the FondaContext record"
[tap-fn fonda-ctx]
(try
(let [res (tap-fn runtime-ctx)]
(let [res (tap-fn fonda-ctx)]
(if (a/async? res)
(a/continue res (fn [_] runtime-ctx) #(assoc runtime-ctx :exception %))
runtime-ctx))
(a/continue res (fn [_] fonda-ctx) #(assoc fonda-ctx :exception %))
fonda-ctx))

(catch :default e
(assoc runtime-ctx :exception e))))
(assoc fonda-ctx :exception e))))



(s/fdef deliver-result
:args (s/cat :runtime-ctx ::r/runtime-context))
:args (s/cat :fonda-ctx ::r/fonda-context))

(defn- deliver-result
"Calls a callback depending on what is on the context.
If there is an exception on the context, calls on-exception.
If there is an anomaly on the context, calls on-anomaly.
Otherwise calls on-success."
[{:as runtime-ctx :keys [exception anomaly on-exception on-anomaly on-success ctx]}]
(if (a/async? runtime-ctx)
(a/continue runtime-ctx deliver-result on-exception)
[{:as fonda-ctx :keys [exception anomaly on-exception on-anomaly on-success ctx]}]
(if (a/async? fonda-ctx)
(a/continue fonda-ctx deliver-result on-exception)
(let [cb (cond exception #(on-exception exception)
anomaly #(on-anomaly anomaly)
:else #(on-success ctx))]
Expand All @@ -106,41 +106,43 @@
;; PUBLIC ;;
;;;;;;;;;;;;
(s/fdef execute-taps
:args (s/cat :runtime-ctx ::r/runtime-context))
:args (s/cat :fonda-ctx ::r/fonda-context))

(defn execute-loggers
"Executes one of the global tap functions.

If the context has an anomaly, calls log-anomaly.
If the context has an exception, calls log-exception"
[{:as runtime-ctx :keys [exception anomaly log-exception log-anomaly log-success]}]
(if (a/async? runtime-ctx)
(a/continue runtime-ctx execute-loggers log-exception)
[{:as fonda-ctx :keys [exception anomaly log-exception log-anomaly log-success]}]
(if (a/async? fonda-ctx)
(a/continue fonda-ctx execute-loggers log-exception)

(if-let [log-fn (cond
(and exception log-exception) log-exception
(and anomaly log-anomaly) log-anomaly
(and (not anomaly) (not exception) log-success) log-success
)]
(try-logger log-fn runtime-ctx)
runtime-ctx)))
(try-logger log-fn fonda-ctx)
fonda-ctx)))



(s/fdef execute-steps
:args (s/cat :runtime-ctx ::r/runtime-context))
:args (s/cat :fonda-ctx ::r/fonda-context))

(defn execute-steps
"Sequentially runs each of the steps.
It blocks the execution on the asynchronous steps.
If any step assocs an exception to the runtime-context, the execution stops."
[{:as runtime-ctx :keys [queue exception anomaly]}]
(if (a/async? runtime-ctx)
(a/continue runtime-ctx execute-steps)

It blocks the execution on the asynchronous steps. If any step assocs
an exception to the FondaContext record, the execution stops."
[{:as fonda-ctx :keys [queue exception anomaly]}]
(if (a/async? fonda-ctx)
(a/continue fonda-ctx execute-steps)
(let [step (peek queue)]
;;(println "step:" step)
(if (or (not step) exception anomaly)
runtime-ctx
fonda-ctx
(recur
(-> runtime-ctx
(assoc :queue (pop queue))
(try-step step)))))))
(-> fonda-ctx
(assoc :queue (pop queue))
(try-step step)))))))
5 changes: 1 addition & 4 deletions src/fonda/log.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
(binding [*print-fn* *print-err-fn*]
(apply println args)))

(s/fdef default-log-exception
:args (s/cat :runtime-ctx ::r/runtime-context-sync))

(defn default-log-exception [{:keys [ctx step-log exception]}]
(println-err "Fonda found an exception:" exception)
(println-err "ctx:" ctx)
Expand All @@ -18,4 +15,4 @@
(defn default-log-anomaly [{:keys [ctx step-log anomaly]}]
(println-err "Fonda found an anomaly:" anomaly)
(println-err "ctx:" ctx)
(println-err "step-log:" step-log))
(println-err "step-log:" step-log))
Loading