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

Commit 55dec6c

Browse files
monamohebbisethboylessweinstein22Harsha Nandiwada
committed
**V3** client can filter **feature flags** by **updated_ats**
* client will receive a user friendly error for trying to filter on **created_ats** because this resource does not surface created_at in the endpoint. [finishes #173720279] Co-authored-by: Mona Mohebbi <mmohebbi@pivotal.io> Co-authored-by: Seth Boyles <sboyles@pivotal.io> Co-authored-by: Sarah Weinstein <sweinstein@pivotal.io> Co-authored-by: Harsha Nandiwada <hnandiwada@vmware.com>
1 parent 071bc07 commit 55dec6c

7 files changed

Lines changed: 118 additions & 5 deletions

File tree

app/controllers/v3/feature_flags_controller.rb

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@
33
require 'messages/feature_flags_update_message'
44
require 'actions/feature_flag_update'
55
require 'cloud_controller/paging/list_paginator'
6+
require 'fetchers/feature_flag_list_fetcher'
67

78
class FeatureFlagsController < ApplicationController
89
def index
910
message = FeatureFlagsListMessage.from_params(query_params)
1011
invalid_param!(message.errors.full_messages) unless message.valid?
1112

12-
db_feature_flags = FeatureFlag.all
13-
dataset = FeatureFlag::DEFAULT_FLAGS.collect do |feature_flag_name, default_enabled_state|
14-
db_flag = db_feature_flags.find { |feature_flag| feature_flag.name == feature_flag_name.to_s }
15-
db_flag || FeatureFlag.new(name: feature_flag_name, enabled: default_enabled_state)
16-
end
13+
dataset = FeatureFlagListFetcher.fetch_all(message)
1714

1815
render status: :ok, json: Presenters::V3::PaginatedListPresenter.new(
1916
presenter: Presenters::V3::FeatureFlagPresenter,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
require 'cloud_controller/paging/sequel_paginator'
2+
require 'cloud_controller/paging/paginated_result'
3+
require 'fetchers/label_selector_query_generator'
4+
require 'fetchers/base_list_fetcher'
5+
6+
module VCAP::CloudController
7+
class FeatureFlagListFetcher < BaseListFetcher
8+
class << self
9+
def fetch_all(message, eager_loaded_associations: [])
10+
db_feature_flags = FeatureFlag.dataset
11+
if message.updated_ats
12+
db_feature_flags = filter(message, db_feature_flags).all
13+
else
14+
FeatureFlag::DEFAULT_FLAGS.collect do |feature_flag_name, default_enabled_state|
15+
db_flag = db_feature_flags.find { |feature_flag| feature_flag.name == feature_flag_name.to_s }
16+
db_flag || FeatureFlag.new(name: feature_flag_name, enabled: default_enabled_state)
17+
end
18+
end
19+
end
20+
21+
private
22+
23+
def filter(message, dataset)
24+
super(message, dataset, FeatureFlag)
25+
end
26+
end
27+
end
28+
end

app/messages/base_message.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,14 @@ def validate(record)
115115
end
116116
end
117117

118+
class DisallowCreatedAtsParamValidator < ActiveModel::Validator
119+
def validate(record)
120+
if record.requested?(:created_ats)
121+
record.errors[:base] << "Filtering by 'created_ats' is not allowed on this resource."
122+
end
123+
end
124+
end
125+
118126
class IncludeParamValidator < ActiveModel::Validator
119127
def validate(record)
120128
if record.requested?(:include)

app/messages/feature_flags_list_message.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module VCAP::CloudController
44
class FeatureFlagsListMessage < ListMessage
55
validates_with NoAdditionalParamsValidator
6+
validates_with DisallowCreatedAtsParamValidator
67

78
def self.from_params(params)
89
super(params, [])

spec/request/feature_flags_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,39 @@
1414
flag_names_in_response = parsed_response['resources'].map { |flag| flag['name'] }
1515
expect(flag_names_in_response).to eq(flag_names_sorted.map(&:to_s))
1616
end
17+
18+
context 'filtering timestamps on update' do
19+
# before must occur before the let! otherwise the resources will be created with
20+
# update_on_create: true
21+
before do
22+
VCAP::CloudController::FeatureFlag.plugin :timestamps, update_on_create: false
23+
end
24+
25+
let!(:resource_1) { VCAP::CloudController::FeatureFlag.make(name: 'set_roles_by_username', updated_at: '2020-05-26T18:47:01Z') }
26+
let!(:resource_2) { VCAP::CloudController::FeatureFlag.make(name: 'task_creation', updated_at: '2020-05-26T18:47:02Z') }
27+
let!(:resource_3) { VCAP::CloudController::FeatureFlag.make(name: 'user_org_creation', updated_at: '2020-05-26T18:47:03Z') }
28+
let!(:resource_4) { VCAP::CloudController::FeatureFlag.make(name: 'unset_roles_by_username', updated_at: '2020-05-26T18:47:04Z') }
29+
30+
after do
31+
VCAP::CloudController::FeatureFlag.plugin :timestamps, update_on_create: true
32+
end
33+
34+
it 'filters' do
35+
get '/v3/feature_flags?updated_ats[gt]=2020-05-26T18:47:02Z', nil, admin_headers
36+
37+
expect(last_response).to have_status_code(200)
38+
expect(parsed_response['resources'].map { |r| r['name'] }).to contain_exactly('user_org_creation', 'unset_roles_by_username')
39+
end
40+
end
41+
42+
context 'filtering timestamps on created_ats' do
43+
it 'filters' do
44+
get '/v3/feature_flags?created_ats[gt]=2020-05-26T18:47:04Z', nil, admin_headers
45+
46+
expect(last_response).to have_status_code(400)
47+
expect(last_response).to have_error_message("The query parameter is invalid: Filtering by 'created_ats' is not allowed on this resource.")
48+
end
49+
end
1750
end
1851

1952
describe 'GET /v3/feature_flags/:name' do
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require 'spec_helper'
2+
3+
module VCAP::CloudController
4+
RSpec.describe FeatureFlagListFetcher do
5+
subject(:fetcher) { FeatureFlagListFetcher }
6+
let(:message) { FeatureFlagsListMessage.from_params(filters) }
7+
describe '#fetch_all' do
8+
let(:filters) { {} }
9+
10+
before do
11+
VCAP::CloudController::FeatureFlag.plugin :timestamps, update_on_create: false
12+
end
13+
14+
let!(:resource_1) { VCAP::CloudController::FeatureFlag.make(name: 'set_roles_by_username', updated_at: '2020-05-26T18:47:01Z') }
15+
let!(:resource_2) { VCAP::CloudController::FeatureFlag.make(name: 'task_creation', updated_at: '2020-05-26T18:47:02Z') }
16+
let!(:resource_3) { VCAP::CloudController::FeatureFlag.make(name: 'user_org_creation', updated_at: '2020-05-26T18:47:03Z') }
17+
let!(:resource_4) { VCAP::CloudController::FeatureFlag.make(name: 'unset_roles_by_username', updated_at: '2020-05-26T18:47:04Z') }
18+
19+
after do
20+
VCAP::CloudController::FeatureFlag.plugin :timestamps, update_on_create: true
21+
end
22+
23+
context 'when not filtering' do
24+
it 'returns an array of all FeatureFlags' do
25+
flag_names = VCAP::CloudController::FeatureFlag::DEFAULT_FLAGS.keys.sort.map(&:to_s)
26+
expect(subject.fetch_all(message).map(&:name)).to contain_exactly(*flag_names)
27+
end
28+
end
29+
30+
context 'when filtering on updated_ats' do
31+
let(:filters) {
32+
{ updated_ats: { gt: '2020-05-26T18:47:02Z' } }
33+
}
34+
it 'it only returns records that have been updated in the time range' do
35+
expect(subject.fetch_all(message).map(&:name)).to contain_exactly('unset_roles_by_username', 'user_org_creation')
36+
end
37+
end
38+
end
39+
end
40+
end

spec/unit/messages/feature_flags_list_message_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ module VCAP::CloudController
6161
expect(message).to be_invalid
6262
expect(message.errors[:order_by].length).to eq 1
6363
end
64+
65+
it 'validates that created_ats is invalid' do
66+
message = FeatureFlagsListMessage.from_params({ created_ats: 'created_at' })
67+
expect(message).to be_invalid
68+
expect(message.errors[:base][0]).to eq "Filtering by 'created_ats' is not allowed on this resource."
69+
end
6470
end
6571
end
6672
end

0 commit comments

Comments
 (0)