Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit 669600b

Browse files
Brian Cunniereidmit
andcommitted
v3: Audit events support multiple relational operators
Previously, we would ignore any operators except for the first one in the list of parameters. Now, we support requests like: `GET /v3/audit_events?created_ats[lt]=2020-01-02T00:00:00Z&created_ats[gt]=2019-12-31T23:59:59Z` This request returns all audit events that occurred on New Year's Day. [finishes #173575979] Co-authored-by: Brian Cunnie <bcunnie@pivotal.io> Co-authored-by: Reid Mitchell <rmitchell@pivotal.io>
1 parent 7ab3ab9 commit 669600b

4 files changed

Lines changed: 124 additions & 15 deletions

File tree

app/fetchers/event_list_fetcher.rb

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,20 @@ def filter(message, dataset)
3030

3131
if message.requested?(:created_ats)
3232
if message.created_ats.is_a?(Hash)
33-
operator = message.created_ats.keys[0]
34-
given_timestamp = message.created_ats.values[0]
35-
36-
if operator == Event::LESS_THAN_COMPARATOR
37-
normalized_timestamp = Time.parse(given_timestamp).utc
38-
dataset = dataset.where(Sequel.lit('created_at < ?', normalized_timestamp))
39-
elsif operator == Event::LESS_THAN_OR_EQUAL_COMPARATOR
40-
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
41-
dataset = dataset.where(Sequel.lit('created_at <= ?', normalized_timestamp))
42-
elsif operator == Event::GREATER_THAN_COMPARATOR
43-
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
44-
dataset = dataset.where(Sequel.lit('created_at > ?', normalized_timestamp))
45-
elsif operator == Event::GREATER_THAN_OR_EQUAL_COMPARATOR
46-
normalized_timestamp = Time.parse(given_timestamp).utc
47-
dataset = dataset.where(Sequel.lit('created_at >= ?', normalized_timestamp))
33+
message.created_ats.map do |operator, given_timestamp|
34+
if operator == Event::LESS_THAN_COMPARATOR
35+
normalized_timestamp = Time.parse(given_timestamp).utc
36+
dataset = dataset.where(Sequel.lit('created_at < ?', normalized_timestamp))
37+
elsif operator == Event::LESS_THAN_OR_EQUAL_COMPARATOR
38+
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
39+
dataset = dataset.where(Sequel.lit('created_at <= ?', normalized_timestamp))
40+
elsif operator == Event::GREATER_THAN_COMPARATOR
41+
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
42+
dataset = dataset.where(Sequel.lit('created_at > ?', normalized_timestamp))
43+
elsif operator == Event::GREATER_THAN_OR_EQUAL_COMPARATOR
44+
normalized_timestamp = Time.parse(given_timestamp).utc
45+
dataset = dataset.where(Sequel.lit('created_at >= ?', normalized_timestamp))
46+
end
4847
end
4948
else
5049
# Gotcha: unlike the other relational operators, which are hashes such as

docs/v3/source/includes/concepts/_filters.md.erb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ the `created_at` field, using the `created_ats` filter.
5353
For example, a response to `/v3/audit_events?created_ats[lt]=2020-06-30T12:34:56Z` will contain
5454
audit events with a `created_at` timestamp strictly earlier than `2020-06-30T12:34:56Z`.
5555

56+
Multiple relational operators can be combined to further refine the listed resources. For example, a
57+
response to `/v3/audit_events?created_ats[lt]=2020-01-02T00:00:00Z&created_ats[gt]=2019-12-31T23:59:59Z`
58+
will return all audit events occurring on New Year's Day.
59+
5660
Timestamps must be in [standard timestamp format](#timestamps).
5761

5862
##### Valid inequality operators

spec/request/events_spec.rb

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,94 @@
307307
)
308308
end
309309
end
310+
context 'using greater than or equal to' do
311+
let!(:extra_event) { VCAP::CloudController::Event.make(created_at: timestamp, organization_guid: org.guid, type: 'audit.organization.create') }
312+
313+
let(:extra_event_json) do
314+
{
315+
guid: extra_event.guid,
316+
created_at: iso8601,
317+
updated_at: iso8601,
318+
type: 'audit.organization.create',
319+
actor: {
320+
guid: extra_event.actor,
321+
type: extra_event.actor_type,
322+
name: extra_event.actor_name
323+
},
324+
target: {
325+
guid: extra_event.actee,
326+
type: extra_event.actee_type,
327+
name: extra_event.actee_name
328+
},
329+
data: {},
330+
space: nil,
331+
organization: {
332+
guid: org.guid
333+
},
334+
links: {
335+
self: {
336+
href: "#{link_prefix}/v3/audit_events/#{extra_event.guid}"
337+
}
338+
}
339+
}
340+
end
341+
342+
it 'returns events at or after the given timestamp' do
343+
get "/v3/audit_events?created_ats[gte]=#{timestamp}", nil, admin_header
344+
345+
expect(
346+
resources: parsed_response['resources']
347+
).to match_json_response(
348+
resources: [org_scoped_event_json, extra_event_json]
349+
)
350+
end
351+
end
352+
353+
context 'using greater than and less than, together' do
354+
let!(:event_1) { VCAP::CloudController::Event.make(guid: '1', created_at: '2020-05-26T18:47:01Z') }
355+
let!(:event_2) { VCAP::CloudController::Event.make(guid: '2', created_at: '2020-05-26T18:47:02Z') }
356+
let!(:event_3) { VCAP::CloudController::Event.make(guid: '3', created_at: '2020-05-26T18:47:03Z') }
357+
let!(:event_4) { VCAP::CloudController::Event.make(guid: '4', created_at: '2020-05-26T18:47:04Z') }
358+
359+
let(:event_3_json) do
360+
{
361+
guid: event_3.guid,
362+
created_at: iso8601,
363+
updated_at: iso8601,
364+
type: event_3.type,
365+
actor: {
366+
guid: event_3.actor,
367+
type: event_3.actor_type,
368+
name: event_3.actor_name
369+
},
370+
target: {
371+
guid: event_3.actee,
372+
type: event_3.actee_type,
373+
name: event_3.actee_name
374+
},
375+
data: {},
376+
space: nil,
377+
organization: {
378+
guid: event_3.organization_guid
379+
},
380+
links: {
381+
self: {
382+
href: "#{link_prefix}/v3/audit_events/#{event_3.guid}"
383+
}
384+
}
385+
}
386+
end
387+
388+
it 'returns events after the greater-than timestamp but before the less-than timestamp' do
389+
get "/v3/audit_events?created_ats[gt]=#{event_2.created_at.iso8601}&created_ats[lt]=#{event_4.created_at.iso8601}", nil, admin_header
390+
391+
expect(
392+
resources: parsed_response['resources']
393+
).to match_json_response(
394+
resources: [event_3_json]
395+
)
396+
end
397+
end
310398

311399
context 'using equal' do
312400
let!(:same_time_event) { VCAP::CloudController::Event.make(created_at: timestamp, organization_guid: org.guid, type: 'audit.organization.create') }

spec/unit/fetchers/event_list_fetcher_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,24 @@ module VCAP::CloudController
146146
end
147147
end
148148

149+
context 'requesting events greater than one timestamp and less than another timestamp' do
150+
let(:filters) do
151+
{ created_ats: { gt: event_1.created_at.iso8601, lt: event_4.created_at.iso8601 } }
152+
end
153+
154+
it 'returns events with a created_at timestamp at or after a given timestamp' do
155+
expect(subject).to match_array([event_2, event_3])
156+
end
157+
158+
context 'when there are events with subsecond timestamps' do
159+
let!(:event_between_3_and_4) { Event.make(guid: '3.5', created_at: '2020-05-26T18:47:03.5Z') }
160+
161+
it 'returns events with a created_at timestamp before or at a given timestamp' do
162+
expect(subject).to match_array([event_2, event_3, event_between_3_and_4])
163+
end
164+
end
165+
end
166+
149167
context 'requesting events equal to a timestamp' do
150168
let(:filters) do
151169
{ created_ats: [event_3.created_at.iso8601] }

0 commit comments

Comments
 (0)