Git Product home page Git Product logo

goflow's Introduction

๐ŸŽข Goflow

Build Status codecov Go Report Card

Specification

See here for the complete specification docs.

Basic Usage

import (
    "github.com/nyaruka/goflow/assets/static"
    "github.com/nyaruka/goflow/flows"
    "github.com/nyaruka/goflow/flows/engine"
    "github.com/nyaruka/goflow/utils"
)

env := envs.NewBuilder().Build()
source, _ := static.LoadSource("myassets.json")
assets, _ := engine.NewSessionAssets(env, source, nil)
contact := flows.NewContact(assets, ...)
trigger := triggers.NewBuilder(env, contact, flow.Reference()).Manual().Build()
eng := engine.NewBuilder().Build()
session, sprint, err := eng.NewSession(assets, trigger)

Sessions

Sessions can be persisted between waits by calling json.Marshal on the Session instance to marshal it as JSON. You can inspect this JSON at https://sessions.temba.io/.

Utilities

Flow Runner

Provides a command line interface for stepping through a given flow.

% go install github.com/nyaruka/goflow/cmd/flowrunner
% $GOPATH/bin/flowrunner test/testdata/runner/two_questions.json 615b8a0f-588c-4d20-a05f-363b0b4ce6f4
Starting flow 'U-Report Registration Flow'....
---------------------------------------
๐Ÿ’ฌ "Hi Ben Haggerty! What is your favorite color? (red/blue) Your number is (206) 555-1212"
โณ waiting for message....

By default it will use a manual trigger to create a session, but the -msg flag can be used to start the session with a message trigger:

% $GOPATH/bin/flowrunner -msg "hi there" cmd/flowrunner/testdata/two_questions.json 615b8a0f-588c-4d20-a05f-363b0b4ce6f4

If the -repro flag is set, it will dump the triggers and resumes it used which can be used to reproduce the session in a test:

% $GOPATH/bin/flowrunner -repro cmd/flowrunner/testdata/two_questions.json 615b8a0f-588c-4d20-a05f-363b0b4ce6f4

Flow Migrator

Takes a legacy flow definition as piped input and outputs the migrated definition:

% go install github.com/nyaruka/goflow/cmd/flowmigrate
% cat legacy_flow.json | $GOPATH/bin/flowmigrate
% cat legacy_export.json | jq '.flows[0]' | $GOPATH/bin/flowmigrate

Expression Tester

Provides a quick way to test evaluation of expressions which can be used in flows:

% go install github.com/nyaruka/goflow/cmd/exptester
% $GOPATH/bin/exptester '@(10 / 5 >= 2)'
% $GOPATH/bin/exptester '@(TITLE("foo"))'

Development

You can run all the tests with:

% go test github.com/nyaruka/goflow/...

If you've made changes to the flow engine output, regenerate the test files with:

% go test github.com/nyaruka/goflow/test -update

If you need to rebuild the ANTLR grammar files:

go generate ./...

goflow's People

Contributors

dependabot[bot] avatar dodobas avatar ericnewcomer avatar nicpottier avatar norkans7 avatar rowanseymour avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

goflow's Issues

Friendly formatting of URNs/channel addresses

In the old world we populate the context with @channel.tel and @channel.tel_e164. In new world users have access to contact URN paths and so far @run.input.channel.address / @contact.channel.address which is E164 formatted.

So how should we let users get a friendlier version of telephone numbers? Could be an attribute like:

  • @contact.urns.1.path.display
  • @run.input.channel.address.display

or a function like:

  • @(display(contact.urns.1.path))
  • @(display(run.input.channel.address)

Figure out strategy for group msgs

Likely solution is something where we return a context of the flow at that place and it is up to container to deal with substituting contact vars

Support for non-contact preferred language

Need a way to request a language preference.

In RP, we look at Contact language, then Org primary language, then Flow default language. In the new world we are only looking at contact. While this seems like a reasonable simplification, we need to be able to bring current behavior forward.

A RP test when run through goflow that exposes this is: temba.flows.FlowsTest.test_cross_language_import

https://github.com/nyaruka/rapidpro/blob/1474af3e12b6e6a6b268b942541934bcede8f31e/temba/flows/tests.py#L5571

Leading with node with a wait

Repro:

  • Create node waiting for inbound message to start a flow
  • Start that flow with an inbound message

See: temba.flows.tests.TriggerStartTest.test_trigger_capture

The first node's wait should be satisfied by that inbound message

Fuzzy matching

Opening this to track and discuss our feelings on fuzzy matching in the new engine. RP tests like contains_any use levenshtein for edit distance. Unless we use the same going forward we will see inconsistent results. Don't believe we added have any sort of fuzzy matching so far.

Migration needs to repurpose first rule uuid for exit

In order to match path activity with legacy data, when we migrate a flow forward the exit uuid needs to be an existing rule_uuid. The old system tracks activity data by rule match, then adds up all like-named categories. Since we have discreet exits we need to use the first like-named category's uuid as the exit uuid during migration.

Migration: location based rule tests

HasWard
HasDistrict
HasState

We probably don't want to send all adminboundary information with every request, so need a solution that either caches this, calls back to the caller, or accesses an external service.

Error codes for events?

We may want to give callers more granular control on how to reliably handle error events. For example, it may be useful to take a different action if the flow blew up due to a runtime cycle.

Calling @webhook.json before webhook causes error

Flow definition: webhook-failure.json.zip

ERRO[1396] request complete http_method=POST http_proto="HTTP/1.1" http_scheme=http panic="runtime error: invalid memory address or nil pointer dereference" remote_addr="[::1]:52361" req_id="esquandolas.local/IAYbjAE4Qo-000007" resp_bytes_length=22 resp_elasped_ms=0.592514 resp_status=500

Migration: legacy add_label/add_group/del_group actions which take expressions

Currently these actions in RapidPro take one of 3 things as a label or group:

  • A dict with uuid and name fields
  • A string representing the label/group name
  • An expression (only evaluated if it begins with a @ char)

My first inclination is that we should drop support for expressions here because we shouldn't give users enough rope to create millions of groups/labels by accident.

Handling of flows that change

RP has a unit test that:

  • Starts the flow, stopping at a wait
  • Modifies that wait into a passive split
  • Sends a new message to resume through that ruleset having the message picked up by the next wait

Seen here: temba.flows.FlowsTest.test_rule_changes_under_us

Running this sort of test in the new world results in cannot resume flow at node [uuid] which no longer contains wait.

Need to determine how want to mitigate this behavior going forward.

Migration: send actions with variables

Currently in RapidPro each variable is resolved as follows:

  1. If it equals @new_contact, create a new empty contact and send to that
  2. Evaluate it as an expression
  3. Try to match it as a contact UUID
  4. Try to match it as a group name
  5. Try to match it as a phone number

We could emulate that in goflow as:

  1. If it equals @new_contact, send that back to rapidpro in the contacts list so it knows to create a contact
  2. Evaluate it as an expression
  3. If it looks like a UUID send that back in the contacts list
  4. If it matches the name of a group asset, send that group back in the groups list
  5. If it is a valid phone number send that back in the URNs list

But is now the time to clean this up and make it more explicit?

Migration: regex tests which write matches to @extra

In old world, RegexTest has access to the run and adds the matches to @extra on the run. In the new world, tests are expressions which know nothing of runs.

Either:

  • we introduce a mechanism for tests to return a dict that can be automatically added to @extra by the router
  • we introduce an action for users to explicitly add things to extra, and allow them to add the result of an expression which returns the matching groups. We could then migrate rulesets with regex tests to nodes that include this action before the switch router
  • we introduce an expression to the old world which allows users to regex parse input and see if we can change existing flows to using this in expression type rulesets instead of relying on @extra

I think I'm leaning toward the latter option because implicitly changing @extra isn't something I particularly want to support in the new world.

has_pattern should be usable in expressions to get specific matching groups

has_pattern("buy btc", "Buy (\w+)").match = ["buy btc", "btc"]
has_pattern("buy btc", "Buy (\w+)").match.0 == "buy btc"
has_pattern("buy btc", "Buy (\w+)").match.1 == "btc"

the flow result saved from a router case with this test would still be match.0. This doesn't provide an automatic solution to #69 but does provide equivalent functionality

Maybe we should return before webhooks?

Webhooks can take a bit.. and sometimes it is nice to send status to the user that it may take a while, but since we return everything at once (the commands to send the status and the webhook result) that doesn't work.

Hrmm

Asset caching and lazy loading

We need a solution that:

  • Avoids the caller needing to sending flow (etc) definitions with every request
  • Supports location data being an asset type and thus solves #71
  • Allows the caller to provide an up-to-date definition when necessary, e.g. the simulator needs to run with the absolute latest version of a flow

One option would be something similar to <script> and <style> elements in HTML where they can be embedded resources, or linked resources.

"assets": [
  {"type": "channel", "uuid": "7a3b...", "src": "http://rpd.io/as/flows/7a3b..."},
  {"type": "flow", "uuid": "4af6...", "src": "http://rpd.io/as/flows/4af6..."},
  {"type": "flow", "def": {
    "uuid": "a9c6...",
    "nodes": [...]
  }},
]

So in the simulator case, it would send flows as embedded definitions, but probably all real world use we would send just the links. Goflow would then be responsible for caching those definitions for X amount of time.

If we wanted to support longer caching we could give callers the ability to invalidate or update goflow's asset cache when they know something has changed, e.g. an /assets endpoint

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.