Skip to content

Separate entity identity from content in the REST API and gts crate #122

Description

@aviator5

Current behavior and scope

The core gts crate embeds a list of candidate entity ID fields: $id, gtsId, gtsIid, gtsOid, gtsI, gts_id, gts_oid, gts_iid, and id. A similar list selects the type field. GtsEntity::new() uses these conventions to infer metadata from document content.

The immediate scope is entity ingestion through GtsOps and the shared entity model. The REST server delegates registration to GtsOps::add_entity(), and make gts-spec-tests runs the conformance suite against that server. The README describes it as a non-production server for operations and testing.

GtsOps is also used by CLI commands and Rust tests/test helpers, so these callers must be included in the migration. This is not a proposal to require an identifier in every instance payload: direct validation with an explicit type ID remains supported.

Why these heuristics do not belong in the core crate

  • Application field names are an integration concern. A domain object's id, type, or schema may have its own meaning. The core should receive GTS metadata explicitly; a caller or file-format adapter can decide where to obtain it.
  • Identity depends on hidden configuration. When multiple candidate fields exist, their order determines the selected ID. Adding a field or changing configuration can change the store key for the same document.
  • Conflicts are silently resolved. When id identifies an instance of type A but type names type B, the ID chain wins, without a metadata-consistency error.
  • Storage requirements leak into payload requirements. REST rejects an instance without an ID field even with validate=false. File loading instead uses the file path or path#index. A source location becomes an instance identifier even though it cannot determine its GTS type.
  • The complexity is unnecessary for validation. GtsStore::validate_payload(type_id, payload) already validates data without extracting identity from it. Alias lists, selection bookkeeping, precedence rules, and file fallbacks add policy to the shared entity model.

Optional helpers with explicit keys can remain in gts; automatic invocation and fallback lists should be removed from core entity construction and registration.

Proposed contract

Use one representation for schemas and instances:

{
  "gts_id": "gts.vendor.package.namespace.order.v1~acme.shop.orders.order42.v1",
  "content": {
    "amount": 100
  }
}
  • Require a valid gts_id and a JSON content value.
  • Determine entity kind from gts_id: a type ID ending in ~ identifies a schema; an instance ID identifies a payload and determines its type from the chain. Registration needs no separate type_id.
  • Never infer instance identity or type from content. Its fields are governed by the schema; an ID field is required only if the schema requires it.
  • For schemas, require any declared $id to match the external gts_id after stripping the gts:// prefix.
Endpoint Representation
POST /entities One {gts_id, content} object
POST /entities/bulk An array of these objects
GET /entities/{gts_id} One such object
GET /entities {entities: [{gts_id, content}, ...], count, total}

Unlike the current metadata-only listing, list entries include content, matching registration and individual retrieval.

Implementation and migration

  • Pass gts_id and content explicitly through GtsOps, entity construction, and store registration. Entity kind must not be re-inferred from content.$schema.
  • Keep helpers such as extract_id(content, key) and extract_type_id(content, key); callers opt in and specify exactly one key.
  • Update file-loading adapters to obtain GTS IDs from explicit metadata or an explicitly selected field. Keep paths and array indices as source metadata.
  • Preserve validation without registration using an explicit type ID and payload.
  • Update affected CLI callers, Rust tests, API documentation, and conformance fixtures. Coordinate the breaking REST contract with gts-spec-tests; any temporary compatibility extraction belongs in the server/file adapter, outside core entity construction.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions