feat(api): add duration_ms to audit log entries (#758)

* feat(api): add duration_ms to audit log entries

Server-computed duration in milliseconds (started_at → ended_at) on
the list audit logs endpoint. Null when ended_at is not set.

Closes #749

* feat(api): add duration_ms to audit log entries and type audit endpoints

- Add server-computed duration_ms (started_at → ended_at) to audit log
  list response. Null when ended_at is not set.
- Add typed Pydantic response models for both audit log endpoints
  (list and stats) so they appear in the OpenAPI spec.
- Regenerate OpenAPI spec and all client SDKs.

Closes #749

* chore: regenerate docs skill after audit log response models
This commit is contained in:
Nicolò Boschi 2026-03-30 12:32:02 +02:00 committed by GitHub
parent d8050387e4
commit a8a63818c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 2488 additions and 45 deletions

View file

@ -5021,12 +5021,55 @@ def _register_routes(app: FastAPI):
# ---- Audit Logs ----
class AuditLogEntry(BaseModel):
"""A single audit log entry."""
id: str
action: str
transport: str
bank_id: str | None
started_at: str | None
ended_at: str | None
duration_ms: int | None = Field(
default=None,
description="Server-computed duration in milliseconds (started_at → ended_at). Null if not yet completed.",
)
request: dict[str, Any] | None
response: dict[str, Any] | None
metadata: dict[str, Any]
class AuditLogListResponse(BaseModel):
"""Response model for list audit logs endpoint."""
bank_id: str
total: int
limit: int
offset: int
items: list[AuditLogEntry]
class AuditLogStatsBucket(BaseModel):
"""A single time bucket in audit log stats."""
time: str
actions: dict[str, int]
total: int
class AuditLogStatsResponse(BaseModel):
"""Response model for audit log stats endpoint."""
bank_id: str
period: str
trunc: str
start: str
buckets: list[AuditLogStatsBucket]
@app.get(
"/v1/default/banks/{bank_id}/audit-logs",
summary="List audit logs",
description="List audit log entries for a bank, ordered by most recent first.",
operation_id="list_audit_logs",
tags=["Audit"],
response_model=AuditLogListResponse,
)
async def api_list_audit_logs(
bank_id: str,
@ -5103,6 +5146,10 @@ def _register_routes(app: FastAPI):
items = []
for row in rows:
duration_ms = None
if row["started_at"] and row["ended_at"]:
duration_ms = int((row["ended_at"] - row["started_at"]).total_seconds() * 1000)
items.append(
{
"id": str(row["id"]),
@ -5111,6 +5158,7 @@ def _register_routes(app: FastAPI):
"bank_id": row["bank_id"],
"started_at": row["started_at"].isoformat() if row["started_at"] else None,
"ended_at": row["ended_at"].isoformat() if row["ended_at"] else None,
"duration_ms": duration_ms,
"request": json.loads(row["request"]) if row["request"] else None,
"response": json.loads(row["response"]) if row["response"] else None,
"metadata": json.loads(row["metadata"]) if row["metadata"] else {},
@ -5140,6 +5188,7 @@ def _register_routes(app: FastAPI):
description="Get audit log counts grouped by time bucket for charting.",
operation_id="audit_log_stats",
tags=["Audit"],
response_model=AuditLogStatsResponse,
)
async def api_audit_log_stats(
bank_id: str,

View file

@ -2868,7 +2868,8 @@ paths:
"200":
content:
application/json:
schema: {}
schema:
$ref: '#/components/schemas/AuditLogListResponse'
description: Successful Response
"422":
content:
@ -2924,7 +2925,8 @@ paths:
"200":
content:
application/json:
schema: {}
schema:
$ref: '#/components/schemas/AuditLogStatsResponse'
description: Successful Response
"422":
content:
@ -2972,6 +2974,185 @@ components:
- operation_id
- status
title: AsyncOperationSubmitResponse
AuditLogEntry:
description: A single audit log entry.
example:
duration_ms: 5
request:
key: ""
metadata:
key: ""
bank_id: bank_id
response:
key: ""
action: action
started_at: started_at
id: id
transport: transport
ended_at: ended_at
properties:
id:
title: Id
type: string
action:
title: Action
type: string
transport:
title: Transport
type: string
bank_id:
nullable: true
type: string
started_at:
nullable: true
type: string
ended_at:
nullable: true
type: string
duration_ms:
nullable: true
type: integer
request:
additionalProperties: {}
nullable: true
response:
additionalProperties: {}
nullable: true
metadata:
additionalProperties: {}
title: Metadata
required:
- action
- bank_id
- ended_at
- id
- metadata
- request
- response
- started_at
- transport
title: AuditLogEntry
AuditLogListResponse:
description: Response model for list audit logs endpoint.
example:
total: 0
offset: 1
bank_id: bank_id
limit: 6
items:
- duration_ms: 5
request:
key: ""
metadata:
key: ""
bank_id: bank_id
response:
key: ""
action: action
started_at: started_at
id: id
transport: transport
ended_at: ended_at
- duration_ms: 5
request:
key: ""
metadata:
key: ""
bank_id: bank_id
response:
key: ""
action: action
started_at: started_at
id: id
transport: transport
ended_at: ended_at
properties:
bank_id:
title: Bank Id
type: string
total:
title: Total
type: integer
limit:
title: Limit
type: integer
offset:
title: Offset
type: integer
items:
items:
$ref: '#/components/schemas/AuditLogEntry'
type: array
required:
- bank_id
- items
- limit
- offset
- total
title: AuditLogListResponse
AuditLogStatsBucket:
description: A single time bucket in audit log stats.
example:
total: 6
time: time
actions:
key: 0
properties:
time:
title: Time
type: string
actions:
additionalProperties:
type: integer
title: Actions
total:
title: Total
type: integer
required:
- actions
- time
- total
title: AuditLogStatsBucket
AuditLogStatsResponse:
description: Response model for audit log stats endpoint.
example:
period: period
trunc: trunc
bank_id: bank_id
buckets:
- total: 6
time: time
actions:
key: 0
- total: 6
time: time
actions:
key: 0
start: start
properties:
bank_id:
title: Bank Id
type: string
period:
title: Period
type: string
trunc:
title: Trunc
type: string
start:
title: Start
type: string
buckets:
items:
$ref: '#/components/schemas/AuditLogStatsBucket'
type: array
required:
- bank_id
- buckets
- period
- start
- trunc
title: AuditLogStatsResponse
BackgroundResponse:
description: "Response model for background update. Deprecated: use MissionResponse\
\ instead."

View file

@ -49,7 +49,7 @@ func (r ApiAuditLogStatsRequest) Authorization(authorization string) ApiAuditLog
return r
}
func (r ApiAuditLogStatsRequest) Execute() (interface{}, *http.Response, error) {
func (r ApiAuditLogStatsRequest) Execute() (*AuditLogStatsResponse, *http.Response, error) {
return r.ApiService.AuditLogStatsExecute(r)
}
@ -71,13 +71,13 @@ func (a *AuditAPIService) AuditLogStats(ctx context.Context, bankId string) ApiA
}
// Execute executes the request
// @return interface{}
func (a *AuditAPIService) AuditLogStatsExecute(r ApiAuditLogStatsRequest) (interface{}, *http.Response, error) {
// @return AuditLogStatsResponse
func (a *AuditAPIService) AuditLogStatsExecute(r ApiAuditLogStatsRequest) (*AuditLogStatsResponse, *http.Response, error) {
var (
localVarHTTPMethod = http.MethodGet
localVarPostBody interface{}
formFiles []formFile
localVarReturnValue interface{}
localVarReturnValue *AuditLogStatsResponse
)
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuditAPIService.AuditLogStats")
@ -222,7 +222,7 @@ func (r ApiListAuditLogsRequest) Authorization(authorization string) ApiListAudi
return r
}
func (r ApiListAuditLogsRequest) Execute() (interface{}, *http.Response, error) {
func (r ApiListAuditLogsRequest) Execute() (*AuditLogListResponse, *http.Response, error) {
return r.ApiService.ListAuditLogsExecute(r)
}
@ -244,13 +244,13 @@ func (a *AuditAPIService) ListAuditLogs(ctx context.Context, bankId string) ApiL
}
// Execute executes the request
// @return interface{}
func (a *AuditAPIService) ListAuditLogsExecute(r ApiListAuditLogsRequest) (interface{}, *http.Response, error) {
// @return AuditLogListResponse
func (a *AuditAPIService) ListAuditLogsExecute(r ApiListAuditLogsRequest) (*AuditLogListResponse, *http.Response, error) {
var (
localVarHTTPMethod = http.MethodGet
localVarPostBody interface{}
formFiles []formFile
localVarReturnValue interface{}
localVarReturnValue *AuditLogListResponse
)
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuditAPIService.ListAuditLogs")

View file

@ -0,0 +1,442 @@
/*
Hindsight HTTP API
HTTP API for Hindsight
API version: 0.4.20
*/
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
package hindsight
import (
"encoding/json"
"bytes"
"fmt"
)
// checks if the AuditLogEntry type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &AuditLogEntry{}
// AuditLogEntry A single audit log entry.
type AuditLogEntry struct {
Id string `json:"id"`
Action string `json:"action"`
Transport string `json:"transport"`
BankId NullableString `json:"bank_id"`
StartedAt NullableString `json:"started_at"`
EndedAt NullableString `json:"ended_at"`
DurationMs NullableInt32 `json:"duration_ms,omitempty"`
Request map[string]interface{} `json:"request"`
Response map[string]interface{} `json:"response"`
Metadata map[string]interface{} `json:"metadata"`
}
type _AuditLogEntry AuditLogEntry
// NewAuditLogEntry instantiates a new AuditLogEntry object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewAuditLogEntry(id string, action string, transport string, bankId NullableString, startedAt NullableString, endedAt NullableString, request map[string]interface{}, response map[string]interface{}, metadata map[string]interface{}) *AuditLogEntry {
this := AuditLogEntry{}
this.Id = id
this.Action = action
this.Transport = transport
this.BankId = bankId
this.StartedAt = startedAt
this.EndedAt = endedAt
this.Request = request
this.Response = response
this.Metadata = metadata
return &this
}
// NewAuditLogEntryWithDefaults instantiates a new AuditLogEntry object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewAuditLogEntryWithDefaults() *AuditLogEntry {
this := AuditLogEntry{}
return &this
}
// GetId returns the Id field value
func (o *AuditLogEntry) GetId() string {
if o == nil {
var ret string
return ret
}
return o.Id
}
// GetIdOk returns a tuple with the Id field value
// and a boolean to check if the value has been set.
func (o *AuditLogEntry) GetIdOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Id, true
}
// SetId sets field value
func (o *AuditLogEntry) SetId(v string) {
o.Id = v
}
// GetAction returns the Action field value
func (o *AuditLogEntry) GetAction() string {
if o == nil {
var ret string
return ret
}
return o.Action
}
// GetActionOk returns a tuple with the Action field value
// and a boolean to check if the value has been set.
func (o *AuditLogEntry) GetActionOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Action, true
}
// SetAction sets field value
func (o *AuditLogEntry) SetAction(v string) {
o.Action = v
}
// GetTransport returns the Transport field value
func (o *AuditLogEntry) GetTransport() string {
if o == nil {
var ret string
return ret
}
return o.Transport
}
// GetTransportOk returns a tuple with the Transport field value
// and a boolean to check if the value has been set.
func (o *AuditLogEntry) GetTransportOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Transport, true
}
// SetTransport sets field value
func (o *AuditLogEntry) SetTransport(v string) {
o.Transport = v
}
// GetBankId returns the BankId field value
// If the value is explicit nil, the zero value for string will be returned
func (o *AuditLogEntry) GetBankId() string {
if o == nil || o.BankId.Get() == nil {
var ret string
return ret
}
return *o.BankId.Get()
}
// GetBankIdOk returns a tuple with the BankId field value
// and a boolean to check if the value has been set.
// NOTE: If the value is an explicit nil, `nil, true` will be returned
func (o *AuditLogEntry) GetBankIdOk() (*string, bool) {
if o == nil {
return nil, false
}
return o.BankId.Get(), o.BankId.IsSet()
}
// SetBankId sets field value
func (o *AuditLogEntry) SetBankId(v string) {
o.BankId.Set(&v)
}
// GetStartedAt returns the StartedAt field value
// If the value is explicit nil, the zero value for string will be returned
func (o *AuditLogEntry) GetStartedAt() string {
if o == nil || o.StartedAt.Get() == nil {
var ret string
return ret
}
return *o.StartedAt.Get()
}
// GetStartedAtOk returns a tuple with the StartedAt field value
// and a boolean to check if the value has been set.
// NOTE: If the value is an explicit nil, `nil, true` will be returned
func (o *AuditLogEntry) GetStartedAtOk() (*string, bool) {
if o == nil {
return nil, false
}
return o.StartedAt.Get(), o.StartedAt.IsSet()
}
// SetStartedAt sets field value
func (o *AuditLogEntry) SetStartedAt(v string) {
o.StartedAt.Set(&v)
}
// GetEndedAt returns the EndedAt field value
// If the value is explicit nil, the zero value for string will be returned
func (o *AuditLogEntry) GetEndedAt() string {
if o == nil || o.EndedAt.Get() == nil {
var ret string
return ret
}
return *o.EndedAt.Get()
}
// GetEndedAtOk returns a tuple with the EndedAt field value
// and a boolean to check if the value has been set.
// NOTE: If the value is an explicit nil, `nil, true` will be returned
func (o *AuditLogEntry) GetEndedAtOk() (*string, bool) {
if o == nil {
return nil, false
}
return o.EndedAt.Get(), o.EndedAt.IsSet()
}
// SetEndedAt sets field value
func (o *AuditLogEntry) SetEndedAt(v string) {
o.EndedAt.Set(&v)
}
// GetDurationMs returns the DurationMs field value if set, zero value otherwise (both if not set or set to explicit null).
func (o *AuditLogEntry) GetDurationMs() int32 {
if o == nil || IsNil(o.DurationMs.Get()) {
var ret int32
return ret
}
return *o.DurationMs.Get()
}
// GetDurationMsOk returns a tuple with the DurationMs field value if set, nil otherwise
// and a boolean to check if the value has been set.
// NOTE: If the value is an explicit nil, `nil, true` will be returned
func (o *AuditLogEntry) GetDurationMsOk() (*int32, bool) {
if o == nil {
return nil, false
}
return o.DurationMs.Get(), o.DurationMs.IsSet()
}
// HasDurationMs returns a boolean if a field has been set.
func (o *AuditLogEntry) HasDurationMs() bool {
if o != nil && o.DurationMs.IsSet() {
return true
}
return false
}
// SetDurationMs gets a reference to the given NullableInt32 and assigns it to the DurationMs field.
func (o *AuditLogEntry) SetDurationMs(v int32) {
o.DurationMs.Set(&v)
}
// SetDurationMsNil sets the value for DurationMs to be an explicit nil
func (o *AuditLogEntry) SetDurationMsNil() {
o.DurationMs.Set(nil)
}
// UnsetDurationMs ensures that no value is present for DurationMs, not even an explicit nil
func (o *AuditLogEntry) UnsetDurationMs() {
o.DurationMs.Unset()
}
// GetRequest returns the Request field value
// If the value is explicit nil, the zero value for map[string]interface{} will be returned
func (o *AuditLogEntry) GetRequest() map[string]interface{} {
if o == nil {
var ret map[string]interface{}
return ret
}
return o.Request
}
// GetRequestOk returns a tuple with the Request field value
// and a boolean to check if the value has been set.
// NOTE: If the value is an explicit nil, `nil, true` will be returned
func (o *AuditLogEntry) GetRequestOk() (map[string]interface{}, bool) {
if o == nil || IsNil(o.Request) {
return map[string]interface{}{}, false
}
return o.Request, true
}
// SetRequest sets field value
func (o *AuditLogEntry) SetRequest(v map[string]interface{}) {
o.Request = v
}
// GetResponse returns the Response field value
// If the value is explicit nil, the zero value for map[string]interface{} will be returned
func (o *AuditLogEntry) GetResponse() map[string]interface{} {
if o == nil {
var ret map[string]interface{}
return ret
}
return o.Response
}
// GetResponseOk returns a tuple with the Response field value
// and a boolean to check if the value has been set.
// NOTE: If the value is an explicit nil, `nil, true` will be returned
func (o *AuditLogEntry) GetResponseOk() (map[string]interface{}, bool) {
if o == nil || IsNil(o.Response) {
return map[string]interface{}{}, false
}
return o.Response, true
}
// SetResponse sets field value
func (o *AuditLogEntry) SetResponse(v map[string]interface{}) {
o.Response = v
}
// GetMetadata returns the Metadata field value
func (o *AuditLogEntry) GetMetadata() map[string]interface{} {
if o == nil {
var ret map[string]interface{}
return ret
}
return o.Metadata
}
// GetMetadataOk returns a tuple with the Metadata field value
// and a boolean to check if the value has been set.
func (o *AuditLogEntry) GetMetadataOk() (map[string]interface{}, bool) {
if o == nil {
return map[string]interface{}{}, false
}
return o.Metadata, true
}
// SetMetadata sets field value
func (o *AuditLogEntry) SetMetadata(v map[string]interface{}) {
o.Metadata = v
}
func (o AuditLogEntry) MarshalJSON() ([]byte, error) {
toSerialize,err := o.ToMap()
if err != nil {
return []byte{}, err
}
return json.Marshal(toSerialize)
}
func (o AuditLogEntry) ToMap() (map[string]interface{}, error) {
toSerialize := map[string]interface{}{}
toSerialize["id"] = o.Id
toSerialize["action"] = o.Action
toSerialize["transport"] = o.Transport
toSerialize["bank_id"] = o.BankId.Get()
toSerialize["started_at"] = o.StartedAt.Get()
toSerialize["ended_at"] = o.EndedAt.Get()
if o.DurationMs.IsSet() {
toSerialize["duration_ms"] = o.DurationMs.Get()
}
if o.Request != nil {
toSerialize["request"] = o.Request
}
if o.Response != nil {
toSerialize["response"] = o.Response
}
toSerialize["metadata"] = o.Metadata
return toSerialize, nil
}
func (o *AuditLogEntry) UnmarshalJSON(data []byte) (err error) {
// This validates that all required properties are included in the JSON object
// by unmarshalling the object into a generic map with string keys and checking
// that every required field exists as a key in the generic map.
requiredProperties := []string{
"id",
"action",
"transport",
"bank_id",
"started_at",
"ended_at",
"request",
"response",
"metadata",
}
allProperties := make(map[string]interface{})
err = json.Unmarshal(data, &allProperties)
if err != nil {
return err;
}
for _, requiredProperty := range(requiredProperties) {
if _, exists := allProperties[requiredProperty]; !exists {
return fmt.Errorf("no value given for required property %v", requiredProperty)
}
}
varAuditLogEntry := _AuditLogEntry{}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
err = decoder.Decode(&varAuditLogEntry)
if err != nil {
return err
}
*o = AuditLogEntry(varAuditLogEntry)
return err
}
type NullableAuditLogEntry struct {
value *AuditLogEntry
isSet bool
}
func (v NullableAuditLogEntry) Get() *AuditLogEntry {
return v.value
}
func (v *NullableAuditLogEntry) Set(val *AuditLogEntry) {
v.value = val
v.isSet = true
}
func (v NullableAuditLogEntry) IsSet() bool {
return v.isSet
}
func (v *NullableAuditLogEntry) Unset() {
v.value = nil
v.isSet = false
}
func NewNullableAuditLogEntry(val *AuditLogEntry) *NullableAuditLogEntry {
return &NullableAuditLogEntry{value: val, isSet: true}
}
func (v NullableAuditLogEntry) MarshalJSON() ([]byte, error) {
return json.Marshal(v.value)
}
func (v *NullableAuditLogEntry) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

View file

@ -0,0 +1,270 @@
/*
Hindsight HTTP API
HTTP API for Hindsight
API version: 0.4.20
*/
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
package hindsight
import (
"encoding/json"
"bytes"
"fmt"
)
// checks if the AuditLogListResponse type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &AuditLogListResponse{}
// AuditLogListResponse Response model for list audit logs endpoint.
type AuditLogListResponse struct {
BankId string `json:"bank_id"`
Total int32 `json:"total"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
Items []AuditLogEntry `json:"items"`
}
type _AuditLogListResponse AuditLogListResponse
// NewAuditLogListResponse instantiates a new AuditLogListResponse object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewAuditLogListResponse(bankId string, total int32, limit int32, offset int32, items []AuditLogEntry) *AuditLogListResponse {
this := AuditLogListResponse{}
this.BankId = bankId
this.Total = total
this.Limit = limit
this.Offset = offset
this.Items = items
return &this
}
// NewAuditLogListResponseWithDefaults instantiates a new AuditLogListResponse object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewAuditLogListResponseWithDefaults() *AuditLogListResponse {
this := AuditLogListResponse{}
return &this
}
// GetBankId returns the BankId field value
func (o *AuditLogListResponse) GetBankId() string {
if o == nil {
var ret string
return ret
}
return o.BankId
}
// GetBankIdOk returns a tuple with the BankId field value
// and a boolean to check if the value has been set.
func (o *AuditLogListResponse) GetBankIdOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.BankId, true
}
// SetBankId sets field value
func (o *AuditLogListResponse) SetBankId(v string) {
o.BankId = v
}
// GetTotal returns the Total field value
func (o *AuditLogListResponse) GetTotal() int32 {
if o == nil {
var ret int32
return ret
}
return o.Total
}
// GetTotalOk returns a tuple with the Total field value
// and a boolean to check if the value has been set.
func (o *AuditLogListResponse) GetTotalOk() (*int32, bool) {
if o == nil {
return nil, false
}
return &o.Total, true
}
// SetTotal sets field value
func (o *AuditLogListResponse) SetTotal(v int32) {
o.Total = v
}
// GetLimit returns the Limit field value
func (o *AuditLogListResponse) GetLimit() int32 {
if o == nil {
var ret int32
return ret
}
return o.Limit
}
// GetLimitOk returns a tuple with the Limit field value
// and a boolean to check if the value has been set.
func (o *AuditLogListResponse) GetLimitOk() (*int32, bool) {
if o == nil {
return nil, false
}
return &o.Limit, true
}
// SetLimit sets field value
func (o *AuditLogListResponse) SetLimit(v int32) {
o.Limit = v
}
// GetOffset returns the Offset field value
func (o *AuditLogListResponse) GetOffset() int32 {
if o == nil {
var ret int32
return ret
}
return o.Offset
}
// GetOffsetOk returns a tuple with the Offset field value
// and a boolean to check if the value has been set.
func (o *AuditLogListResponse) GetOffsetOk() (*int32, bool) {
if o == nil {
return nil, false
}
return &o.Offset, true
}
// SetOffset sets field value
func (o *AuditLogListResponse) SetOffset(v int32) {
o.Offset = v
}
// GetItems returns the Items field value
func (o *AuditLogListResponse) GetItems() []AuditLogEntry {
if o == nil {
var ret []AuditLogEntry
return ret
}
return o.Items
}
// GetItemsOk returns a tuple with the Items field value
// and a boolean to check if the value has been set.
func (o *AuditLogListResponse) GetItemsOk() ([]AuditLogEntry, bool) {
if o == nil {
return nil, false
}
return o.Items, true
}
// SetItems sets field value
func (o *AuditLogListResponse) SetItems(v []AuditLogEntry) {
o.Items = v
}
func (o AuditLogListResponse) MarshalJSON() ([]byte, error) {
toSerialize,err := o.ToMap()
if err != nil {
return []byte{}, err
}
return json.Marshal(toSerialize)
}
func (o AuditLogListResponse) ToMap() (map[string]interface{}, error) {
toSerialize := map[string]interface{}{}
toSerialize["bank_id"] = o.BankId
toSerialize["total"] = o.Total
toSerialize["limit"] = o.Limit
toSerialize["offset"] = o.Offset
toSerialize["items"] = o.Items
return toSerialize, nil
}
func (o *AuditLogListResponse) UnmarshalJSON(data []byte) (err error) {
// This validates that all required properties are included in the JSON object
// by unmarshalling the object into a generic map with string keys and checking
// that every required field exists as a key in the generic map.
requiredProperties := []string{
"bank_id",
"total",
"limit",
"offset",
"items",
}
allProperties := make(map[string]interface{})
err = json.Unmarshal(data, &allProperties)
if err != nil {
return err;
}
for _, requiredProperty := range(requiredProperties) {
if _, exists := allProperties[requiredProperty]; !exists {
return fmt.Errorf("no value given for required property %v", requiredProperty)
}
}
varAuditLogListResponse := _AuditLogListResponse{}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
err = decoder.Decode(&varAuditLogListResponse)
if err != nil {
return err
}
*o = AuditLogListResponse(varAuditLogListResponse)
return err
}
type NullableAuditLogListResponse struct {
value *AuditLogListResponse
isSet bool
}
func (v NullableAuditLogListResponse) Get() *AuditLogListResponse {
return v.value
}
func (v *NullableAuditLogListResponse) Set(val *AuditLogListResponse) {
v.value = val
v.isSet = true
}
func (v NullableAuditLogListResponse) IsSet() bool {
return v.isSet
}
func (v *NullableAuditLogListResponse) Unset() {
v.value = nil
v.isSet = false
}
func NewNullableAuditLogListResponse(val *AuditLogListResponse) *NullableAuditLogListResponse {
return &NullableAuditLogListResponse{value: val, isSet: true}
}
func (v NullableAuditLogListResponse) MarshalJSON() ([]byte, error) {
return json.Marshal(v.value)
}
func (v *NullableAuditLogListResponse) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

View file

@ -0,0 +1,214 @@
/*
Hindsight HTTP API
HTTP API for Hindsight
API version: 0.4.20
*/
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
package hindsight
import (
"encoding/json"
"bytes"
"fmt"
)
// checks if the AuditLogStatsBucket type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &AuditLogStatsBucket{}
// AuditLogStatsBucket A single time bucket in audit log stats.
type AuditLogStatsBucket struct {
Time string `json:"time"`
Actions map[string]int32 `json:"actions"`
Total int32 `json:"total"`
}
type _AuditLogStatsBucket AuditLogStatsBucket
// NewAuditLogStatsBucket instantiates a new AuditLogStatsBucket object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewAuditLogStatsBucket(time string, actions map[string]int32, total int32) *AuditLogStatsBucket {
this := AuditLogStatsBucket{}
this.Time = time
this.Actions = actions
this.Total = total
return &this
}
// NewAuditLogStatsBucketWithDefaults instantiates a new AuditLogStatsBucket object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewAuditLogStatsBucketWithDefaults() *AuditLogStatsBucket {
this := AuditLogStatsBucket{}
return &this
}
// GetTime returns the Time field value
func (o *AuditLogStatsBucket) GetTime() string {
if o == nil {
var ret string
return ret
}
return o.Time
}
// GetTimeOk returns a tuple with the Time field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsBucket) GetTimeOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Time, true
}
// SetTime sets field value
func (o *AuditLogStatsBucket) SetTime(v string) {
o.Time = v
}
// GetActions returns the Actions field value
func (o *AuditLogStatsBucket) GetActions() map[string]int32 {
if o == nil {
var ret map[string]int32
return ret
}
return o.Actions
}
// GetActionsOk returns a tuple with the Actions field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsBucket) GetActionsOk() (map[string]int32, bool) {
if o == nil {
return map[string]int32{}, false
}
return o.Actions, true
}
// SetActions sets field value
func (o *AuditLogStatsBucket) SetActions(v map[string]int32) {
o.Actions = v
}
// GetTotal returns the Total field value
func (o *AuditLogStatsBucket) GetTotal() int32 {
if o == nil {
var ret int32
return ret
}
return o.Total
}
// GetTotalOk returns a tuple with the Total field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsBucket) GetTotalOk() (*int32, bool) {
if o == nil {
return nil, false
}
return &o.Total, true
}
// SetTotal sets field value
func (o *AuditLogStatsBucket) SetTotal(v int32) {
o.Total = v
}
func (o AuditLogStatsBucket) MarshalJSON() ([]byte, error) {
toSerialize,err := o.ToMap()
if err != nil {
return []byte{}, err
}
return json.Marshal(toSerialize)
}
func (o AuditLogStatsBucket) ToMap() (map[string]interface{}, error) {
toSerialize := map[string]interface{}{}
toSerialize["time"] = o.Time
toSerialize["actions"] = o.Actions
toSerialize["total"] = o.Total
return toSerialize, nil
}
func (o *AuditLogStatsBucket) UnmarshalJSON(data []byte) (err error) {
// This validates that all required properties are included in the JSON object
// by unmarshalling the object into a generic map with string keys and checking
// that every required field exists as a key in the generic map.
requiredProperties := []string{
"time",
"actions",
"total",
}
allProperties := make(map[string]interface{})
err = json.Unmarshal(data, &allProperties)
if err != nil {
return err;
}
for _, requiredProperty := range(requiredProperties) {
if _, exists := allProperties[requiredProperty]; !exists {
return fmt.Errorf("no value given for required property %v", requiredProperty)
}
}
varAuditLogStatsBucket := _AuditLogStatsBucket{}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
err = decoder.Decode(&varAuditLogStatsBucket)
if err != nil {
return err
}
*o = AuditLogStatsBucket(varAuditLogStatsBucket)
return err
}
type NullableAuditLogStatsBucket struct {
value *AuditLogStatsBucket
isSet bool
}
func (v NullableAuditLogStatsBucket) Get() *AuditLogStatsBucket {
return v.value
}
func (v *NullableAuditLogStatsBucket) Set(val *AuditLogStatsBucket) {
v.value = val
v.isSet = true
}
func (v NullableAuditLogStatsBucket) IsSet() bool {
return v.isSet
}
func (v *NullableAuditLogStatsBucket) Unset() {
v.value = nil
v.isSet = false
}
func NewNullableAuditLogStatsBucket(val *AuditLogStatsBucket) *NullableAuditLogStatsBucket {
return &NullableAuditLogStatsBucket{value: val, isSet: true}
}
func (v NullableAuditLogStatsBucket) MarshalJSON() ([]byte, error) {
return json.Marshal(v.value)
}
func (v *NullableAuditLogStatsBucket) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

View file

@ -0,0 +1,270 @@
/*
Hindsight HTTP API
HTTP API for Hindsight
API version: 0.4.20
*/
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
package hindsight
import (
"encoding/json"
"bytes"
"fmt"
)
// checks if the AuditLogStatsResponse type satisfies the MappedNullable interface at compile time
var _ MappedNullable = &AuditLogStatsResponse{}
// AuditLogStatsResponse Response model for audit log stats endpoint.
type AuditLogStatsResponse struct {
BankId string `json:"bank_id"`
Period string `json:"period"`
Trunc string `json:"trunc"`
Start string `json:"start"`
Buckets []AuditLogStatsBucket `json:"buckets"`
}
type _AuditLogStatsResponse AuditLogStatsResponse
// NewAuditLogStatsResponse instantiates a new AuditLogStatsResponse object
// This constructor will assign default values to properties that have it defined,
// and makes sure properties required by API are set, but the set of arguments
// will change when the set of required properties is changed
func NewAuditLogStatsResponse(bankId string, period string, trunc string, start string, buckets []AuditLogStatsBucket) *AuditLogStatsResponse {
this := AuditLogStatsResponse{}
this.BankId = bankId
this.Period = period
this.Trunc = trunc
this.Start = start
this.Buckets = buckets
return &this
}
// NewAuditLogStatsResponseWithDefaults instantiates a new AuditLogStatsResponse object
// This constructor will only assign default values to properties that have it defined,
// but it doesn't guarantee that properties required by API are set
func NewAuditLogStatsResponseWithDefaults() *AuditLogStatsResponse {
this := AuditLogStatsResponse{}
return &this
}
// GetBankId returns the BankId field value
func (o *AuditLogStatsResponse) GetBankId() string {
if o == nil {
var ret string
return ret
}
return o.BankId
}
// GetBankIdOk returns a tuple with the BankId field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsResponse) GetBankIdOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.BankId, true
}
// SetBankId sets field value
func (o *AuditLogStatsResponse) SetBankId(v string) {
o.BankId = v
}
// GetPeriod returns the Period field value
func (o *AuditLogStatsResponse) GetPeriod() string {
if o == nil {
var ret string
return ret
}
return o.Period
}
// GetPeriodOk returns a tuple with the Period field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsResponse) GetPeriodOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Period, true
}
// SetPeriod sets field value
func (o *AuditLogStatsResponse) SetPeriod(v string) {
o.Period = v
}
// GetTrunc returns the Trunc field value
func (o *AuditLogStatsResponse) GetTrunc() string {
if o == nil {
var ret string
return ret
}
return o.Trunc
}
// GetTruncOk returns a tuple with the Trunc field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsResponse) GetTruncOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Trunc, true
}
// SetTrunc sets field value
func (o *AuditLogStatsResponse) SetTrunc(v string) {
o.Trunc = v
}
// GetStart returns the Start field value
func (o *AuditLogStatsResponse) GetStart() string {
if o == nil {
var ret string
return ret
}
return o.Start
}
// GetStartOk returns a tuple with the Start field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsResponse) GetStartOk() (*string, bool) {
if o == nil {
return nil, false
}
return &o.Start, true
}
// SetStart sets field value
func (o *AuditLogStatsResponse) SetStart(v string) {
o.Start = v
}
// GetBuckets returns the Buckets field value
func (o *AuditLogStatsResponse) GetBuckets() []AuditLogStatsBucket {
if o == nil {
var ret []AuditLogStatsBucket
return ret
}
return o.Buckets
}
// GetBucketsOk returns a tuple with the Buckets field value
// and a boolean to check if the value has been set.
func (o *AuditLogStatsResponse) GetBucketsOk() ([]AuditLogStatsBucket, bool) {
if o == nil {
return nil, false
}
return o.Buckets, true
}
// SetBuckets sets field value
func (o *AuditLogStatsResponse) SetBuckets(v []AuditLogStatsBucket) {
o.Buckets = v
}
func (o AuditLogStatsResponse) MarshalJSON() ([]byte, error) {
toSerialize,err := o.ToMap()
if err != nil {
return []byte{}, err
}
return json.Marshal(toSerialize)
}
func (o AuditLogStatsResponse) ToMap() (map[string]interface{}, error) {
toSerialize := map[string]interface{}{}
toSerialize["bank_id"] = o.BankId
toSerialize["period"] = o.Period
toSerialize["trunc"] = o.Trunc
toSerialize["start"] = o.Start
toSerialize["buckets"] = o.Buckets
return toSerialize, nil
}
func (o *AuditLogStatsResponse) UnmarshalJSON(data []byte) (err error) {
// This validates that all required properties are included in the JSON object
// by unmarshalling the object into a generic map with string keys and checking
// that every required field exists as a key in the generic map.
requiredProperties := []string{
"bank_id",
"period",
"trunc",
"start",
"buckets",
}
allProperties := make(map[string]interface{})
err = json.Unmarshal(data, &allProperties)
if err != nil {
return err;
}
for _, requiredProperty := range(requiredProperties) {
if _, exists := allProperties[requiredProperty]; !exists {
return fmt.Errorf("no value given for required property %v", requiredProperty)
}
}
varAuditLogStatsResponse := _AuditLogStatsResponse{}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
err = decoder.Decode(&varAuditLogStatsResponse)
if err != nil {
return err
}
*o = AuditLogStatsResponse(varAuditLogStatsResponse)
return err
}
type NullableAuditLogStatsResponse struct {
value *AuditLogStatsResponse
isSet bool
}
func (v NullableAuditLogStatsResponse) Get() *AuditLogStatsResponse {
return v.value
}
func (v *NullableAuditLogStatsResponse) Set(val *AuditLogStatsResponse) {
v.value = val
v.isSet = true
}
func (v NullableAuditLogStatsResponse) IsSet() bool {
return v.isSet
}
func (v *NullableAuditLogStatsResponse) Unset() {
v.value = nil
v.isSet = false
}
func NewNullableAuditLogStatsResponse(val *AuditLogStatsResponse) *NullableAuditLogStatsResponse {
return &NullableAuditLogStatsResponse{value: val, isSet: true}
}
func (v NullableAuditLogStatsResponse) MarshalJSON() ([]byte, error) {
return json.Marshal(v.value)
}
func (v *NullableAuditLogStatsResponse) UnmarshalJSON(src []byte) error {
v.isSet = true
return json.Unmarshal(src, &v.value)
}

View file

@ -18,6 +18,10 @@ hindsight_client_api/exceptions.py
hindsight_client_api/models/__init__.py
hindsight_client_api/models/add_background_request.py
hindsight_client_api/models/async_operation_submit_response.py
hindsight_client_api/models/audit_log_entry.py
hindsight_client_api/models/audit_log_list_response.py
hindsight_client_api/models/audit_log_stats_bucket.py
hindsight_client_api/models/audit_log_stats_response.py
hindsight_client_api/models/background_response.py
hindsight_client_api/models/bank_config_response.py
hindsight_client_api/models/bank_config_update.py

View file

@ -43,6 +43,10 @@ from hindsight_client_api.exceptions import ApiException
# import models into sdk package
from hindsight_client_api.models.add_background_request import AddBackgroundRequest
from hindsight_client_api.models.async_operation_submit_response import AsyncOperationSubmitResponse
from hindsight_client_api.models.audit_log_entry import AuditLogEntry
from hindsight_client_api.models.audit_log_list_response import AuditLogListResponse
from hindsight_client_api.models.audit_log_stats_bucket import AuditLogStatsBucket
from hindsight_client_api.models.audit_log_stats_response import AuditLogStatsResponse
from hindsight_client_api.models.background_response import BackgroundResponse
from hindsight_client_api.models.bank_config_response import BankConfigResponse
from hindsight_client_api.models.bank_config_update import BankConfigUpdate

View file

@ -17,8 +17,10 @@ from typing import Any, Dict, List, Optional, Tuple, Union
from typing_extensions import Annotated
from pydantic import Field, StrictStr
from typing import Any, Optional
from typing import Optional
from typing_extensions import Annotated
from hindsight_client_api.models.audit_log_list_response import AuditLogListResponse
from hindsight_client_api.models.audit_log_stats_response import AuditLogStatsResponse
from hindsight_client_api.api_client import ApiClient, RequestSerialized
from hindsight_client_api.api_response import ApiResponse
@ -57,7 +59,7 @@ class AuditApi:
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> object:
) -> AuditLogStatsResponse:
"""Audit log statistics
Get audit log counts grouped by time bucket for charting.
@ -104,7 +106,7 @@ class AuditApi:
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "object",
'200': "AuditLogStatsResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
@ -137,7 +139,7 @@ class AuditApi:
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> ApiResponse[object]:
) -> ApiResponse[AuditLogStatsResponse]:
"""Audit log statistics
Get audit log counts grouped by time bucket for charting.
@ -184,7 +186,7 @@ class AuditApi:
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "object",
'200': "AuditLogStatsResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
@ -264,7 +266,7 @@ class AuditApi:
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "object",
'200': "AuditLogStatsResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
@ -373,7 +375,7 @@ class AuditApi:
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> object:
) -> AuditLogListResponse:
"""List audit logs
List audit log entries for a bank, ordered by most recent first.
@ -432,7 +434,7 @@ class AuditApi:
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "object",
'200': "AuditLogListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
@ -469,7 +471,7 @@ class AuditApi:
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> ApiResponse[object]:
) -> ApiResponse[AuditLogListResponse]:
"""List audit logs
List audit log entries for a bank, ordered by most recent first.
@ -528,7 +530,7 @@ class AuditApi:
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "object",
'200': "AuditLogListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
@ -624,7 +626,7 @@ class AuditApi:
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "object",
'200': "AuditLogListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(

View file

@ -16,6 +16,10 @@
# import models into model package
from hindsight_client_api.models.add_background_request import AddBackgroundRequest
from hindsight_client_api.models.async_operation_submit_response import AsyncOperationSubmitResponse
from hindsight_client_api.models.audit_log_entry import AuditLogEntry
from hindsight_client_api.models.audit_log_list_response import AuditLogListResponse
from hindsight_client_api.models.audit_log_stats_bucket import AuditLogStatsBucket
from hindsight_client_api.models.audit_log_stats_response import AuditLogStatsResponse
from hindsight_client_api.models.background_response import BackgroundResponse
from hindsight_client_api.models.bank_config_response import BankConfigResponse
from hindsight_client_api.models.bank_config_update import BankConfigUpdate

View file

@ -0,0 +1,135 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.4.20
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
from __future__ import annotations
import pprint
import re # noqa: F401
import json
from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from typing import Optional, Set
from typing_extensions import Self
class AuditLogEntry(BaseModel):
"""
A single audit log entry.
""" # noqa: E501
id: StrictStr
action: StrictStr
transport: StrictStr
bank_id: Optional[StrictStr]
started_at: Optional[StrictStr]
ended_at: Optional[StrictStr]
duration_ms: Optional[StrictInt] = None
request: Optional[Dict[str, Any]]
response: Optional[Dict[str, Any]]
metadata: Dict[str, Any]
__properties: ClassVar[List[str]] = ["id", "action", "transport", "bank_id", "started_at", "ended_at", "duration_ms", "request", "response", "metadata"]
model_config = ConfigDict(
populate_by_name=True,
validate_assignment=True,
protected_namespaces=(),
)
def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod
def from_json(cls, json_str: str) -> Optional[Self]:
"""Create an instance of AuditLogEntry from a JSON string"""
return cls.from_dict(json.loads(json_str))
def to_dict(self) -> Dict[str, Any]:
"""Return the dictionary representation of the model using alias.
This has the following differences from calling pydantic's
`self.model_dump(by_alias=True)`:
* `None` is only added to the output dict for nullable fields that
were set at model initialization. Other fields with value `None`
are ignored.
"""
excluded_fields: Set[str] = set([
])
_dict = self.model_dump(
by_alias=True,
exclude=excluded_fields,
exclude_none=True,
)
# set to None if bank_id (nullable) is None
# and model_fields_set contains the field
if self.bank_id is None and "bank_id" in self.model_fields_set:
_dict['bank_id'] = None
# set to None if started_at (nullable) is None
# and model_fields_set contains the field
if self.started_at is None and "started_at" in self.model_fields_set:
_dict['started_at'] = None
# set to None if ended_at (nullable) is None
# and model_fields_set contains the field
if self.ended_at is None and "ended_at" in self.model_fields_set:
_dict['ended_at'] = None
# set to None if duration_ms (nullable) is None
# and model_fields_set contains the field
if self.duration_ms is None and "duration_ms" in self.model_fields_set:
_dict['duration_ms'] = None
# set to None if request (nullable) is None
# and model_fields_set contains the field
if self.request is None and "request" in self.model_fields_set:
_dict['request'] = None
# set to None if response (nullable) is None
# and model_fields_set contains the field
if self.response is None and "response" in self.model_fields_set:
_dict['response'] = None
return _dict
@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of AuditLogEntry from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"id": obj.get("id"),
"action": obj.get("action"),
"transport": obj.get("transport"),
"bank_id": obj.get("bank_id"),
"started_at": obj.get("started_at"),
"ended_at": obj.get("ended_at"),
"duration_ms": obj.get("duration_ms"),
"request": obj.get("request"),
"response": obj.get("response"),
"metadata": obj.get("metadata")
})
return _obj

View file

@ -0,0 +1,103 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.4.20
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
from __future__ import annotations
import pprint
import re # noqa: F401
import json
from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List
from hindsight_client_api.models.audit_log_entry import AuditLogEntry
from typing import Optional, Set
from typing_extensions import Self
class AuditLogListResponse(BaseModel):
"""
Response model for list audit logs endpoint.
""" # noqa: E501
bank_id: StrictStr
total: StrictInt
limit: StrictInt
offset: StrictInt
items: List[AuditLogEntry]
__properties: ClassVar[List[str]] = ["bank_id", "total", "limit", "offset", "items"]
model_config = ConfigDict(
populate_by_name=True,
validate_assignment=True,
protected_namespaces=(),
)
def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod
def from_json(cls, json_str: str) -> Optional[Self]:
"""Create an instance of AuditLogListResponse from a JSON string"""
return cls.from_dict(json.loads(json_str))
def to_dict(self) -> Dict[str, Any]:
"""Return the dictionary representation of the model using alias.
This has the following differences from calling pydantic's
`self.model_dump(by_alias=True)`:
* `None` is only added to the output dict for nullable fields that
were set at model initialization. Other fields with value `None`
are ignored.
"""
excluded_fields: Set[str] = set([
])
_dict = self.model_dump(
by_alias=True,
exclude=excluded_fields,
exclude_none=True,
)
# override the default output from pydantic by calling `to_dict()` of each item in items (list)
_items = []
if self.items:
for _item_items in self.items:
if _item_items:
_items.append(_item_items.to_dict())
_dict['items'] = _items
return _dict
@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of AuditLogListResponse from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"bank_id": obj.get("bank_id"),
"total": obj.get("total"),
"limit": obj.get("limit"),
"offset": obj.get("offset"),
"items": [AuditLogEntry.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None
})
return _obj

View file

@ -0,0 +1,91 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.4.20
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
from __future__ import annotations
import pprint
import re # noqa: F401
import json
from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List
from typing import Optional, Set
from typing_extensions import Self
class AuditLogStatsBucket(BaseModel):
"""
A single time bucket in audit log stats.
""" # noqa: E501
time: StrictStr
actions: Dict[str, StrictInt]
total: StrictInt
__properties: ClassVar[List[str]] = ["time", "actions", "total"]
model_config = ConfigDict(
populate_by_name=True,
validate_assignment=True,
protected_namespaces=(),
)
def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod
def from_json(cls, json_str: str) -> Optional[Self]:
"""Create an instance of AuditLogStatsBucket from a JSON string"""
return cls.from_dict(json.loads(json_str))
def to_dict(self) -> Dict[str, Any]:
"""Return the dictionary representation of the model using alias.
This has the following differences from calling pydantic's
`self.model_dump(by_alias=True)`:
* `None` is only added to the output dict for nullable fields that
were set at model initialization. Other fields with value `None`
are ignored.
"""
excluded_fields: Set[str] = set([
])
_dict = self.model_dump(
by_alias=True,
exclude=excluded_fields,
exclude_none=True,
)
return _dict
@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of AuditLogStatsBucket from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"time": obj.get("time"),
"actions": obj.get("actions"),
"total": obj.get("total")
})
return _obj

View file

@ -0,0 +1,103 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.4.20
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
from __future__ import annotations
import pprint
import re # noqa: F401
import json
from pydantic import BaseModel, ConfigDict, StrictStr
from typing import Any, ClassVar, Dict, List
from hindsight_client_api.models.audit_log_stats_bucket import AuditLogStatsBucket
from typing import Optional, Set
from typing_extensions import Self
class AuditLogStatsResponse(BaseModel):
"""
Response model for audit log stats endpoint.
""" # noqa: E501
bank_id: StrictStr
period: StrictStr
trunc: StrictStr
start: StrictStr
buckets: List[AuditLogStatsBucket]
__properties: ClassVar[List[str]] = ["bank_id", "period", "trunc", "start", "buckets"]
model_config = ConfigDict(
populate_by_name=True,
validate_assignment=True,
protected_namespaces=(),
)
def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod
def from_json(cls, json_str: str) -> Optional[Self]:
"""Create an instance of AuditLogStatsResponse from a JSON string"""
return cls.from_dict(json.loads(json_str))
def to_dict(self) -> Dict[str, Any]:
"""Return the dictionary representation of the model using alias.
This has the following differences from calling pydantic's
`self.model_dump(by_alias=True)`:
* `None` is only added to the output dict for nullable fields that
were set at model initialization. Other fields with value `None`
are ignored.
"""
excluded_fields: Set[str] = set([
])
_dict = self.model_dump(
by_alias=True,
exclude=excluded_fields,
exclude_none=True,
)
# override the default output from pydantic by calling `to_dict()` of each item in buckets (list)
_items = []
if self.buckets:
for _item_buckets in self.buckets:
if _item_buckets:
_items.append(_item_buckets.to_dict())
_dict['buckets'] = _items
return _dict
@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of AuditLogStatsResponse from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"bank_id": obj.get("bank_id"),
"period": obj.get("period"),
"trunc": obj.get("trunc"),
"start": obj.get("start"),
"buckets": [AuditLogStatsBucket.from_dict(_item) for _item in obj["buckets"]] if obj.get("buckets") is not None else None
})
return _obj

View file

@ -40,6 +40,140 @@ export type AsyncOperationSubmitResponse = {
status: string;
};
/**
* AuditLogEntry
*
* A single audit log entry.
*/
export type AuditLogEntry = {
/**
* Id
*/
id: string;
/**
* Action
*/
action: string;
/**
* Transport
*/
transport: string;
/**
* Bank Id
*/
bank_id: string | null;
/**
* Started At
*/
started_at: string | null;
/**
* Ended At
*/
ended_at: string | null;
/**
* Duration Ms
*
* Server-computed duration in milliseconds (started_at ended_at). Null if not yet completed.
*/
duration_ms?: number | null;
/**
* Request
*/
request: {
[key: string]: unknown;
} | null;
/**
* Response
*/
response: {
[key: string]: unknown;
} | null;
/**
* Metadata
*/
metadata: {
[key: string]: unknown;
};
};
/**
* AuditLogListResponse
*
* Response model for list audit logs endpoint.
*/
export type AuditLogListResponse = {
/**
* Bank Id
*/
bank_id: string;
/**
* Total
*/
total: number;
/**
* Limit
*/
limit: number;
/**
* Offset
*/
offset: number;
/**
* Items
*/
items: Array<AuditLogEntry>;
};
/**
* AuditLogStatsBucket
*
* A single time bucket in audit log stats.
*/
export type AuditLogStatsBucket = {
/**
* Time
*/
time: string;
/**
* Actions
*/
actions: {
[key: string]: number;
};
/**
* Total
*/
total: number;
};
/**
* AuditLogStatsResponse
*
* Response model for audit log stats endpoint.
*/
export type AuditLogStatsResponse = {
/**
* Bank Id
*/
bank_id: string;
/**
* Period
*/
period: string;
/**
* Trunc
*/
trunc: string;
/**
* Start
*/
start: string;
/**
* Buckets
*/
buckets: Array<AuditLogStatsBucket>;
};
/**
* BackgroundResponse
*
@ -4937,9 +5071,12 @@ export type ListAuditLogsResponses = {
/**
* Successful Response
*/
200: unknown;
200: AuditLogListResponse;
};
export type ListAuditLogsResponse =
ListAuditLogsResponses[keyof ListAuditLogsResponses];
export type AuditLogStatsData = {
body?: never;
headers?: {
@ -4984,5 +5121,8 @@ export type AuditLogStatsResponses = {
/**
* Successful Response
*/
200: unknown;
200: AuditLogStatsResponse;
};
export type AuditLogStatsResponse2 =
AuditLogStatsResponses[keyof AuditLogStatsResponses];

View file

@ -4215,7 +4215,9 @@
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
"schema": {
"$ref": "#/components/schemas/AuditLogListResponse"
}
}
}
},
@ -4302,7 +4304,9 @@
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
"schema": {
"$ref": "#/components/schemas/AuditLogStatsResponse"
}
}
}
},
@ -4370,6 +4374,211 @@
"status": "queued"
}
},
"AuditLogEntry": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"action": {
"type": "string",
"title": "Action"
},
"transport": {
"type": "string",
"title": "Transport"
},
"bank_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Bank Id"
},
"started_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Started At"
},
"ended_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Ended At"
},
"duration_ms": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Duration Ms",
"description": "Server-computed duration in milliseconds (started_at \u2192 ended_at). Null if not yet completed."
},
"request": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Request"
},
"response": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Response"
},
"metadata": {
"additionalProperties": true,
"type": "object",
"title": "Metadata"
}
},
"type": "object",
"required": [
"id",
"action",
"transport",
"bank_id",
"started_at",
"ended_at",
"request",
"response",
"metadata"
],
"title": "AuditLogEntry",
"description": "A single audit log entry."
},
"AuditLogListResponse": {
"properties": {
"bank_id": {
"type": "string",
"title": "Bank Id"
},
"total": {
"type": "integer",
"title": "Total"
},
"limit": {
"type": "integer",
"title": "Limit"
},
"offset": {
"type": "integer",
"title": "Offset"
},
"items": {
"items": {
"$ref": "#/components/schemas/AuditLogEntry"
},
"type": "array",
"title": "Items"
}
},
"type": "object",
"required": [
"bank_id",
"total",
"limit",
"offset",
"items"
],
"title": "AuditLogListResponse",
"description": "Response model for list audit logs endpoint."
},
"AuditLogStatsBucket": {
"properties": {
"time": {
"type": "string",
"title": "Time"
},
"actions": {
"additionalProperties": {
"type": "integer"
},
"type": "object",
"title": "Actions"
},
"total": {
"type": "integer",
"title": "Total"
}
},
"type": "object",
"required": [
"time",
"actions",
"total"
],
"title": "AuditLogStatsBucket",
"description": "A single time bucket in audit log stats."
},
"AuditLogStatsResponse": {
"properties": {
"bank_id": {
"type": "string",
"title": "Bank Id"
},
"period": {
"type": "string",
"title": "Period"
},
"trunc": {
"type": "string",
"title": "Trunc"
},
"start": {
"type": "string",
"title": "Start"
},
"buckets": {
"items": {
"$ref": "#/components/schemas/AuditLogStatsBucket"
},
"type": "array",
"title": "Buckets"
}
},
"type": "object",
"required": [
"bank_id",
"period",
"trunc",
"start",
"buckets"
],
"title": "AuditLogStatsResponse",
"description": "Response model for audit log stats endpoint."
},
"BackgroundResponse": {
"properties": {
"mission": {

View file

@ -0,0 +1,19 @@
---
hide_table_of_contents: true
---
import PageHero from '@site/src/components/PageHero';
<PageHero title="OpenAI Codex CLI Changelog" subtitle="Hindsight memory integration for OpenAI Codex CLI." />
[← Codex CLI integration](https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/codex)
## [0.1.0](https://github.com/vectorize-io/hindsight/tree/integrations/codex/v0.1.0)
**Features**
- Added Hindsight memory integration for OpenAI Codex CLI with three hook scripts: SessionStart (daemon warm-up), UserPromptSubmit (auto-recall), and Stop (auto-retain). ([`0b17a67c`](https://github.com/vectorize-io/hindsight/commit/0b17a67c))
- Full-session retain with session-level upsert using session ID as document ID. ([`0b17a67c`](https://github.com/vectorize-io/hindsight/commit/0b17a67c))
- Dynamic bank IDs for per-project memory isolation. ([`0b17a67c`](https://github.com/vectorize-io/hindsight/commit/0b17a67c))
- Automatic daemon lifecycle management with background pre-start. ([`0b17a67c`](https://github.com/vectorize-io/hindsight/commit/0b17a67c))
- 57 automated tests covering content processing and end-to-end hook behavior. ([`71125cd9`](https://github.com/vectorize-io/hindsight/commit/71125cd9))

View file

@ -947,6 +947,7 @@ Configuration for MCP server endpoints.
|----------|-------------|---------|
| `HINDSIGHT_API_MCP_ENABLED` | Enable MCP server at `/mcp/{bank_id}/` | `true` |
| `HINDSIGHT_API_MCP_ENABLED_TOOLS` | Comma-separated allowlist of MCP tools to expose globally (empty = all tools) | - |
| `HINDSIGHT_API_MCP_STATELESS` | Use stateless HTTP transport (POST-only). When `false`, enables stateful mode with GET/SSE support for server-initiated messages | `false` |
| `HINDSIGHT_API_MCP_AUTH_TOKEN` | Bearer token for MCP authentication (optional) | - |
| `HINDSIGHT_API_MCP_LOCAL_BANK_ID` | Memory bank ID for local MCP | `mcp` |
| `HINDSIGHT_API_MCP_INSTRUCTIONS` | Additional instructions appended to retain/recall tool descriptions | - |

View file

@ -4215,7 +4215,9 @@
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
"schema": {
"$ref": "#/components/schemas/AuditLogListResponse"
}
}
}
},
@ -4302,7 +4304,9 @@
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
"schema": {
"$ref": "#/components/schemas/AuditLogStatsResponse"
}
}
}
},
@ -4370,6 +4374,211 @@
"status": "queued"
}
},
"AuditLogEntry": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"action": {
"type": "string",
"title": "Action"
},
"transport": {
"type": "string",
"title": "Transport"
},
"bank_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Bank Id"
},
"started_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Started At"
},
"ended_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Ended At"
},
"duration_ms": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Duration Ms",
"description": "Server-computed duration in milliseconds (started_at \u2192 ended_at). Null if not yet completed."
},
"request": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Request"
},
"response": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Response"
},
"metadata": {
"additionalProperties": true,
"type": "object",
"title": "Metadata"
}
},
"type": "object",
"required": [
"id",
"action",
"transport",
"bank_id",
"started_at",
"ended_at",
"request",
"response",
"metadata"
],
"title": "AuditLogEntry",
"description": "A single audit log entry."
},
"AuditLogListResponse": {
"properties": {
"bank_id": {
"type": "string",
"title": "Bank Id"
},
"total": {
"type": "integer",
"title": "Total"
},
"limit": {
"type": "integer",
"title": "Limit"
},
"offset": {
"type": "integer",
"title": "Offset"
},
"items": {
"items": {
"$ref": "#/components/schemas/AuditLogEntry"
},
"type": "array",
"title": "Items"
}
},
"type": "object",
"required": [
"bank_id",
"total",
"limit",
"offset",
"items"
],
"title": "AuditLogListResponse",
"description": "Response model for list audit logs endpoint."
},
"AuditLogStatsBucket": {
"properties": {
"time": {
"type": "string",
"title": "Time"
},
"actions": {
"additionalProperties": {
"type": "integer"
},
"type": "object",
"title": "Actions"
},
"total": {
"type": "integer",
"title": "Total"
}
},
"type": "object",
"required": [
"time",
"actions",
"total"
],
"title": "AuditLogStatsBucket",
"description": "A single time bucket in audit log stats."
},
"AuditLogStatsResponse": {
"properties": {
"bank_id": {
"type": "string",
"title": "Bank Id"
},
"period": {
"type": "string",
"title": "Period"
},
"trunc": {
"type": "string",
"title": "Trunc"
},
"start": {
"type": "string",
"title": "Start"
},
"buckets": {
"items": {
"$ref": "#/components/schemas/AuditLogStatsBucket"
},
"type": "array",
"title": "Buckets"
}
},
"type": "object",
"required": [
"bank_id",
"period",
"trunc",
"start",
"buckets"
],
"title": "AuditLogStatsResponse",
"description": "Response model for audit log stats endpoint."
},
"BackgroundResponse": {
"properties": {
"mission": {

View file

@ -4,30 +4,23 @@ sidebar_position: 6
# OpenAI Codex CLI
[View Changelog →](../../changelog/integrations/codex.md)
Persistent memory for [OpenAI Codex CLI](https://github.com/openai/codex) using [Hindsight](https://vectorize.io/hindsight). Three Python hook scripts automatically recall relevant context before each prompt and retain conversations after each turn — no changes to your Codex workflow required.
## Quick Start
```bash
# 1. Clone the Hindsight repo and install the plugin
git clone https://github.com/vectorize-io/hindsight.git
cd hindsight/hindsight-integrations/codex
./install.sh
# 2. Configure your Hindsight connection
cat > ~/.hindsight/codex.json << 'EOF'
{
"hindsightApiUrl": "https://api.hindsight.vectorize.io",
"hindsightApiToken": "hsk_your_token_here",
"bankId": "codex"
}
EOF
# 3. Start Codex — memory is live
codex
curl -fsSL https://hindsight.vectorize.io/get-codex | bash
```
For a local Hindsight instance, set `hindsightApiUrl` to `http://localhost:9077` and omit `hindsightApiToken`.
The installer will guide you through choosing local or cloud mode and configuring your connection. Once installed, start a new Codex session — memory is live.
To uninstall:
```bash
curl -fsSL https://hindsight.vectorize.io/get-codex | bash -s -- --uninstall
```
## Features
@ -172,7 +165,7 @@ With this config, running Codex in `~/projects/api` and `~/projects/frontend` st
## Troubleshooting
**Hooks not firing**: Check that `~/.codex/config.toml` contains `codex_hooks = true` under `[features]`. Re-run `install.sh` to write this automatically.
**Hooks not firing**: Check that `~/.codex/config.toml` contains `codex_hooks = true` under `[features]`. Re-run the installer to fix this automatically.
**No memories recalled**: Recall returns results only after something has been retained. Either complete one Codex session first, or seed your bank manually using the [cookbook example](https://github.com/vectorize-io/hindsight-cookbook/tree/main/applications/codex-memory).