Documentation
¶
Overview ¶
Package apicontract_local is datatug-cli's OWN provisional copy of the shared JSON types core-investigation-loop's normative transport appendix (datatug/datatug spec/features/core-investigation-loop/api-contract.md) defines: Scope, SourceRef, TypedValue, Fact, Limitation, Binding, Result, Candidate, ExecutionRequest, the agent-info envelope and the structured error envelope.
Plan task 12 (S64) assigns the real schema authority to a new github.com/datatug/datatug-core/pkg/apicontract package (lane S64a, concurrent with this one) that will also freeze the shared fixtures (pkg/apicontract/fixtures/*.json) both server and client consume. This package exists ONLY because that module tag did not exist yet when this stream needed to start writing server code against the contract — see the stream brief (s64-server-task12.md): "start by writing the server against the contract markdown with your own provisional types under pkg/apicontract_local ... and when the lead tells you the core tag exists (or v0.25.0 shows up on `git ls-remote --tags`), replace the local types with the module's and consume its fixtures."
Every type below is named, field-tagged and shaped to be byte-identical to the appendix's own examples, so that swap is a mechanical find-and-replace of the import path (github.com/datatug/datatug-cli/pkg/ apicontract_local -> github.com/datatug/datatug-core/pkg/apicontract) plus deleting this package, not a redesign. Fixtures under ./fixtures mirror the appendix's "Acceptance and migration" section: empty arrays, null/ false/zero/large-integer values, ambiguous/missing inputs, omitted unauthorized metadata, HTTP failure and every error code.
Index ¶
- func NewRequestID() string
- func StatusFor(code ErrorCode) int
- type AgentInfoCapabilities
- type AgentInfoPrincipal
- type AgentInfoProject
- type AgentInfoResponse
- type ApplicableRequest
- type ApplicableResponse
- type Binding
- type BindingOrigin
- type BindingOriginInput
- type Candidate
- type CandidateAmbiguous
- type CandidateChainEntry
- type CandidateState
- type CandidateTarget
- type Column
- type ColumnEntry
- type ColumnProvenance
- type ColumnsResponse
- type Error
- func NewAccessDenied(message string) *Error
- func NewAmbiguousBinding(field, message string) *Error
- func NewError(code ErrorCode, message, field string) *Error
- func NewInvalidRequest(field, message string) *Error
- func NewMissingParameter(field string) *Error
- func NewNotFound(message string) *Error
- func NewResponseTooLarge(message string) *Error
- func NewSourceUnavailable(message string) *Error
- func NewStaleContext(message string) *Error
- func NewTargetRequired(message string, targets []CandidateTarget) *Error
- func NewTimeout(message string) *Error
- func NewTypeMismatch(field, message string) *Error
- func NewUnsupportedProtectedExecution(message string) *Error
- type ErrorBody
- type ErrorCode
- type ErrorEnvelope
- type ExecutionMode
- type ExecutionProfile
- type ExecutionRequest
- type Fact
- type FactOrigin
- type Limitation
- type OriginEvidence
- type PhysicalRef
- type Provenance
- type Recordset
- type RelatedEntry
- type RelatedRequest
- type RelatedResponse
- type RelatedRowsRequest
- type Result
- type Scope
- type SourceRef
- type TypedValue
- func FromGoValue(v any, declaredType string) (TypedValue, error)
- func NewBooleanValue(b bool) TypedValue
- func NewDateTimeValue(t time.Time) TypedValue
- func NewDateValue(text string) (TypedValue, error)
- func NewDecimalValue(text string) (TypedValue, error)
- func NewIntegerText(text string) (TypedValue, error)
- func NewIntegerValue(n int64) TypedValue
- func NewNumberValue(n float64) TypedValue
- func NewStringValue(s string) TypedValue
- func NullValue() TypedValue
- type ValueType
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewRequestID ¶
func NewRequestID() string
NewRequestID returns a fresh opaque correlation ID: 16 random bytes, hex encoded (api-contract.md requires every error to carry one; "Server logs record a correlation ID and operation, not raw facts").
Types ¶
type AgentInfoCapabilities ¶
type AgentInfoCapabilities struct {
ProtectedQueries bool `json:"protectedQueries"`
OpaqueReadOnly bool `json:"opaqueReadOnly"`
}
AgentInfoCapabilities is agent-info's capabilities object.
type AgentInfoPrincipal ¶
type AgentInfoPrincipal struct {
ID string `json:"id"`
Roles []string `json:"roles"`
Groups []string `json:"groups"`
}
AgentInfoPrincipal is agent-info's principal object.
type AgentInfoProject ¶
type AgentInfoProject struct {
ID string `json:"id"`
}
AgentInfoProject is one entry of agent-info's projects array.
type AgentInfoResponse ¶
type AgentInfoResponse struct {
Version string `json:"version"`
Principal AgentInfoPrincipal `json:"principal"`
SecurityContextID string `json:"securityContextId"`
Projects []AgentInfoProject `json:"projects"`
Capabilities AgentInfoCapabilities `json:"capabilities"`
}
AgentInfoResponse is GET agent-info's exact success envelope (api-contract.md "Endpoint table").
type ApplicableRequest ¶
ApplicableRequest is POST queries/applicable's body.
type ApplicableResponse ¶
type ApplicableResponse struct {
Applicable []Candidate `json:"applicable"`
NotYet []Candidate `json:"notYet"`
}
ApplicableResponse is POST queries/applicable's exact success envelope.
type Binding ¶
type Binding struct {
ParameterID string `json:"parameterId"`
Value TypedValue `json:"value"`
Origin BindingOrigin `json:"origin"`
OriginEvidence OriginEvidence `json:"originEvidence"`
FactID string `json:"factId,omitempty"`
}
Binding is one query parameter's resolved value and its provenance.
type BindingOrigin ¶
type BindingOrigin string
BindingOrigin is Binding.Origin's closed set — one more member than FactOrigin ("default", a declared query default).
const ( BindingOriginSelection BindingOrigin = "selection" BindingOriginContext BindingOrigin = "context" BindingOriginManual BindingOrigin = "manual" BindingOriginDefault BindingOrigin = "default" )
type BindingOriginInput ¶
type BindingOriginInput struct {
ParameterID string `json:"parameterId"`
Origin BindingOrigin `json:"origin"`
FactID string `json:"factId,omitempty"`
}
BindingOriginInput is one ExecutionRequest.BindingOrigins entry.
type Candidate ¶
type Candidate struct {
QueryID string `json:"queryId"`
Targets []CandidateTarget `json:"targets"`
SelectedSource string `json:"selectedSource,omitempty"`
Bindings []Binding `json:"bindings"`
Chain []CandidateChainEntry `json:"chain"`
Missing []string `json:"missing"`
Ambiguous []CandidateAmbiguous `json:"ambiguous"`
State CandidateState `json:"state"`
}
Candidate is one library query's resolution state against the caller's available facts (api-contract.md "Endpoint table" / the Candidate type).
type CandidateAmbiguous ¶
type CandidateAmbiguous struct {
ParameterID string `json:"parameterId"`
FactIDs []string `json:"factIds"`
}
CandidateAmbiguous names one parameter more than one distinct automatic value could bind, with the conflicting fact IDs.
type CandidateChainEntry ¶
type CandidateChainEntry struct {
ParameterID string `json:"parameterId"`
FactID string `json:"factId,omitempty"`
Explanation string `json:"explanation"`
}
CandidateChainEntry explains how one parameter resolved (or why it is still missing).
type CandidateState ¶
type CandidateState string
CandidateState is Candidate.State's closed set.
const ( StateRunnable CandidateState = "runnable" StateNeedsInput CandidateState = "needs-input" StateNeedsTarget CandidateState = "needs-target" )
type CandidateTarget ¶
CandidateTarget is one authorized eligible target option on a Candidate.
type ColumnEntry ¶
type ColumnEntry struct {
Column string `json:"column"`
Entity string `json:"entity"`
Field string `json:"field"`
Provenance ColumnProvenance `json:"provenance"`
}
ColumnEntry is one GET semantic/columns response entry. Unmapped columns are omitted entirely (never present with empty entity/field).
type ColumnProvenance ¶
type ColumnProvenance string
ColumnProvenance is ColumnEntry.Provenance's closed set.
const ( ColumnDeclared ColumnProvenance = "declared" ColumnInferred ColumnProvenance = "inferred" )
type ColumnsResponse ¶
type ColumnsResponse struct {
Columns []ColumnEntry `json:"columns"`
}
ColumnsResponse is GET semantic/columns's exact success envelope.
type Error ¶
type Error struct {
Code ErrorCode
Message string
Field string
RequestID string
Targets []CandidateTarget
}
Error is the Go error type every contract-facing handler in this repo returns instead of a bare error, carrying everything ErrorEnvelope needs. It implements the error interface so it composes with errors.Is/As and fmt.Errorf(%w) like any other error.
func NewAccessDenied ¶
NewAccessDenied builds an ACCESS_DENIED error. message MUST name the policy and MUST NOT echo the hidden field/value it protects.
func NewAmbiguousBinding ¶
NewAmbiguousBinding builds an AMBIGUOUS_BINDING error naming the conflicting parameter.
func NewError ¶
NewError builds an *Error, stamping a fresh RequestID. Use the code- specific constructors below where one exists; this is the general escape hatch (e.g. NOT_FOUND, UNAUTHENTICATED).
func NewInvalidRequest ¶
NewInvalidRequest builds an INVALID_REQUEST error naming field.
func NewMissingParameter ¶
NewMissingParameter builds a MISSING_PARAMETER error naming the parameter field.
func NewNotFound ¶
NewNotFound builds a 404 NOT_FOUND error.
func NewResponseTooLarge ¶
NewResponseTooLarge builds a 413 RESPONSE_TOO_LARGE error.
func NewSourceUnavailable ¶
NewSourceUnavailable builds a 503 SOURCE_UNAVAILABLE error.
func NewStaleContext ¶
NewStaleContext builds a 409 STALE_CONTEXT error.
func NewTargetRequired ¶
func NewTargetRequired(message string, targets []CandidateTarget) *Error
NewTargetRequired builds a 400 TARGET_REQUIRED error carrying the authorized eligible target options (api-contract.md: "TARGET_REQUIRED errors return the same authorized target options in error.targets, never hidden source IDs").
func NewTypeMismatch ¶
NewTypeMismatch builds a TYPE_MISMATCH error naming field.
func NewUnsupportedProtectedExecution ¶
NewUnsupportedProtectedExecution builds a 403 UNSUPPORTED_PROTECTED_EXECUTION error (REQ:opaque-sql-limitation).
func (*Error) Envelope ¶
func (e *Error) Envelope() ErrorEnvelope
Envelope converts e into the wire ErrorEnvelope, stamping a fresh RequestID if none was set (defensive: every constructor above already sets one).
type ErrorBody ¶
type ErrorBody struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Field string `json:"field,omitempty"`
RequestID string `json:"requestId"`
Targets []CandidateTarget `json:"targets,omitempty"`
}
ErrorBody is the inner "error" object of the appendix's error envelope. Targets is populated ONLY for TARGET_REQUIRED (api-contract.md: "Only TARGET_REQUIRED may include authorized target options").
type ErrorCode ¶
type ErrorCode string
ErrorCode is the closed set api-contract.md "Security and errors" names.
const ( CodeInvalidRequest ErrorCode = "INVALID_REQUEST" CodeTypeMismatch ErrorCode = "TYPE_MISMATCH" CodeMissingParameter ErrorCode = "MISSING_PARAMETER" CodeAmbiguousBinding ErrorCode = "AMBIGUOUS_BINDING" CodeTargetRequired ErrorCode = "TARGET_REQUIRED" CodeUnauthenticated ErrorCode = "UNAUTHENTICATED" CodeAccessDenied ErrorCode = "ACCESS_DENIED" CodeUnsupportedProtectedExec ErrorCode = "UNSUPPORTED_PROTECTED_EXECUTION" CodeNotFound ErrorCode = "NOT_FOUND" CodeStaleContext ErrorCode = "STALE_CONTEXT" CodeResponseTooLarge ErrorCode = "RESPONSE_TOO_LARGE" CodeTimeout ErrorCode = "TIMEOUT" )
The exact error codes and their HTTP statuses (api-contract.md's own table): "HTTP 400 covers INVALID_REQUEST, TYPE_MISMATCH, MISSING_PARAMETER, AMBIGUOUS_BINDING and TARGET_REQUIRED; 401 UNAUTHENTICATED; 403 ACCESS_DENIED or UNSUPPORTED_PROTECTED_EXECUTION; 404 NOT_FOUND (including invisible resources); 409 STALE_CONTEXT; 413 RESPONSE_TOO_LARGE; 503 SOURCE_UNAVAILABLE; 504 TIMEOUT."
type ErrorEnvelope ¶
type ErrorEnvelope struct {
Error ErrorBody `json:"error"`
}
ErrorEnvelope is the appendix's exact error response shape: {"error": {...}}.
type ExecutionMode ¶
type ExecutionMode string
ExecutionMode is Result.Provenance.Mode / ExecutionRequest.Mode's closed set.
const ( ModeLive ExecutionMode = "live" ModeSnapshot ExecutionMode = "snapshot" )
type ExecutionProfile ¶
type ExecutionProfile string
ExecutionProfile is Result.Provenance.ExecutionProfile's closed set (REQ:opaque-sql-limitation).
const ( ProfileProtected ExecutionProfile = "protected" ProfileOpaquePrivileged ExecutionProfile = "opaque-privileged" )
type ExecutionRequest ¶
type ExecutionRequest struct {
Project string `json:"project"`
Environment string `json:"environment"`
SecurityContextID string `json:"securityContextId"`
Source string `json:"source,omitempty"`
QueryID string `json:"queryId,omitempty"`
DTQL string `json:"dtql,omitempty"`
Parameters map[string]TypedValue `json:"parameters"`
BindingOrigins []BindingOriginInput `json:"bindingOrigins"`
Mode ExecutionMode `json:"mode"`
SnapshotID string `json:"snapshotId,omitempty"`
Limit int `json:"limit,omitempty"`
}
ExecutionRequest is POST exec/run_query's body (api-contract.md "Endpoint table" / the ExecutionRequest type). Exactly one of QueryID/DTQL is required; Source is required for ad-hoc DTQL and optional for a saved query obeying target resolution.
type Fact ¶
type Fact struct {
ID string `json:"id"`
Entity string `json:"entity"`
Field string `json:"field"`
Value TypedValue `json:"value"`
Origin FactOrigin `json:"origin"`
Physical *PhysicalRef `json:"physical,omitempty"`
Mapping string `json:"mapping,omitempty"` // "declared" | "inferred"
Enabled bool `json:"enabled"`
}
Fact is one typed semantic value the browser holds (a grid selection, an Investigation Context item): api-contract.md "Shared JSON types".
type FactOrigin ¶
type FactOrigin string
FactOrigin is Fact.Origin's closed set.
const ( OriginSelection FactOrigin = "selection" OriginContext FactOrigin = "context" OriginManual FactOrigin = "manual" )
type Limitation ¶
type Limitation struct {
Policy string `json:"policy"`
RowsFiltered bool `json:"rowsFiltered"`
HiddenColumns []string `json:"hiddenColumns"`
}
Limitation reports one applied restriction — never the number or values of the rows/columns it restricted (api-contract.md "Shared JSON types").
type OriginEvidence ¶
type OriginEvidence string
OriginEvidence distinguishes a client-reported binding origin from an actual server-attested default (api-contract.md "Endpoint table" / "Binding and context behavior": "The UI must not present client origins as server-attested provenance").
const ( EvidenceServerDefault OriginEvidence = "server-default" EvidenceClientReported OriginEvidence = "client-reported" )
type PhysicalRef ¶
type PhysicalRef struct {
Source string `json:"source"`
Collection string `json:"collection"`
Column string `json:"column"`
}
PhysicalRef is one physical column an EntityField maps to.
type Provenance ¶
type Provenance struct {
Source string `json:"source"`
Collection string `json:"collection,omitempty"`
QueryID string `json:"queryId,omitempty"`
Mode ExecutionMode `json:"mode"`
SnapshotID string `json:"snapshotId,omitempty"`
ObservedAt string `json:"observedAt"`
ExecutionProfile ExecutionProfile `json:"executionProfile"`
}
Provenance is Result.Provenance.
type Recordset ¶
type Recordset struct {
Columns []Column `json:"columns"`
Rows [][]TypedValue `json:"rows"`
}
Recordset is Result.Recordset: ordered columns, and rows with exactly one TypedValue per returned column, in column order.
type RelatedEntry ¶
type RelatedEntry struct {
LookupID string `json:"lookupId"`
Label string `json:"label"`
Source string `json:"source"`
Collection string `json:"collection"`
Count *int `json:"count"`
}
RelatedEntry is one POST semantic/related response entry. Count is omitted (null) when an exact authorized count could not be obtained.
type RelatedRequest ¶
RelatedRequest is POST semantic/related's body (beyond the embedded Scope, sent as query params per api-contract.md's GET/POST placement rule... except semantic/related is itself POST — see api-contract.md "Endpoint table": "POST semantic/related | Scope + {fact:Fact,limit?}"). Scope travels in the JSON body here (unlike semantic/columns' GET, which carries it in the URL query) because the whole request is a POST body.
type RelatedResponse ¶
type RelatedResponse struct {
Related []RelatedEntry `json:"related"`
Truncated bool `json:"truncated"`
}
RelatedResponse is POST semantic/related's exact success envelope.
type RelatedRowsRequest ¶
type RelatedRowsRequest struct {
Scope
LookupID string `json:"lookupId"`
Value TypedValue `json:"value"`
Limit int `json:"limit,omitempty"`
}
RelatedRowsRequest is POST semantic/related/rows's body.
type Result ¶
type Result struct {
Recordset Recordset `json:"recordset"`
Limitations []Limitation `json:"limitations"`
BindingsApplied []Binding `json:"bindingsApplied"`
Provenance Provenance `json:"provenance"`
Truncated bool `json:"truncated"`
}
Result is the appendix's one execution/read response shape, returned by exec/run_query and semantic/related/rows.
type Scope ¶
type Scope struct {
Project string `json:"project"`
Environment string `json:"environment"`
SecurityContextID string `json:"securityContextId"`
}
Scope identifies one authorized request context: api-contract.md "Scope and identity". Project and Environment are required, nonempty project- local IDs; SecurityContextID is the opaque staleness token agent-info issues (never authentication/authority on its own).
type SourceRef ¶
SourceRef identifies one collection within one registered project source: api-contract.md "Scope and identity". Source is a stable project-local ID resolved through the project's source registry for Scope.Environment — see pkg/api's resolver — never a filesystem path, URL or credential.
type TypedValue ¶
type TypedValue struct {
Type ValueType
// Text carries the wire "value" for string, integer, decimal, date and
// datetime — every variant whose JSON value is itself a string.
Text string
// Number carries the wire "value" for TypeNumber: a finite float64,
// exactly representable as a JSON number (never NaN/Inf).
Number float64
// Bool carries the wire "value" for TypeBoolean.
Bool bool
}
TypedValue is the appendix's {type, value} discriminated union. Exactly one of the typed fields below is meaningful, selected by Type; the zero TypedValue{} is invalid (no valid Type) and must never be marshalled or returned to a caller — build one with the New* constructors or Null().
func FromGoValue ¶
func FromGoValue(v any, declaredType string) (TypedValue, error)
FromGoValue converts a Go-native value (as returned by secureread's row data: typically int64/float64/string/bool/[]byte/time.Time/nil from a database/sql driver) into a TypedValue. declaredType is an optional hint (a QueryDef/Recordset column's own declared "type" string: "integer", "number", "string", "boolean", "date", "datetime", "decimal") consulted first; when it is empty or does not match v's runtime shape, the type is inferred from v itself. This is Phase 1's own engineering rule for shaping a database row into the appendix's typed Result.recordset — the appendix defines the wire TypedValue shape but not how a server derives one from a driver value, and no richer per-column type registry exists yet in this codebase to consult instead.
func NewBooleanValue ¶
func NewBooleanValue(b bool) TypedValue
NewBooleanValue builds a TypeBoolean TypedValue.
func NewDateTimeValue ¶
func NewDateTimeValue(t time.Time) TypedValue
NewDateTimeValue builds a TypeDateTime TypedValue, normalizing t to UTC and formatting it RFC3339 with a literal "Z" offset.
func NewDateValue ¶
func NewDateValue(text string) (TypedValue, error)
NewDateValue builds a TypeDate TypedValue from a YYYY-MM-DD string, validating it is a real calendar date.
func NewDecimalValue ¶
func NewDecimalValue(text string) (TypedValue, error)
NewDecimalValue builds a TypeDecimal TypedValue from already-formatted canonical decimal text, validating it.
func NewIntegerText ¶
func NewIntegerText(text string) (TypedValue, error)
NewIntegerText builds a TypeInteger TypedValue from already-decimal text (e.g. one too large for int64), validating it is canonical.
func NewIntegerValue ¶
func NewIntegerValue(n int64) TypedValue
NewIntegerValue builds a TypeInteger TypedValue from a Go int64, always producing the appendix's canonical decimal text (no leading zeros, "-0" normalized to "0").
func NewNumberValue ¶
func NewNumberValue(n float64) TypedValue
NewNumberValue builds a TypeNumber TypedValue. n must be finite.
func NewStringValue ¶
func NewStringValue(s string) TypedValue
NewStringValue builds a TypeString TypedValue.
func (TypedValue) Equal ¶
func (v TypedValue) Equal(other TypedValue) bool
Equal reports whether v and other carry the same type AND the same value — AC:typed-context-isolation's "distinct typed values 5 and \"5\"... types stay distinct": TypedValue{integer,"5"} and TypedValue{string,"5"} are never Equal, regardless of how their Go-native forms might compare.
func (TypedValue) IsZero ¶
func (v TypedValue) IsZero() bool
IsZero reports whether v is the zero TypedValue (no Type set) — never a valid wire value, only a "not built yet" sentinel for Go callers.
func (TypedValue) MarshalJSON ¶
func (v TypedValue) MarshalJSON() ([]byte, error)
MarshalJSON writes v as {"type":..., "value":...}, exactly the appendix's shape. An invalid Type or a non-finite Number is refused rather than silently emitting a malformed envelope.
func (TypedValue) Native ¶
func (v TypedValue) Native() (any, error)
Native converts v to a plain Go value suitable for driver-bound query parameter binding (dal.Param substitution -> database/sql args) and for JSON-free comparisons: string/date/datetime stay string, integer parses to int64 (returning an error for a canonical value too large for int64 — Phase 1's demo parameters never need bigint), decimal stays string (preserve precision; no lossy float64 conversion), number is float64, boolean is bool, null is nil.
func (*TypedValue) UnmarshalJSON ¶
func (v *TypedValue) UnmarshalJSON(data []byte) error
UnmarshalJSON parses {"type":..., "value":...}, validating value against type's exact wire rules (canonical integer/decimal text, calendar date, RFC3339-UTC datetime, finite number). Unknown fields alongside type/value are rejected: the appendix requires "exact field names", and this is also where a client-supplied principal/role hidden in a values payload would first be caught (defense in depth; the real guard is server-side scope validation elsewhere).
type ValueType ¶
type ValueType string
ValueType is TypedValue's discriminant, exactly the appendix's eight variants (api-contract.md "Shared JSON types").
const ( TypeString ValueType = "string" TypeNumber ValueType = "number" TypeInteger ValueType = "integer" TypeDecimal ValueType = "decimal" TypeBoolean ValueType = "boolean" TypeDate ValueType = "date" TypeDateTime ValueType = "datetime" TypeNull ValueType = "null" )
The eight TypedValue variants. Every other string is invalid.