Class: ForestAdminAgent::Routes::Resources::AuditTrail

Inherits:
AbstractAuthenticatedRoute show all
Includes:
AuditTrailRoute, Utils
Defined in:
lib/forest_admin_agent/routes/resources/audit_trail.rb

Overview

Record-history route, mirroring the Node agent's /_audit-trail/{collection}/:id.

Registered only when config.audit_trail[:database] is set, in which case the agent factory built the store the capture layer writes to.

Constant Summary collapse

DEFAULT_PAGE_SIZE =
20
MAX_PAGE_SIZE =
100
DATE_ONLY =
/\A\d{4}-\d{2}-\d{2}\z/
DATE_TIME =

Wall-clock datetime, T or space separator, seconds optional: YYYY-MM-DD[T ]HH:mm[:ss].

/\A(\d{4}-\d{2}-\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?\z/

Instance Method Summary collapse

Methods included from AuditTrailRoute

#assert_record_in_scope, #audited_projection, #earlier_bound, #first_record, #key_projection, #record_segments, #scoped_record, #serialize_record, #store

Methods inherited from AbstractAuthenticatedRoute

#build, #format_attributes, #redacted_full_projection, #redacted_projection_with_pks

Methods inherited from AbstractRoute

#add_route, #build, #initialize, #routes

Constructor Details

This class inherits a constructor from ForestAdminAgent::Routes::AbstractRoute

Instance Method Details

#handle_request(args = {}) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/forest_admin_agent/routes/resources/audit_trail.rb', line 39

def handle_request(args = {})
  context = build(args)
  context.permissions.can?(:read, context.collection)
  assert_record_in_scope(context, context.collection, args[:params]['id'])

  skip, limit = parse_pagination(args)
  filters = {
    collection: context.collection.name,
    # args[:params]['id'] is already Forest's packed id, the form the audit store keys on — plus any id
    # this record was filed under before a rename, each bounded by when it stopped being that id.
    record_id: record_segments(context.collection, args[:params]['id']),
    **parse_filters(args)
  }

  history = store.list_by_record(**filters, skip: skip, limit: limit, order: parse_sort(args))
  # `count` reflects the active filters (not the absolute total) and is independent of the page.
  count = store.count_by_record(**filters)

  {
    name: args[:params]['collection_name'],
    content: { data: history.map { |record| serialize_record(record) }, meta: meta(args, filters, count) }
  }
end

#handle_state(args = {}) ⇒ Object

Record as it stood at timestamp: the current record with every later entry undone. data is null when the record did not exist yet (or not any more) at that instant. Shape matches the Node agent's handleStateAt — data and nothing else.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/forest_admin_agent/routes/resources/audit_trail.rb', line 66

def handle_state(args = {})
  context = build(args)
  context.permissions.can?(:read, context.collection)
  # Authorizes and reads in one query: the record it hands back is the one the scope covered.
  current = scoped_record(
    context, context.collection, args[:params]['id'], audited_projection(context.collection)
  )

  timestamp = parse_state_timestamp(args)
  entries = store.list_since(
    collection: context.collection.name,
    record_id: record_segments(context.collection, args[:params]['id']),
    timestamp: timestamp
  )
  # Fully qualified: inside this class, `AuditTrail` is the route itself.
  state = ::ForestAdminAgent::AuditTrail::RecordState.at(current, entries)

  { name: args[:params]['collection_name'], content: { data: state } }
end

#setup_routesObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/forest_admin_agent/routes/resources/audit_trail.rb', line 20

def setup_routes
  return self unless store

  add_route(
    'forest_audit_trail',
    'get',
    '/_audit-trail/:collection_name/:id',
    ->(args) { handle_request(args) }
  )
  add_route(
    'forest_audit_trail_state',
    'get',
    '/_audit-trail/:collection_name/:id/state',
    ->(args) { handle_state(args) }
  )

  self
end