soma.cell

Webhook receiver + event log. POST /hook/<source> appends an event;

GET /events returns recent events newest-first with a bounded capacity.

A Soma program from the web part of the verified corpus: 71 lines, cells Webhooks, WebhooksTest. It passes soma check with soma 3.2.4.

How it is built

The cell Webhooks declares 4 signals in its face: request(method: String, path: String, body: String) -> Map, receive(source: String, payload: String) -> Map, recent(n: Int) -> List and count_source(source: String) -> Int; keeps 1 memory slot: events (List<String>, persistent, capacity and evict). Handlers: receive (line 17), recent (line 31), count_source (line 42) and request (line 46). Private helpers: _all.

The file also holds 1 test cell (WebhooksTest) with 1 assertion, run by soma test.

Generated from soma describe on this file.

The source

soma.toml

[package]
name = "web-corpus"
version = "0.1.0"
entry = "todo_state.cell"

[verify]
cells = ["TodoService"]
deadlock_free = true
eventually = ["Done", "Cancelled"]

webhook_log.cell

// Webhook receiver + event log. POST /hook/<source> appends an event;
// GET /events returns recent events newest-first with a bounded capacity.

cell Webhooks {
    face {
        signal request(method: String, path: String, body: String) -> Map
        signal receive(source: String, payload: String) -> Map
        signal recent(n: Int) -> List
        signal count_source(source: String) -> Int
        promise all_persistent
    }

    memory {
        events: List<String> [persistent, capacity(1000), evict(fifo)]
    }

    on receive(source: String, payload: String) {
        let ev = map("source", source, "payload", payload, "seq", next_id())
        events.push(to_json(ev))
        return ev
    }

    on _all() {
        let out = list()
        for raw in events.all {
            out = push(out, from_json(raw))
        }
        return out
    }

    on recent(n: Int) {
        let all = _all() |> reverse()
        let out = list()
        let i = 0
        while i < n && i < len(all) {
            out = push(out, all[i])
            i = i + 1
        }
        return out
    }

    on count_source(source: String) {
        return _all() |> count(e => e.source == source)
    }

    on request(method: String, path: String, body: String) {
        let req = map("method", method, "path", path)
        match req {
            {method: "GET",  path: "/events"}      -> recent(20)
            {method: "POST", path: "/hook/" + src} -> receive(src, body)
            _                                      -> response(404, map("error", "no route"))
        }
    }
}

cell test WebhooksTest {
    rules {
        assert _fixture() == "3|2|stripe"
    }

    on _fixture() {
        receive("github", "a")
        receive("github", "b")
        receive("stripe", "c")
        let total = len(_all())
        let gh = count_source("github")
        let newest = recent(1)[0].source
        return concat(to_string(total), concat("|", concat(to_string(gh), concat("|", newest))))
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check webhook_log.cell
warning: slot 'events' of 'Webhooks' is marked [evict]: entries are NOT evicted at capacity — the capacity is a budget assumption, not a limit; delete old entries yourself
  --> webhook_log.cell:14:59
   |
14 |         events: List<String> [persistent, capacity(1000), evict(fifo)]
   |                                                           ^
✓ 1 warning(s), no errors.
$ soma test webhook_log.cell
test WebhooksTest ...
  ✓ assert _fixture() == "3|2|stripe"

1 tests: 1 passed, 0 failed
$ soma verify webhook_log.cell
Verifying webhook_log.cell...
note: soma.toml [verify] cells names 'TodoService', which this file does not define — its properties are not checked here

Webhooks/termination:

  ⚠ handler `recent`: while-loop without provable termination bound (write `while [loop_bound(N)] cond { … }`, or a for-loop over a bounded range)
  ⚠ `request` calls `Webhooks.recent`, which may not terminate

0 passed, 2 warnings, 0 failures
soma.toml: 1 user-defined properties loaded — NONE applies to this file ([verify] cells = ["TodoService"] names no cell of it)
Temporal: 0 passed, 0 failed
VERIFY OK

Builtins used

concat, count, from_json, len, list, map, next_id, push, response, reverse, to_json, to_string

Source file: /corpus/web/webhook_log.cell · examples/corpus/web/webhook_log.cell