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

Commit 497126d

Browse files
reidmitmonamohebbiselzoc
committed
Add created_ats/updated_ats filters to apps, processes, iso segs
Prior to this commit, the `created_ats` and `updated_ats` filters were only supported for v3 audit events. This change extends these filters to more v3 resources: apps, processes, and isolation segments. Most of this work is moving the shared message validation logic into `validators.rb` and the shared fetching logic into the new `BaseListFetcher`. Fetchers that inherit from `BaseListFetcher` must now have class methods, not instance methods, so this required some refactoring to the `AppListFetcher`, `IsolationSegmentListFetcher`, and `ProcessListFetcher`. [finishes #173693543] Co-authored-by: Reid Mitchell <rmitchell@pivotal.io> Co-authored-by: Mona Mohebbi <mmohebbi@pivotal.io> Co-authored-by: Chris Selzo <cselzo@pivotal.io>
1 parent 030a8aa commit 497126d

36 files changed

Lines changed: 924 additions & 717 deletions

app/controllers/v3/apps_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ def index
3939
invalid_param!(message.errors.full_messages) unless message.valid?
4040

4141
dataset = if permission_queryer.can_read_globally?
42-
AppListFetcher.new.fetch_all(message, eager_loaded_associations: Presenters::V3::AppPresenter.associated_resources)
42+
AppListFetcher.fetch_all(message, eager_loaded_associations: Presenters::V3::AppPresenter.associated_resources)
4343
else
44-
AppListFetcher.new.fetch(message, permission_queryer.readable_space_guids, eager_loaded_associations: Presenters::V3::AppPresenter.associated_resources)
44+
AppListFetcher.fetch(message, permission_queryer.readable_space_guids, eager_loaded_associations: Presenters::V3::AppPresenter.associated_resources)
4545
end
4646

4747
decorators = []

app/controllers/v3/isolation_segments_controller.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,10 @@ def index
4242
message = IsolationSegmentsListMessage.from_params(query_params)
4343
invalid_param!(message.errors.full_messages) unless message.valid?
4444

45-
fetcher = IsolationSegmentListFetcher.new(message: message)
46-
4745
dataset = if permission_queryer.can_read_globally?
48-
fetcher.fetch_all
46+
IsolationSegmentListFetcher.fetch_all(message)
4947
else
50-
fetcher.fetch_for_organizations(org_guids: permission_queryer.readable_org_guids)
48+
IsolationSegmentListFetcher.fetch_for_organizations(message, org_guids: permission_queryer.readable_org_guids)
5149
end
5250

5351
render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(

app/controllers/v3/processes_controller.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ def index
2525
invalid_param!(message.errors.full_messages) unless message.valid?
2626

2727
if app_nested?
28-
app, dataset = ProcessListFetcher.new(message).fetch_for_app(eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources)
28+
app, dataset = ProcessListFetcher.fetch_for_app(message, eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources)
2929
app_not_found! unless app && permission_queryer.can_read_from_space?(app.space.guid, app.organization.guid)
3030
else
3131
dataset = if permission_queryer.can_read_globally?
32-
ProcessListFetcher.new(message).fetch_all(eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources)
32+
ProcessListFetcher.fetch_all(message, eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources)
3333
else
34-
ProcessListFetcher.new(message).fetch_for_spaces(
34+
ProcessListFetcher.fetch_for_spaces(
35+
message,
3536
space_guids: permission_queryer.readable_space_guids,
3637
eager_loaded_associations: Presenters::V3::ProcessPresenter.associated_resources
3738
)

app/controllers/v3/stacks_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ def show_apps
6565
invalid_param!(message.errors.full_messages) unless message.valid?
6666

6767
dataset = if permission_queryer.can_read_globally?
68-
AppListFetcher.new.fetch_all(message)
68+
AppListFetcher.fetch_all(message)
6969
else
70-
AppListFetcher.new.fetch(message, permission_queryer.readable_space_guids)
70+
AppListFetcher.fetch(message, permission_queryer.readable_space_guids)
7171
end
7272

7373
render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(

app/fetchers/app_list_fetcher.rb

Lines changed: 65 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,85 @@
11
require 'cloud_controller/paging/sequel_paginator'
22
require 'cloud_controller/paging/paginated_result'
33
require 'fetchers/label_selector_query_generator'
4+
require 'fetchers/base_list_fetcher'
45

56
module VCAP::CloudController
6-
class AppListFetcher
7-
def fetch_all(message, eager_loaded_associations: [])
8-
dataset = AppModel.dataset.eager(eager_loaded_associations)
9-
filter(message, dataset)
10-
end
11-
12-
def fetch(message, space_guids, eager_loaded_associations: [])
13-
dataset = AppModel.where(space_guid: space_guids).eager(eager_loaded_associations)
14-
filter(message, dataset)
15-
end
16-
17-
private
18-
19-
def filter(message, dataset)
20-
if message.requested?(:names)
21-
dataset = dataset.where(name: message.names)
22-
end
23-
24-
if message.requested?(:space_guids)
25-
dataset = dataset.where(space_guid: message.space_guids)
7+
class AppListFetcher < BaseListFetcher
8+
class << self
9+
def fetch_all(message, eager_loaded_associations: [])
10+
dataset = AppModel.dataset.eager(eager_loaded_associations)
11+
filter(message, dataset)
2612
end
2713

28-
if message.requested?(:organization_guids)
29-
dataset = dataset.
30-
join(:spaces, guid: :space_guid).
31-
join(:organizations, id: :organization_id).
32-
where(Sequel[:organizations][:guid] => message.organization_guids).
33-
qualify(:apps)
14+
def fetch(message, space_guids, eager_loaded_associations: [])
15+
dataset = AppModel.where(space_guid: space_guids).eager(eager_loaded_associations)
16+
filter(message, dataset)
3417
end
3518

36-
if message.requested?(:stacks)
37-
buildpack_lifecycle_data_dataset = NullFilterQueryGenerator.add_filter(
38-
BuildpackLifecycleDataModel.dataset,
39-
:stack,
40-
message.stacks
41-
)
19+
private
4220

43-
dataset = dataset.where(guid: buildpack_lifecycle_data_dataset.map(&:app_guid))
44-
end
21+
def filter(message, dataset)
22+
if message.requested?(:names)
23+
dataset = dataset.where(name: message.names)
24+
end
4525

46-
if message.requested?(:guids)
47-
dataset = dataset.where(guid: message.guids).qualify
48-
end
26+
if message.requested?(:space_guids)
27+
dataset = dataset.where(space_guid: message.space_guids)
28+
end
4929

50-
if message.requested?(:label_selector)
51-
dataset = LabelSelectorQueryGenerator.add_selector_queries(
52-
label_klass: AppLabelModel,
53-
resource_dataset: dataset,
54-
requirements: message.requirements,
55-
resource_klass: AppModel,
56-
)
57-
end
30+
if message.requested?(:organization_guids)
31+
dataset = dataset.
32+
join(:spaces, guid: :space_guid).
33+
join(:organizations, id: :organization_id).
34+
where(Sequel[:organizations][:guid] => message.organization_guids).
35+
qualify(:apps)
36+
end
5837

59-
if message.requested?(:lifecycle_type)
60-
if message.lifecycle_type == BuildpackLifecycleDataModel::LIFECYCLE_TYPE
61-
dataset = dataset.where(
62-
guid: BuildpackLifecycleDataModel.
63-
select(:app_guid).
64-
where(Sequel.~(app_guid: nil)).
65-
map(&:app_guid)
38+
if message.requested?(:stacks)
39+
buildpack_lifecycle_data_dataset = NullFilterQueryGenerator.add_filter(
40+
BuildpackLifecycleDataModel.dataset,
41+
:stack,
42+
message.stacks
6643
)
67-
elsif message.lifecycle_type == DockerLifecycleDataModel::LIFECYCLE_TYPE
68-
dataset = dataset.exclude(
69-
guid: BuildpackLifecycleDataModel.
70-
select(:app_guid).
71-
where(Sequel.~(app_guid: nil)).
72-
map(&:app_guid)
44+
45+
dataset = dataset.where(guid: buildpack_lifecycle_data_dataset.map(&:app_guid))
46+
end
47+
48+
if message.requested?(:guids)
49+
dataset = dataset.where(guid: message.guids).qualify
50+
end
51+
52+
if message.requested?(:label_selector)
53+
dataset = LabelSelectorQueryGenerator.add_selector_queries(
54+
label_klass: AppLabelModel,
55+
resource_dataset: dataset,
56+
requirements: message.requirements,
57+
resource_klass: AppModel,
7358
)
7459
end
75-
end
7660

77-
dataset.eager(:processes)
61+
if message.requested?(:lifecycle_type)
62+
if message.lifecycle_type == BuildpackLifecycleDataModel::LIFECYCLE_TYPE
63+
dataset = dataset.where(
64+
guid: BuildpackLifecycleDataModel.
65+
select(:app_guid).
66+
where(Sequel.~(app_guid: nil)).
67+
map(&:app_guid)
68+
)
69+
elsif message.lifecycle_type == DockerLifecycleDataModel::LIFECYCLE_TYPE
70+
dataset = dataset.exclude(
71+
guid: BuildpackLifecycleDataModel.
72+
select(:app_guid).
73+
where(Sequel.~(app_guid: nil)).
74+
map(&:app_guid)
75+
)
76+
end
77+
end
78+
79+
dataset = super(message, dataset, AppModel)
80+
81+
dataset.eager(:processes)
82+
end
7883
end
7984
end
8085
end

app/fetchers/base_list_fetcher.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
require 'models/helpers/relational_operators'
2+
3+
module VCAP::CloudController
4+
class BaseListFetcher
5+
class << self
6+
def filter(message, dataset, klass)
7+
advanced_filters = {}
8+
advanced_filters['created_at'] = message.created_ats if message.requested?(:created_ats)
9+
advanced_filters['updated_at'] = message.updated_ats if message.requested?(:updated_ats)
10+
11+
advanced_filters.each do |filter, values|
12+
if values.is_a?(Hash)
13+
values.map do |operator, given_timestamp|
14+
if operator == RelationalOperators::LESS_THAN_COMPARATOR
15+
normalized_timestamp = Time.parse(given_timestamp).utc
16+
dataset = dataset.where { Sequel.qualify(klass.table_name, filter) < normalized_timestamp }
17+
elsif operator == RelationalOperators::LESS_THAN_OR_EQUAL_COMPARATOR
18+
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
19+
dataset = dataset.where { Sequel.qualify(klass.table_name, filter) <= normalized_timestamp }
20+
elsif operator == RelationalOperators::GREATER_THAN_COMPARATOR
21+
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
22+
dataset = dataset.where { Sequel.qualify(klass.table_name, filter) > normalized_timestamp }
23+
elsif operator == RelationalOperators::GREATER_THAN_OR_EQUAL_COMPARATOR
24+
normalized_timestamp = Time.parse(given_timestamp).utc
25+
dataset = dataset.where { Sequel.qualify(klass.table_name, filter) >= normalized_timestamp }
26+
end
27+
end
28+
else
29+
# Gotcha: unlike the other relational operators, which are hashes such as
30+
# { lt: '2020-06-30T12:34:56Z' }, the equals operator is simply an array, e.g.
31+
# [ '2020-06-30T12:34:56Z' ].
32+
# Gotcha: the equals operator returns all resources occurring within
33+
# the span of the second (e.g. "12:34:56.00-12:34:56.9999999"), for databases store
34+
# timestamps in sub-second accuracy (PostgreSQL stores in microseconds, for example)
35+
36+
bounds_expressions = values.map do |timestamp|
37+
lower_bound = Time.parse(timestamp).utc
38+
upper_bound = Time.at(lower_bound + 0.999999).utc
39+
40+
(Sequel.qualify(klass.table_name, filter) <= upper_bound) &
41+
(Sequel.qualify(klass.table_name, filter) >= lower_bound)
42+
end
43+
44+
dataset = dataset.where(Sequel.|(*bounds_expressions))
45+
end
46+
end
47+
dataset
48+
end
49+
end
50+
end
51+
end

app/fetchers/event_list_fetcher.rb

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
require 'cloud_controller/paging/sequel_paginator'
22
require 'cloud_controller/paging/paginated_result'
33
require 'fetchers/label_selector_query_generator'
4+
require 'fetchers/base_list_fetcher'
45

56
module VCAP::CloudController
6-
class EventListFetcher
7+
class EventListFetcher < BaseListFetcher
78
class << self
89
def fetch_all(message, event_dataset)
910
filter(message, event_dataset)
@@ -28,48 +29,7 @@ def filter(message, dataset)
2829
dataset = dataset.where(organization_guid: message.organization_guids)
2930
end
3031

31-
advanced_filters = {}
32-
advanced_filters['created_at'] = message.created_ats if message.requested?(:created_ats)
33-
advanced_filters['updated_at'] = message.updated_ats if message.requested?(:updated_ats)
34-
35-
advanced_filters.each do |filter, values|
36-
if values.is_a?(Hash)
37-
values.map do |operator, given_timestamp|
38-
if operator == Event::LESS_THAN_COMPARATOR
39-
normalized_timestamp = Time.parse(given_timestamp).utc
40-
dataset = dataset.where(Sequel.lit("#{filter} < ?", normalized_timestamp))
41-
elsif operator == Event::LESS_THAN_OR_EQUAL_COMPARATOR
42-
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
43-
dataset = dataset.where(Sequel.lit("#{filter} <= ?", normalized_timestamp))
44-
elsif operator == Event::GREATER_THAN_COMPARATOR
45-
normalized_timestamp = (Time.parse(given_timestamp).utc + 0.999999).utc
46-
dataset = dataset.where(Sequel.lit("#{filter} > ?", normalized_timestamp))
47-
elsif operator == Event::GREATER_THAN_OR_EQUAL_COMPARATOR
48-
normalized_timestamp = Time.parse(given_timestamp).utc
49-
dataset = dataset.where(Sequel.lit("#{filter} >= ?", normalized_timestamp))
50-
end
51-
end
52-
else
53-
# Gotcha: unlike the other relational operators, which are hashes such as
54-
# { lt: '2020-06-30T12:34:56Z' }, the equals operator is simply an array, e.g.
55-
# [ '2020-06-30T12:34:56Z' ].
56-
# Gotcha: the equals operator returns all resources occurring within
57-
# the span of the second (e.g. "12:34:56.00-12:34:56.9999999"), for databases store
58-
# timestamps in sub-second accuracy (PostgreSQL stores in microseconds, for example)
59-
sequel_query =
60-
(["#{filter} BETWEEN ? AND ?"] * values.size).join(' OR ')
61-
62-
times = values.map do |timestamp|
63-
lower_bound = Time.parse(timestamp).utc
64-
upper_bound = Time.at(lower_bound + 0.999999).utc
65-
[lower_bound, upper_bound]
66-
end.flatten
67-
68-
dataset = dataset.where(Sequel.lit(sequel_query, *times))
69-
end
70-
end
71-
72-
dataset
32+
super(message, dataset, Event)
7333
end
7434
end
7535
end

0 commit comments

Comments
 (0)