# Calling CoCoCo

Normal and Integration scripts reach CoCoCo through the global `ctx`: run a GraphQL
query, look up a device, read a config value, send a notification. *The ctx
reference* lists every function.

## Two results: `ok` and `result`

A function that has to wait for something, such as a database, a device or another
service, returns two values. When `ok` is true, `result` is the value; when it is
false, `result` is the error message. The reference shows these functions returning
`ok, result`.

This is the most common mistake in a script: binding one value binds `ok`, not the
data. Always write `local ok, result = …` and check `ok` before using `result`.
Passing such a call straight into another function passes both values, so bind them
first.

Functions that don't wait, such as `ctx.log.info`, return their value directly.

```lua
-- Service-reaching calls are async, so they return TWO values: an ok flag and
-- the result. Binding one value binds the FLAG, not the data.
local ok, result = ctx.graphql.query([[
  query ListCustomers($first: Int!) {
    listCustomers(first: $first) {
      edges { node { id name email isActive } }
      totalCount
    }
  }
]], { first = 5 })

if not ok then error(result) end

-- `ok` covers transport and authorization. GraphQL-level errors arrive inside
-- the payload, so they need their own check.
if result.errors then error(json.encode(result.errors)) end

-- `data` is optional and has to be bound before it is indexed. It is absent
-- when the request was rejected outright rather than partially failing — an
-- errors block whose path names a field can otherwise read like a problem with
-- that field, when nothing ran at all.
local data = result.data
if data == nil then error("GraphQL returned no data") end

local customers = {}
for _, edge in ipairs(data.listCustomers.edges) do
  table.insert(customers, { name = edge.node.name, email = edge.node.email })
end

return { total = data.listCustomers.totalCount, customers = customers }
```

## GraphQL results are checked

When a query is written directly in the script, `ctx.graphql.query` is checked
against CoCoCo's GraphQL schema before the script runs: the fields you select, the
variables you pass and the shape of the result. The editor completes fields as you
type. A GraphQL `null` arrives as `nil`, so check a field before you use it.
Checking doesn't run the query or change who may run it: the query runs with the
permissions of whoever, or whatever, runs the script.

## Objects

Some functions return an object rather than data: `ctx.device.get` returns a device,
`ctx.template.get` a template, `ctx.ml.get` a model. Getting the object doesn't look
anything up yet, so a name that doesn't exist fails when you first call one of the
object's methods, and it raises an error there rather than returning a false `ok`.
Methods are called with a colon (`printer:http(…)`), and those that wait return
`ok, result` like any other call.

```lua
-- ctx.device.get is a CONSTRUCTOR, not a service call: it is synchronous and
-- returns ONE value — the handle. There is no ok flag to bind, and binding two
-- names here would put the handle in the first and nil in the second.
--
-- Resolution is LAZY: building the handle does not look the device up, so an
-- unknown name fails later, when a method runs — and it raises there rather
-- than returning a false ok. Wrap the whole use if the name is not trusted.
local available, printer = pcall(function() return ctx.device.get("my-printer") end)
if not available then return { available = false } end

-- The handle's METHODS are the async half, and they take the handle as self —
-- so they are called with a colon. These do return (ok, result).
local ok, resp = printer:http({ method = "GET", path = "/api/status" })
if not ok then return { available = true, ok = false, error = resp } end

-- `id()` confirms which device the name actually bound to — async like the
-- rest, so it too returns two values.
local idOk, deviceId = printer:id()

-- The same handle also speaks SQL where the device exposes a database, so one
-- resolution serves both protocols — that is the point of holding a handle
-- rather than passing the name to every call.
local sqlOk, rows = printer:sql({ query = "SELECT 1", bindings = {} })

return {
  available = true,
  ok = true,
  status = resp.status,
  deviceId = idOk and deviceId or nil,
  sqlReachable = sqlOk,
}
```

## Records a script keeps: `ctx.store`

`ctx.store` keeps named records that belong to whatever is running: a custom app's
own records, or an integration's. Other scripts have no store. A script can't reach
another app's records, whatever name it asks for. `ctx.dataContainer` is the older
name for the same functions.

```lua
-- ctx.store belongs to whatever is running: here a custom app, so the records
-- are that app's. The owner is fixed when the run is built, so a script cannot
-- reach another app's records whatever name it asks for.
local name = "sync-state"

-- Every ctx.store call waits on the database, so each returns TWO values.
-- `put` answers with the stored RECORD (name, data, timestamps, …).
local ok, saved = ctx.store.put(name, { cursor = 42, label = "nightly" })
if not ok then error(saved) end

-- `get` returns that record, or nil when the name was never written. A missing
-- record is a SUCCESSFUL read: ok stays true and the value is nil, so test the
-- value — not the flag — to decide "missing".
local readOk, found = ctx.store.get(name)
if not readOk then error(found) end

local cursor = nil
if found ~= nil then cursor = found.data.cursor end

-- `delete` is safe to call without checking first: deleting a missing record
-- is not an error.
local deleteOk, problem = ctx.store.delete(name)
if not deleteOk then error(problem) end

-- Proving the round trip means reading back AFTER the delete too.
local afterOk, after = ctx.store.get(name)
if not afterOk then error(after) end

return {
  name = saved.name,
  cursor = cursor,
  goneAfterDelete = after == nil,
}
```

## In an integration

An integration uses the same `ctx` names, narrowed to the integration: *Integrations*
describes how.

## Names or ids

`ctx.device.get` accepts a device's id (`dev_…`) or its name (`"my-printer"`).
`ctx.notify.send` accepts a group id (`ngr_…`), a recipient id (`nrc_…`) or a group
name. Looking something up by name checks your permissions, just as an id does.