Commit 561ce0c0 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'introduce-environment-search-endpoint' into 'master'

Introduce Internal API for searching environment names

See merge request gitlab-org/gitlab-ce!24923
parents 880afe22 e8d9df83
...@@ -158,6 +158,16 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -158,6 +158,16 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end end
end end
def search
respond_to do |format|
format.json do
environment_names = search_environment_names
render json: environment_names, status: environment_names.any? ? :ok : :no_content
end
end
end
private private
def verify_api_request! def verify_api_request!
...@@ -181,6 +191,12 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -181,6 +191,12 @@ class Projects::EnvironmentsController < Projects::ApplicationController
@environment ||= project.environments.find(params[:id]) @environment ||= project.environments.find(params[:id])
end end
def search_environment_names
return [] unless params[:query]
project.environments.for_name_like(params[:query]).pluck_names
end
def serialize_environments(request, response, nested = false) def serialize_environments(request, response, nested = false)
EnvironmentSerializer EnvironmentSerializer
.new(project: @project, current_user: @current_user) .new(project: @project, current_user: @current_user)
......
...@@ -50,6 +50,14 @@ class Environment < ActiveRecord::Base ...@@ -50,6 +50,14 @@ class Environment < ActiveRecord::Base
end end
scope :in_review_folder, -> { where(environment_type: "review") } scope :in_review_folder, -> { where(environment_type: "review") }
scope :for_name, -> (name) { where(name: name) } scope :for_name, -> (name) { where(name: name) }
##
# Search environments which have names like the given query.
# Do not set a large limit unless you've confirmed that it works on gitlab.com scale.
scope :for_name_like, -> (query, limit: 5) do
where('name LIKE ?', "#{sanitize_sql_like(query)}%").limit(limit)
end
scope :for_project, -> (project) { where(project_id: project) } scope :for_project, -> (project) { where(project_id: project) }
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) } scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
...@@ -70,6 +78,10 @@ class Environment < ActiveRecord::Base ...@@ -70,6 +78,10 @@ class Environment < ActiveRecord::Base
end end
end end
def self.pluck_names
pluck(:name)
end
def predefined_variables def predefined_variables
Gitlab::Ci::Variables::Collection.new Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_ENVIRONMENT_NAME', value: name) .append(key: 'CI_ENVIRONMENT_NAME', value: name)
......
---
title: Introduce Internal API for searching environment names
merge_request: 24923
author:
type: added
...@@ -224,6 +224,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -224,6 +224,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
collection do collection do
get :metrics, action: :metrics_redirect get :metrics, action: :metrics_redirect
get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ } get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ }
get :search
end end
resources :deployments, only: [:index] do resources :deployments, only: [:index] do
......
...@@ -422,6 +422,79 @@ describe Projects::EnvironmentsController do ...@@ -422,6 +422,79 @@ describe Projects::EnvironmentsController do
end end
end end
describe 'GET #search' do
before do
create(:environment, name: 'staging', project: project)
create(:environment, name: 'review/patch-1', project: project)
create(:environment, name: 'review/patch-2', project: project)
end
let(:query) { 'pro' }
it 'responds with status code 200' do
get :search, params: environment_params(format: :json, query: query)
expect(response).to have_gitlab_http_status(:ok)
end
it 'returns matched results' do
get :search, params: environment_params(format: :json, query: query)
expect(json_response).to contain_exactly('production')
end
context 'when query is review' do
let(:query) { 'review' }
it 'returns matched results' do
get :search, params: environment_params(format: :json, query: query)
expect(json_response).to contain_exactly('review/patch-1', 'review/patch-2')
end
end
context 'when query is empty' do
let(:query) { '' }
it 'returns matched results' do
get :search, params: environment_params(format: :json, query: query)
expect(json_response)
.to contain_exactly('production', 'staging', 'review/patch-1', 'review/patch-2')
end
end
context 'when query is review/patch-3' do
let(:query) { 'review/patch-3' }
it 'responds with status code 204' do
get :search, params: environment_params(format: :json, query: query)
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'when query is partially matched in the middle of environment name' do
let(:query) { 'patch' }
it 'responds with status code 204' do
get :search, params: environment_params(format: :json, query: query)
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'when query contains a wildcard character' do
let(:query) { 'review%' }
it 'prevents wildcard injection' do
get :search, params: environment_params(format: :json, query: query)
expect(response).to have_gitlab_http_status(:no_content)
end
end
end
def environment_params(opts = {}) def environment_params(opts = {})
opts.reverse_merge(namespace_id: project.namespace, opts.reverse_merge(namespace_id: project.namespace,
project_id: project, project_id: project,
......
...@@ -41,6 +41,76 @@ describe Environment do ...@@ -41,6 +41,76 @@ describe Environment do
end end
end end
describe '.for_name_like' do
subject { project.environments.for_name_like(query, limit: limit) }
let!(:environment) { create(:environment, name: 'production', project: project) }
let(:query) { 'pro' }
let(:limit) { 5 }
it 'returns a found name' do
is_expected.to include(environment)
end
context 'when query is production' do
let(:query) { 'production' }
it 'returns a found name' do
is_expected.to include(environment)
end
end
context 'when query is productionA' do
let(:query) { 'productionA' }
it 'returns empty array' do
is_expected.to be_empty
end
end
context 'when query is empty' do
let(:query) { '' }
it 'returns a found name' do
is_expected.to include(environment)
end
end
context 'when query is nil' do
let(:query) { }
it 'raises an error' do
expect { subject }.to raise_error(NoMethodError)
end
end
context 'when query is partially matched in the middle of environment name' do
let(:query) { 'duction' }
it 'returns empty array' do
is_expected.to be_empty
end
end
context 'when query contains a wildcard character' do
let(:query) { 'produc%' }
it 'prevents wildcard injection' do
is_expected.to be_empty
end
end
end
describe '.pluck_names' do
subject { described_class.pluck_names }
let!(:environment) { create(:environment, name: 'production', project: project) }
it 'plucks names' do
is_expected.to eq(%w[production])
end
end
describe '#expire_etag_cache' do describe '#expire_etag_cache' do
let(:store) { Gitlab::EtagCaching::Store.new } let(:store) { Gitlab::EtagCaching::Store.new }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment