← All Modules

assay.huly

Huly client for a self-hosted or cloud workspace: read any document class, write through transactions, search fulltext, and drive the tracker (projects, issues, milestones, components).

Huly has no per-resource REST endpoints. Everything is one of two calls — a class-parameterised query, or a transaction document — so the module is shaped around those rather than around nouns.

read   GET  /find-all/{workspace}?class=&query=&options=  -> {dataType, total, value:[...]}
write  POST /tx/{workspace}   {_class: core:class:Tx*Doc, objectClass, objectSpace, ...}

Client

local huly = require("assay.huly")
local c = huly.client({
  token = env.get("HULY_TOKEN"),
  workspace = "b07d7630-3393-44e8-803d-4366baaeb80b",
  base_url = "https://huly.example.com",
})

huly.client(opts) accepts:

Requests ask for Accept-Encoding: identity. Huly's own client asks for snappy, gzip; assay can decode neither, and a compressed response arrives as bytes the JSON parser rejects.

Reading

query is a Mongo-flavoured document filter ({ space = "p1", priority = { ["$in"] = {1, 2} } }). options carries limit, sort, projection, total, lookup.

Two server behaviours are papered over, matching what Huly's own client does:

total is -1 unless the request asks for it, which is why count exists.

Writing

Tracker helpers

Issue numbering lives on the project, not the issue. create_issue atomically increments the project's sequence and derives both number and the PREFIX-N identifier from the result; an issue written without them is invisible in the UI. It also fills the fields the transactor requires but the caller rarely cares about — rank, kind, and the attachedTo / attachedToClass / collection triple that marks a top-level issue.

Class ids are exposed as constants (huly.ISSUE_CLASS, huly.PROJECT_CLASS, huly.STATUS_CLASS, huly.MILESTONE_CLASS, huly.COMPONENT_CLASS, huly.NO_PARENT, huly.TX_SPACE).

Example — file a triage issue and move it

local huly = require("assay.huly")
local c = huly.client({ workspace = env.get("HULY_WORKSPACE") })

local project = huly.resolve_project(c, "TSK")
local issue = huly.ensure_issue(c, project, {
  title = "Nightly backup verification failed",
  description = "rustic check reported a damaged pack",
  priority = 1,
})

huly.set_issue_status(c, issue, "tracker:status:InProgress")
log.info(issue.identifier .. " is now in progress")

Example — read anything by class

local huly = require("assay.huly")
local c = huly.client({})

-- Discover what a deployment actually has, then query it.
for _, tx in ipairs(c:model()) do
  if tx.objectClass == "core:class:Class" then log.info(tx.objectId) end
end

local overdue = c:find_all("tracker:class:Issue", {
  space = "tracker:project:DefaultProject",
  dueDate = { ["$lt"] = os.time() * 1000 },
}, { sort = { dueDate = 1 }, limit = 20 })
log.info(#overdue .. " overdue issues")