Botmation Documentation
Piping
Piping
There's a special kind of Assembly Line for passing data from one BotAction to the next called Pipe. When BotAction's are assembled in a Pipe, the values they return, are "piped" into subsequent BotAction's. Pipes wrap the values resolved from each assembled BotAction, in a branded Pipe object (for type gaurding), then injects the Pipe object at the end of the spreaded injects
array.
This is useful for de-composing complex functionality that depends on data collected from a page. For example, if a website stores the User auth token in Local Storage, a single a BotAction can get the value, then the next BotAction can operate on it, all through a Pipe.
Pipe BotAction
Let's get started with the pipe()() BotAction to assemble BotAction's for writing and reading to Local Storage:
await pipe()( setLocalStorageItem('userID', '12345'), getLocalStorageItem('userID'), log('User ID is in the Pipe') // has '12345' as the Pipe valued logged to console)(page)
setLocalStorageItem() sets a key/value in Local Storage. It does not return a value. BotAction's assembled in Pipe's don't have to return a value. However, getLocalStorageItem() reads the value by the key
provided, then returns it. log()
then logs the message and Pipe object value.
"Console" BotAction's support logging Pipe values, when assembled in a Pipe and return the Pipe value like passing it along. Read more in the API docs for log().
Pipe object
pipe()()()
runs the assembled actions, one at a time, and checks the values returned from each. When a function doesn't return a value, that is considered, emptying the Pipe. Empty pipes are still injected and have an undefined
value:
const anEmptyPipeObject = { brand: 'Pipe', value: undefined}
When running a pipe()()
in a context without Piping, pipe()()()
will inject an empty Pipe object into the first assembled BotAction, unless you override that by passing in a value to pipe, in the first pipe()
call.
BotAction's specific to Piping
There are a collection of BotAction's specific to Piping: map(), pipeValue(), and emptyPipe. map() accepts a pure function to operate on the Pipe value, so if you want to cast the Pipe value from one type to another, use the map() BotAction. Let's see an example, isGuest
from Instagram auth, uses map() to convert the Pipe value to the correct boolean value:
const isGuest: ConditionalBotAction = indexedDBStore('redux', 'paths')( getIndexedDBValue('users.viewerId'), map(viewerId => viewerId ? false : true), )
IndexedDBStore()()
runs assembled BotAction's in a Pipe.
Here we are getting a value from the redux
IndexedDB database, in a store called paths
, by the key users.viewerId
. The returned value (Pipe value) is mapped to the correct value for isGuest
. Basically, if the viewerId
is defined, then the Bot is considered logged in, and isGuest
is false
otherwise true
.
Switch Pipe
For a different kind of Piping assembler, check out Switch Pipe. It works similar to pipe()()
except it injects the same Pipe object, from the its injects or the higher-order call, into each assembled BotAction.
To learn more about switchPipe()()
read its documentation.
Helpers for Piping
Also, there are separate functions in this library, that are found in the module, that are not BotAction's, but regular functions, to help reduce boilerplate called Helpers. For example, there are many Helper functions for piping.
To name a few, there are unpipeInjects(), removePipe(), getInjectsPipeValue(), wrapValueInPipe(), pipeInjects(), and so forth. When starting out with piping, the first three are the most relevant as they help write pipeable BotAction's that can be still be used safely in a Chain.
If you're not interested in whats injected, but need the Pipe value, use getInjectsPipeValue() as it has safe fallbacks for when there is no Pipe, in case the BotAction is assembled in a Chain. See the Local Storage BotAction's for examples, in how it's used.
If you're interested in the injects and the Pipe value, use unpipeInjects(), which allows you to specify the number of injects you're expecting, to safely retrieve them with the Pipe value, even if there was no Pipe, or some injects didn't get injected. See the IndexedDB BotAction's for examples.
If you just want the injects without any Pipe, use removePipe().
Edit this page on GitHub