Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions app/fetchers/droplet_list_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,19 @@ def droplet_dataset(eager_loaded_associations, dataset=DropletModel.dataset)
end

def filter(message, app, space_guids, dataset)
if message.requested?(:current) && app
dataset = dataset.extension(:null_dataset)
return dataset.nullify unless app.droplet

dataset = dataset.where(guid: app.droplet_guid)
if message.requested?(:current)
if app
dataset = dataset.extension(:null_dataset)
return dataset.nullify unless app.droplet

dataset = dataset.where(guid: app.droplet_guid)
else
dataset = dataset.select_all(DropletModel.table_name).
join_table(:inner, AppModel.table_name, { droplet_guid: Sequel[DropletModel.table_name][:guid] }, { table_alias: :apps_current })
end
end

dataset = dataset.where(app_guid: message.app_guids) if message.requested?(:app_guids)
dataset = dataset.where(Sequel[DropletModel.table_name][:app_guid] => message.app_guids) if message.requested?(:app_guids)

dataset = dataset.where(state: message.states) if message.requested?(:states)

Expand Down
16 changes: 8 additions & 8 deletions app/messages/droplets_list_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class DropletsListMessage < MetadataListMessage
validates :states, array: true, allow_nil: true
validates :space_guids, array: true, allow_nil: true
validates :organization_guids, array: true, allow_nil: true
validates :current, inclusion: { in: ['true'], message: 'only accepts the value \'true\'' }, allow_nil: true, if: -> { app_guid.present? }
validates :current, inclusion: { in: ['true'], message: 'only accepts the value \'true\'' }, allow_nil: true
validate :app_nested_request, if: -> { app_guid.present? }
validate :not_app_nested_request, unless: -> { app_guid.present? }
validate :package_nested_request, if: -> { package_guid.present? }

def to_param_hash
super(exclude: %i[app_guid package_guid])
Expand All @@ -32,18 +32,18 @@ def self.from_params(params)

private

def not_app_nested_request
invalid_attributes = []
invalid_attributes << :current if current
errors.add(:base, "Unknown query parameter(s): '#{invalid_attributes.join("', '")}'") if invalid_attributes.present?
end

def app_nested_request
invalid_attributes = []
invalid_attributes << :app_guids if app_guids
invalid_attributes << :organization_guids if organization_guids
invalid_attributes << :space_guids if space_guids
errors.add(:base, "Unknown query parameter(s): '#{invalid_attributes.join("', '")}'") if invalid_attributes.present?
end

def package_nested_request
invalid_attributes = []
invalid_attributes << :current if current
errors.add(:base, "Unknown query parameter(s): '#{invalid_attributes.join("', '")}'") if invalid_attributes.present?
end
end
end
6 changes: 6 additions & 0 deletions docs/openapi/apis/cf/latest/paths/Droplets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
type: string
description: |
Comma-delimited list of app GUIDs to filter by
- name: current
in: query
schema:
type: boolean
description: |
If true, only include the current droplet for each app
- name: space_guids
in: query
schema:
Expand Down
2 changes: 1 addition & 1 deletion docs/v3/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ gulp.task('checkV3docs', gulp.series('build', done => {

try {
checkPathAndExit('build', {
linksToSkip: ['http://localhost:8001/version/release-candidate'],
linksToSkip: ['http://localhost:8001/version/release-candidate', 'slack.com'],
recurse: true,
silent: true,
markdown: true,
Expand Down
2 changes: 1 addition & 1 deletion docs/v3/source/includes/introduction/_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ key features:
* Changing application source code without stopping the app via deployments

## Getting help
The CAPI team can most easily be reached on our [Slack channel](https://cloudfoundry.slack.com/messages/capi/) for
The CAPI team can most easily be reached on our [Slack channel](https://cloudfoundry.slack.com/archives/C07C04W4Q) for
questions and issues regarding the API. To report an issue with the docs or API, please feel free to file a GitHub
issue on our API repo, [cloud_controller_ng](https://github.com/cloudfoundry/cloud_controller_ng).

Expand Down
1 change: 1 addition & 0 deletions docs/v3/source/includes/resources/droplets/_list.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Name | Type | Description
**guids** | _list of strings_ | Comma-delimited list of droplet guids to filter by
**states** | _list of strings_ | Comma-delimited list of droplet states to filter by
**app_guids** | _list of strings_ | Comma-delimited list of app guids to filter by
**current** | _boolean_ | If true, only include the current droplet for each app
**space_guids** | _list of strings_ | Comma-delimited list of space guids to filter by
**organization_guids** | _list of strings_ | Comma-delimited list of organization guids to filter by
**page** | _integer_ | Page to display; valid values are integers >= 1
Expand Down
2 changes: 1 addition & 1 deletion docs/v3/source/includes/upgrade_guide/_header.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ This document is intended to help client authors upgrade from Cloud Foundry's V2
When moving to the V3 API, it is important to understand that the V3 API is backed by the same database as the V2 API. Though resources may be presented differently and have different interaction patterns, the internal state of CF is the same across both APIs. If you create an organization using the V3 API,
it will be visible to the V2 API, and vice-versa.

If you have questions, need help, or want to chat about the upgrade process, please reach out to us in [Cloud Foundry Slack](https://cloudfoundry.slack.com/messages/C07C04W4Q).
If you have questions, need help, or want to chat about the upgrade process, please reach out to us in [Cloud Foundry Slack](https://cloudfoundry.slack.com/archives/C07C04W4Q).


46 changes: 45 additions & 1 deletion spec/request/droplets_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,7 @@
space_guids
app_guids
organization_guids
current
]
end
let(:params) do
Expand All @@ -668,7 +669,6 @@
order_by: 'updated_at',
guids: 'foo,bar',
app_guid: app_model.guid,
current: true,
package_guid: package_model.guid,
states: %w[test foo],
label_selector: 'foo,bar',
Expand Down Expand Up @@ -941,6 +941,50 @@
returned_guids = parsed_response['resources'].pluck('guid')
expect(returned_guids).to contain_exactly(droplet1.guid, droplet2.guid, droplet3.guid)
end

it 'filters by current=true as admin' do
current_droplet_app1 = create(:droplet_model, app: app_model, state: VCAP::CloudController::DropletModel::STAGED_STATE)
current_droplet_app2 = create(:droplet_model, app: app_model2, state: VCAP::CloudController::DropletModel::STAGED_STATE)
current_droplet_app3 = create(:droplet_model, app: app_model3, state: VCAP::CloudController::DropletModel::STAGED_STATE)
app_model.update(droplet: current_droplet_app1)
app_model2.update(droplet: current_droplet_app2)
app_model3.update(droplet: current_droplet_app3)

get '/v3/droplets?current=true', nil, admin_headers

expect(last_response.status).to eq(200)
returned_guids = parsed_response['resources'].pluck('guid')
expect(returned_guids).to contain_exactly(current_droplet_app1.guid, current_droplet_app2.guid, current_droplet_app3.guid)
end

it 'filters by current=true as a non-admin developer, returning only current droplets in readable spaces' do
current_droplet_app1 = create(:droplet_model, app: app_model, state: VCAP::CloudController::DropletModel::STAGED_STATE)
current_droplet_app2 = create(:droplet_model, app: app_model2, state: VCAP::CloudController::DropletModel::STAGED_STATE)
current_droplet_app3 = create(:droplet_model, app: app_model3, state: VCAP::CloudController::DropletModel::STAGED_STATE)
app_model.update(droplet: current_droplet_app1)
app_model2.update(droplet: current_droplet_app2)
app_model3.update(droplet: current_droplet_app3)

get '/v3/droplets?current=true', nil, developer_headers

expect(last_response.status).to eq(200)
returned_guids = parsed_response['resources'].pluck('guid')
expect(returned_guids).to contain_exactly(current_droplet_app1.guid, current_droplet_app2.guid)
expect(returned_guids).not_to include(current_droplet_app3.guid)
end

it 'filters by current=true combined with app_guids' do
current_droplet_app1 = create(:droplet_model, app: app_model, state: VCAP::CloudController::DropletModel::STAGED_STATE)
current_droplet_app2 = create(:droplet_model, app: app_model2, state: VCAP::CloudController::DropletModel::STAGED_STATE)
app_model.update(droplet: current_droplet_app1)
app_model2.update(droplet: current_droplet_app2)

get "/v3/droplets?current=true&app_guids=#{app_model.guid}", nil, developer_headers

expect(last_response.status).to eq(200)
returned_guids = parsed_response['resources'].pluck('guid')
expect(returned_guids).to contain_exactly(current_droplet_app1.guid)
end
end

context 'label_selector' do
Expand Down
70 changes: 70 additions & 0 deletions spec/unit/fetchers/droplet_list_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,76 @@ module VCAP::CloudController
expect(results).to contain_exactly(staged_droplet_for_app1)
end
end

context 'filtering by current=true' do
let(:filters) { { current: 'true' } }

context 'when some apps have a current droplet set' do
before do
app1.update(droplet: staged_droplet_for_app1)
app2.update(droplet: staged_droplet_for_app2)
end

it 'returns only current droplets' do
results = fetcher.fetch_all(message).all
expect(results).to contain_exactly(staged_droplet_for_app1, staged_droplet_for_app2)
end

it 'does not return non-current droplets' do
results = fetcher.fetch_all(message).all
expect(results).not_to include(failed_droplet_for_app1)
end
end

context 'when no apps have a current droplet set' do
it 'returns an empty list' do
results = fetcher.fetch_all(message).all
expect(results).to be_empty
end
end

context 'when combined with app_guids filter' do
before do
app1.update(droplet: staged_droplet_for_app1)
app2.update(droplet: staged_droplet_for_app2)
end

let(:filters) { { current: 'true', app_guids: [app1.guid] } }

it 'returns only the current droplet for the specified app' do
results = fetcher.fetch_all(message).all
expect(results).to contain_exactly(staged_droplet_for_app1)
end
end

context 'when combined with space_guids filter' do
before do
app1.update(droplet: staged_droplet_for_app1)
app2.update(droplet: staged_droplet_for_app2)
end

let(:filters) { { current: 'true', space_guids: [app1.space.guid] } }

it 'returns only current droplets in the specified space' do
results = fetcher.fetch_all(message).all
expect(results).to contain_exactly(staged_droplet_for_app1)
end
end

context 'when combined with organization_guids filter' do
before do
app1.update(droplet: staged_droplet_for_app1)
app2.update(droplet: staged_droplet_for_app2)
end

let(:filters) { { current: 'true', organization_guids: [app1.organization.guid] } }

it 'returns only current droplets in the specified organization' do
results = fetcher.fetch_all(message).all
expect(results).to contain_exactly(staged_droplet_for_app1)
end
end
end
end

describe '#fetch_for_spaces' do
Expand Down
17 changes: 16 additions & 1 deletion spec/unit/messages/droplets_list_message_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,23 @@ module VCAP::CloudController

context 'when the query is not nested under an app' do
context 'when the request contains current field' do
it 'is invalid' do
it 'is valid' do
message = DropletsListMessage.from_params({ current: 'true' })
expect(message).to be_valid
end

it 'validates current must be true' do
message = DropletsListMessage.from_params({ current: 'false' })
expect(message).not_to be_valid
expect(message.errors[:current]).to include("only accepts the value 'true'")
end
end
end

context 'when the query is nested under a package' do
context 'when the request contains current field' do
it 'is invalid' do
message = DropletsListMessage.from_params({ package_guid: 'some-package-guid', current: 'true' })
expect(message).not_to be_valid
expect(message.errors[:base][0]).to include("Unknown query parameter(s): 'current'")
end
Expand Down
Loading