Inroduce Internal API for searching environment names

Add changelog

Rename word to query

User hash for limit

Do not allow control limit

Rename pluck names and add more specs
This commit is contained in:
Shinya Maeda 2019-02-05 16:14:30 +09:00
parent 591380a3f1
commit e8d9df83a6
6 changed files with 177 additions and 0 deletions

View file

@ -158,6 +158,16 @@ class Projects::EnvironmentsController < Projects::ApplicationController
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
def verify_api_request!
@ -181,6 +191,12 @@ class Projects::EnvironmentsController < Projects::ApplicationController
@environment ||= project.environments.find(params[:id])
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)
EnvironmentSerializer
.new(project: @project, current_user: @current_user)

View file

@ -50,6 +50,14 @@ class Environment < ActiveRecord::Base
end
scope :in_review_folder, -> { where(environment_type: "review") }
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 :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
end
end
def self.pluck_names
pluck(:name)
end
def predefined_variables
Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_ENVIRONMENT_NAME', value: name)

View file

@ -0,0 +1,5 @@
---
title: Introduce Internal API for searching environment names
merge_request: 24923
author:
type: added

View file

@ -224,6 +224,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
collection do
get :metrics, action: :metrics_redirect
get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ }
get :search
end
resources :deployments, only: [:index] do

View file

@ -422,6 +422,79 @@ describe Projects::EnvironmentsController do
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 = {})
opts.reverse_merge(namespace_id: project.namespace,
project_id: project,

View file

@ -41,6 +41,76 @@ describe Environment do
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
let(:store) { Gitlab::EtagCaching::Store.new }