381 lines
18 KiB
ReStructuredText
381 lines
18 KiB
ReStructuredText
Arcadia Studio model REST API
|
||
=============================
|
||
|
||
This is the HTTP contract for ``oa-server``. The normative engineering model
|
||
and source language belong to ``language.rst``. ``openapi.json`` describes the
|
||
JSON records and closed command vocabulary. The server uses the same Rust
|
||
``Project``, ``Command``, parser/printer and validation implementation as the
|
||
desktop.
|
||
|
||
Identity, revision and writes
|
||
-----------------------------
|
||
|
||
All model routes begin with ``/api/v1``. A hosted project's immutable service
|
||
``resource_id`` is different from its editable ``model.id``. Each stored file
|
||
contains ``{resource_id, revision, model}``. The revision starts at 1, increments
|
||
once per changed accepted transaction, and does not change for a no-op.
|
||
|
||
Existing-project writes require the exact quoted ETag returned by a project
|
||
read, for example::
|
||
|
||
If-Match: "p-6f7162cc-094c-4f30-9b70-3f6f355f0c7f-r1"
|
||
|
||
Missing preconditions return 428; stale preconditions return 412. Weak ETags,
|
||
wildcards and lists are not accepted on writes. GET of the envelope, model and
|
||
source supports ``If-None-Match`` and returns an empty 304 when unchanged.
|
||
Use ETags as opaque strings; revisions are unsigned integer counters in JSON.
|
||
|
||
Every accepted changed write is persisted before the response. A worker captures
|
||
a revision-qualified snapshot, then parses and validates its candidate outside
|
||
the catalog lock. Commit rechecks the exact ETag and atomically installs the
|
||
envelope under the lock. Another accepted edit during preparation therefore
|
||
returns 412 even when the original snapshot passed its initial revision check.
|
||
Malformed or rejected inputs preserve the accepted model. Incomplete-model
|
||
warnings permit work and are returned in ``issues``.
|
||
|
||
There is no retained revision history, idempotency receipt, tombstone or
|
||
server undo. DELETE permanently removes the hosted envelope. Source/JSON
|
||
exports contain the current model only. They preserve the model's persistent
|
||
aliases, diagrams, artifact references and OA/SA trace data. Artifact revision
|
||
strings refer to external artifacts, not past server snapshots.
|
||
|
||
A lost connection or 504 can occur after an admitted write has begun. That
|
||
write may still complete. Read current state before choosing a new action.
|
||
Do not automatically retry POST create/import or regenerate random IDs and
|
||
assume the original request failed.
|
||
|
||
Routes
|
||
------
|
||
|
||
Health and schema routes are public. All model routes require the configured
|
||
bearer token, if any. Non-loopback binding requires a configured token.
|
||
|
||
.. list-table:: Endpoints
|
||
:header-rows: 1
|
||
:widths: 12 41 47
|
||
|
||
* - Method
|
||
- Path
|
||
- Behavior
|
||
* - GET
|
||
- ``/health``, ``/health/live``, ``/health/ready``
|
||
- Responding process with successfully opened store.
|
||
* - GET
|
||
- ``/openapi.json``, ``/api/v1/openapi.json``
|
||
- OpenAPI 3.1 contract.
|
||
* - POST
|
||
- ``/api/v1/projects``
|
||
- Create from ``{"name":"..."}``; 201 + Location/ETag.
|
||
* - GET
|
||
- ``/api/v1/projects``
|
||
- Summaries; ``limit``, ``after``, name substring ``q``.
|
||
* - GET
|
||
- ``/api/v1/projects/{id}``
|
||
- Complete current stored envelope.
|
||
* - PATCH
|
||
- ``/api/v1/projects/{id}``
|
||
- Conditional rename using ``{"name":"..."}``.
|
||
* - DELETE
|
||
- ``/api/v1/projects/{id}``
|
||
- Conditional permanent removal; 204.
|
||
* - POST
|
||
- ``/api/v1/projects/import``
|
||
- Raw model JSON or Arcadia source; new service ID.
|
||
* - GET / PUT
|
||
- ``/api/v1/projects/{id}/model``
|
||
- Export or conditionally replace native model JSON.
|
||
* - GET / PUT
|
||
- ``/api/v1/projects/{id}/source``
|
||
- Export or conditionally replace canonical UTF-8 source.
|
||
* - GET
|
||
- ``/api/v1/projects/{id}/elements``
|
||
- Filter by ``kind``, ``layer``, ``alias``, name ``q``.
|
||
* - GET
|
||
- ``/api/v1/projects/{id}/relations``
|
||
- Filter by ``kind``, ``source``, ``target``, ``incident_to``.
|
||
* - GET
|
||
- ``/api/v1/projects/{id}/artifact-links``
|
||
- Artifact links; optional owner ``object`` filter.
|
||
* - GET
|
||
- ``/api/v1/projects/{id}/traces``
|
||
- OA/SA traces; optional endpoint ``object`` filter.
|
||
* - POST
|
||
- ``/api/v1/projects/{id}/query``
|
||
- Bounded directed neighborhood, no mutation.
|
||
* - GET / POST
|
||
- ``/api/v1/projects/{id}/validation``
|
||
- Validate current model or a submitted candidate.
|
||
* - POST
|
||
- ``/api/v1/projects/{id}/transactions``
|
||
- Conditional typed command batch, optional dry run.
|
||
* - POST
|
||
- ``/api/v1/projects/{id}/transitions/preview``
|
||
- Preview a core transition against If-Match; return plan/base_revision.
|
||
* - POST
|
||
- ``/api/v1/projects/{id}/transitions/apply``
|
||
- Conditionally apply that client-held plan and base_revision.
|
||
|
||
``Content-Type: application/json`` is required for JSON requests.
|
||
``text/plain; charset=utf-8`` is used for Arcadia source. The service does not
|
||
guess formats from a filename. Unknown DTO fields, invalid UUIDs/enums, duplicate
|
||
model map keys and unsupported schemas are rejected. Maximum body size is
|
||
8 MiB. Compressed or encoded request bodies and browser Origin requests are
|
||
not supported.
|
||
|
||
Model admission permits at most 5,000 elements plus relations, 20,000 total
|
||
persistent records (including the project, views and placements), and 50,000
|
||
ordered step references. Source declaration counts and decoded JSON record
|
||
counts are checked before structural validation. Legacy import counts the
|
||
occurrences and placements migration will generate before validating or
|
||
migrating that model. Exceeding these budgets returns ``422 model_limit``;
|
||
candidate validation reports the same diagnostic in its 200 response.
|
||
Parsing, validation and query processing use snapshot workers, so this work
|
||
does not hold the catalog lock or serialize unrelated model preparation.
|
||
|
||
Import accepts schema-1 JSON through the shared explicit migration adapter,
|
||
as well as schema-2 native JSON. Source import uses the shared language parser
|
||
and its supported migrations. Export and native JSON replacement use schema 2.
|
||
Only documented query parameters are accepted. In particular, appending
|
||
``?revision=...`` to model/source export is an error, not historical retrieval.
|
||
|
||
Queries and pagination
|
||
----------------------
|
||
|
||
List filters combine using AND. Enum spelling is case-sensitive and comes
|
||
from the shared schema: examples include ``kind=Activity`` and ``layer=Oa``.
|
||
Aliases match exactly; ``q`` is a Unicode-lowercase substring match on the
|
||
display name. Empty or invalid enum values are request errors.
|
||
|
||
Lists are ordered by UUID. ``limit`` defaults to 100 and accepts 1 through
|
||
1000. ``after`` is an exclusive last UUID. ``next_after`` is null at the end.
|
||
Project-scoped lists return a revision. Include that revision on following
|
||
pages to detect a changed model; 409 ``snapshot_changed`` means restart the
|
||
query. Supplying a past revision does not retrieve history. The project catalog
|
||
is a live list and does not have a multi-page snapshot guarantee.
|
||
|
||
A neighborhood request is::
|
||
|
||
{
|
||
"roots": ["1b37b9d7-9a95-4a51-8707-010878a82419"],
|
||
"direction": "both",
|
||
"relation_kinds": ["Allocation", "Interaction"],
|
||
"max_depth": 2,
|
||
"max_objects": 200,
|
||
"revision": 3
|
||
}
|
||
|
||
Roots may identify elements or relations. The service traverses relation
|
||
source/target adjacency in the selected direction (incoming/outgoing/both),
|
||
preserves distinct parallel relations, and returns ``elements``, ``relations``,
|
||
``revision`` and ``truncated``. Empty kind filters permit all relation kinds.
|
||
Every returned relation includes its endpoint closure, including the endpoints
|
||
of a referenced relation. Closure records count against the object bound but
|
||
do not add traversal depth. Model ownership, process occurrence order and
|
||
non-endpoint references remain available in the returned records and complete
|
||
model; they are not silently treated as additional graph edges.
|
||
|
||
Use 1–100 roots, depth 0–4, and 1–1000 total returned objects. The cap must fit
|
||
the roots and their endpoint closure. A root closure that cannot fit is an
|
||
error; further omitted edges set ``truncated: true``. A missing root is 422.
|
||
Deterministic UUID iteration makes repeated queries of one snapshot stable.
|
||
|
||
Transactions
|
||
------------
|
||
|
||
The ``commands`` array contains the shared externally tagged Serde command
|
||
representation. It is not RFC 6902 JSON Patch. For example::
|
||
|
||
{
|
||
"commands": [
|
||
{"Rename": ["1b37b9d7-9a95-4a51-8707-010878a82419", "Assess assistance"]},
|
||
{"Describe": ["1b37b9d7-9a95-4a51-8707-010878a82419", "Review the request."]}
|
||
],
|
||
"dry_run": true
|
||
}
|
||
|
||
One-field variants use ``{"Delete":"uuid"}``; tuple variants use arrays;
|
||
named-field variants use objects. For a new typed OA activity::
|
||
|
||
{
|
||
"CreateElement": {
|
||
"id": "1b37b9d7-9a95-4a51-8707-010878a82419",
|
||
"alias": "assess",
|
||
"name": "Assess assistance",
|
||
"description": "Review the request.",
|
||
"parent": null,
|
||
"body": {"Oa": "Activity"}
|
||
}
|
||
}
|
||
|
||
``CreateArtifact``/``EditArtifact`` and ``CreateTrace``/``EditTrace`` use the same
|
||
transaction boundary as engineering edits. Artifact URI, revision and fragment
|
||
strings are opaque references. No URI is fetched or verified by the service.
|
||
Model references and alias uniqueness are checked by the core.
|
||
|
||
At most 200 commands are allowed, counting nested Batch wrappers. Explicit
|
||
creation identities allow later commands in the batch to reference earlier
|
||
creations. The shared ``execute_batch`` applies all operations to one candidate
|
||
and validates the final state. A rejected operation or final structural error
|
||
rejects the whole request. Reference rewrites/deletion cleanup follow the same
|
||
core logic as desktop editing.
|
||
|
||
Dry run still requires ``If-Match``. Its response includes ``revision``,
|
||
``dry_run``, ``changed``, ``issues`` and the proposed ``model``. It neither writes
|
||
nor reserves IDs. A committed response has the same fields except the model is
|
||
omitted. Commit must check the revision again. A no-op is successful with
|
||
``changed: false`` and the original ETag.
|
||
|
||
Contextual process and chain topology
|
||
-------------------------------------
|
||
|
||
Operational processes and system functional chains can combine work
|
||
contributions, exchange contributions, explicit controls and references to
|
||
reusable sequences. Each contribution is an ordinary ``Occurrence`` element
|
||
owned directly by the process/chain, with its stable ID in that owner's
|
||
``steps`` list. ``OccurrenceBody`` includes, for example::
|
||
|
||
{"Action":{"action":"<work UUID>","lifeline":null}}
|
||
{"Control":{"kind":"Choice"}}
|
||
{"SequenceReference":{"sequence":"<process or chain UUID>"}}
|
||
|
||
``ControlKind`` is ``Choice``, ``Merge``, ``Fork``, ``Join`` or ``Loop``.
|
||
``RelationBody.SequenceLink`` connects contribution IDs in one owner and
|
||
contains ``source``, ``target`` and a string ``guard``. Conditions belong to
|
||
choice/loop paths; an explicit ``else`` guard can describe the other path.
|
||
The readable order of the ``steps`` list remains persistent, while links
|
||
express the explicit alternative, parallel and loop topology. The core checks
|
||
owner context, work/exchange endpoint agreement and recursive sequence reuse;
|
||
engineering diagnostics identify incomplete branch cardinality or paths.
|
||
Old exchange-only sequences without explicit links retain their linear meaning.
|
||
|
||
These records use the same create/edit transaction commands and source/model
|
||
replacement routes as other engineering records. For example,
|
||
``GET /api/v1/projects/{id}/relations?kind=SequenceLink`` queries the links.
|
||
The source representation uses ``sequence @alias`` references. The service
|
||
does not execute conditions or simulate a process.
|
||
|
||
Typed scenarios and message values
|
||
----------------------------------
|
||
|
||
Scenario bodies store an explicit ``viewpoint``. Occurrence Exchange bodies
|
||
store ``message_kind``, optional correlated-call ``reply_to`` and contextual
|
||
``values``. Message kinds are Asynchronous, Call, Reply, Create, Delete, or
|
||
``{"Timer":{"expression":"after 5 s"}}``. Each value record contains a
|
||
carried DataField UUID in ``field`` and its textual ``expression``; expressions
|
||
do not change the shared exchange item's type or evaluate server-side.
|
||
|
||
``ScenarioUse`` contains a referenced ``scenario`` and one-to-one lifeline
|
||
``bindings`` records with ``referenced`` and ``local`` UUIDs. The core validates
|
||
viewpoint/layer compatibility, call/reply order and reversal, lifetime order,
|
||
carried fields, binding identity and recursive reuse through the same candidate
|
||
boundary used by the desktop. The normative details are in ``language.rst``.
|
||
|
||
Existing schema-2 files may omit the new fields: missing viewpoint maps to
|
||
MixedLegacy and missing message fields to Asynchronous, null and []. Source
|
||
exports make those compatibility defaults explicit. This preserves original V2
|
||
models while the release schema is being integrated; it does not silently
|
||
reinterpret old mixed lifelines as a strict new viewpoint.
|
||
|
||
Validation
|
||
----------
|
||
|
||
GET returns current-model diagnostics. POST accepts either::
|
||
|
||
{"format":"json", "model": {"...":"complete native Project record"}}
|
||
|
||
or::
|
||
|
||
{"format":"arcadia", "source":"complete source document"}
|
||
|
||
A well-formed validation request returns 200 with ``valid`` and ``issues`` even
|
||
if its candidate is invalid. A malformed request envelope or JSON model record
|
||
is a 400. Source diagnostics include one-based line and column according to the
|
||
shared language parser. Candidate validation does not replace current state.
|
||
|
||
Typed state machines and events
|
||
-------------------------------
|
||
|
||
OA and SA Event records use the same ``SharedBody::Event`` and ``EventBody``
|
||
contract as source and graphical editing. Signal carries same-layer item
|
||
references; Time specifies After/At and an expression; Change specifies a
|
||
condition expression. State vertices include Choice, Junction, Fork, Join,
|
||
ShallowHistory, DeepHistory, EntryPoint, ExitPoint and Terminate, as well as
|
||
State, Mode, Initial and Final. Query these through the ordinary ``kind``
|
||
filters. No timer or executable state-machine service is implied.
|
||
|
||
StateTransition keeps ``trigger``, ``guard`` and ``effect`` prose and adds
|
||
``transition_kind`` (External/Local/Internal), ``triggers`` (Event or work UUIDs)
|
||
and ``effects`` (work UUIDs). The reference lists use UUIDs in JSON and aliases
|
||
in source. Missing new properties in staged schema-2 input hydrate as
|
||
External/[]/[] without changing prior text. These defaults are compatibility
|
||
behavior pending the distinct release schema, not a versionless expansion of
|
||
every future input contract.
|
||
|
||
Use one ordinary transaction to create an event and edit its referring
|
||
transition. Reidentification updates references, and deletion prunes signal
|
||
items and effect references. Removing the last typed trigger with no prior
|
||
trigger prose leaves a descriptive replacement reminder; it does not silently
|
||
introduce a completion transition. Invalid typed references reject the whole
|
||
candidate and preserve revision/ETag. All writes retain the same conditional
|
||
commit and disk persistence contract as other semantic records.
|
||
|
||
Cross-layer transitions
|
||
-----------------------
|
||
|
||
Preview accepts the shared ``TransitionRequest`` record, including direction
|
||
(``OaToSa`` or ``SaToOa``), sources, dependency scope, mappings, selected fields,
|
||
conflict resolutions and optional destination diagram. All fields are explicit
|
||
in OpenAPI. At most 100 sources, 200 mappings and 400 field resolutions are
|
||
accepted. A conflict-free plan must fit the same 200-command transaction limit
|
||
and model-capacity limits.
|
||
|
||
The preview response is ``{"base_revision":1,"plan":{...}}``. It contains
|
||
planned changes, conflicts, warnings and exact commands/IDs. It does not write
|
||
the model or persist a server-side plan. Apply accepts that entire response and
|
||
requires the original ETag in ``If-Match``. It checks both base_revision and the
|
||
core's snapshot fingerprint before executing the shared transition validator.
|
||
Stale revisions fail with 412 and unresolved conflicts with 422. Successful
|
||
apply increments the revision once, preserving the planned identities.
|
||
|
||
A supplied plan is a client-owned command proposal. It is not cryptographically
|
||
authenticated as an untouched server preview; the same authenticated client
|
||
can submit ordinary typed commands. The core fingerprint is a change detector,
|
||
not a security digest. There is no plan token, expiration store or receipt.
|
||
|
||
Error contract
|
||
--------------
|
||
|
||
All errors have ``Content-Type: application/problem+json``. Example::
|
||
|
||
{
|
||
"type":"urn:arcadia:problem:revision_conflict",
|
||
"title":"Precondition Failed",
|
||
"status":412,
|
||
"code":"revision_conflict",
|
||
"detail":"Project changed. Reload and review the current model before retrying.",
|
||
"current_revision":3
|
||
}
|
||
|
||
``issues`` contain severity, code and message, and may include field,
|
||
object_id, line and column. Use
|
||
``code`` for client decisions, not the English message. ``X-Request-ID`` is a
|
||
response correlation UUID. This is not an idempotency key or receipt address.
|
||
|
||
* 400: malformed JSON/envelope/query/UUID or unsupported ETag form.
|
||
* 401: missing or incorrect configured bearer token; includes WWW-Authenticate.
|
||
* 403: browser Origin is unsupported, or unauthenticated Host is not loopback.
|
||
* 404: missing project or route.
|
||
* 405: unsupported HTTP method.
|
||
* 409: requested query revision changed or revision counter exhausted.
|
||
* 412: stale write precondition.
|
||
* 413: body or serialized model exceeds size limit.
|
||
* 415: unsupported Content-Type or Content-Encoding.
|
||
* 422: rejected source/model/command, model count limit, or missing graph root.
|
||
* 428: missing If-Match.
|
||
* 503: work slots full or filesystem/storage unavailable; Retry-After is present.
|
||
* 504: request deadline; an admitted write outcome may be unknown.
|
||
* 507: fixed store cap of 128 models or 256 MiB serialized envelopes reached.
|
||
* 500: unexpected server/worker failure.
|
||
|
||
Deployment and persistence limitations are documented in ``server.md``.
|