From 297dc70158f905fef4557d1ee6510bcf459a08a9 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 2 Feb 2017 15:04:02 +0100 Subject: Create MM team for GitLab group --- lib/mattermost/session.rb | 2 +- lib/mattermost/team.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index 377cb7b1021..d4b4ba97f8c 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -153,7 +153,7 @@ module Mattermost yield rescue HTTParty::Error => e raise Mattermost::ConnectionError.new(e.message) - rescue Errno::ECONNREFUSED + rescue Errno::ECONNREFUSED => e raise Mattermost::ConnectionError.new(e.message) end end diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb index 09dfd082b3a..9e80f7f8571 100644 --- a/lib/mattermost/team.rb +++ b/lib/mattermost/team.rb @@ -1,7 +1,12 @@ module Mattermost class Team < Client + # Returns **all** teams for an admin def all session_get('/api/v3/teams/all') end + + def create(params) + session_post('/api/v3/teams/create', body: params.to_json) + end end end -- cgit v1.2.3 From 1a0e54b32d43e2a3de88c20037bd167b1f8563d6 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Fri, 3 Feb 2017 11:29:56 +0100 Subject: Add tests for Mattermost team creation --- lib/mattermost/client.rb | 2 +- lib/mattermost/team.rb | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb index e55c0d6ac49..29b10e2afce 100644 --- a/lib/mattermost/client.rb +++ b/lib/mattermost/client.rb @@ -26,7 +26,7 @@ module Mattermost def session_get(path, options = {}) with_session do |session| - get(session, path, options) + get(session, path, options) end end diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb index 9e80f7f8571..d5648f7167f 100644 --- a/lib/mattermost/team.rb +++ b/lib/mattermost/team.rb @@ -5,8 +5,22 @@ module Mattermost session_get('/api/v3/teams/all') end - def create(params) - session_post('/api/v3/teams/create', body: params.to_json) + # Creates a team on the linked Mattermost instance, the team admin will be the + # `current_user` passed to the Mattermost::Client instance + def create(group, params) + session_post('/api/v3/teams/create', body: new_team_params(group, params).to_json) + end + + private + + MATTERMOST_TEAM_LENGTH_MAX = 59 + + def new_team_params(group, options) + { + name: group.path[0..MATTERMOST_TEAM_LENGTH_MAX], + display_name: group.name[0..MATTERMOST_TEAM_LENGTH_MAX], + type: group.public? ? 'O' : 'I' # Open vs Invite-only + }.merge(options) end end end -- cgit v1.2.3 From 444d71e043eb19979ec1b08504b2760910cb2a47 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Mon, 20 Feb 2017 13:41:50 +0100 Subject: Transactional mattermost team creation Before this commit, but still on this feature branch, the creation of mattermost teams where a background job. However, it was decided it was better that these happened as transaction so feedback could be displayed to the user. --- lib/mattermost/team.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb index d5648f7167f..52486fd1a54 100644 --- a/lib/mattermost/team.rb +++ b/lib/mattermost/team.rb @@ -7,20 +7,20 @@ module Mattermost # Creates a team on the linked Mattermost instance, the team admin will be the # `current_user` passed to the Mattermost::Client instance - def create(group, params) - session_post('/api/v3/teams/create', body: new_team_params(group, params).to_json) + def create(group) + session_post('/api/v3/teams/create', body: new_team_params(group).to_json) end private MATTERMOST_TEAM_LENGTH_MAX = 59 - def new_team_params(group, options) + def new_team_params(group) { name: group.path[0..MATTERMOST_TEAM_LENGTH_MAX], display_name: group.name[0..MATTERMOST_TEAM_LENGTH_MAX], type: group.public? ? 'O' : 'I' # Open vs Invite-only - }.merge(options) + } end end end -- cgit v1.2.3 From 7a7ec1ac0f742753d7acfacb7b7913ebbaf81558 Mon Sep 17 00:00:00 2001 From: Jon Keys Date: Tue, 28 Feb 2017 16:30:58 -0500 Subject: Add storage class configuration option for Amazon S3 remote backups --- lib/backup/manager.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 5cc164a6325..7b4476fa4db 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -51,7 +51,8 @@ module Backup if directory.files.create(key: tar_file, body: File.open(tar_file), public: false, multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, - encryption: Gitlab.config.backup.upload.encryption) + encryption: Gitlab.config.backup.upload.encryption, + storage_class: Gitlab.config.backup.upload.storage_class) $progress.puts "done".color(:green) else puts "uploading backup to #{remote_directory} failed".color(:red) -- cgit v1.2.3 From 61c9604721dbda53eb4a7111d16c1b19292f9766 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Tue, 7 Feb 2017 18:06:08 +0100 Subject: Add middleware for ETag caching with Redis --- lib/gitlab/etag_caching/middleware.rb | 66 +++++++++++++++++++++++++++++++++++ lib/gitlab/etag_caching/store.rb | 32 +++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 lib/gitlab/etag_caching/middleware.rb create mode 100644 lib/gitlab/etag_caching/store.rb (limited to 'lib') diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb new file mode 100644 index 00000000000..0f24f9bbfde --- /dev/null +++ b/lib/gitlab/etag_caching/middleware.rb @@ -0,0 +1,66 @@ +module Gitlab + module EtagCaching + class Middleware + RESERVED_WORDS = ProjectPathValidator::RESERVED.map { |word| "/#{word}/" }.join('|') + ROUTE_REGEXP = Regexp.union( + %r(^(?!.*(#{RESERVED_WORDS})).*/noteable/issue/\d+/notes\z) + ) + + def initialize(app) + @app = app + end + + def call(env) + return @app.call(env) unless enabled_for_current_route?(env) + Gitlab::Metrics.add_event(:etag_caching_middleware_used) + + etag, cached_value_present = get_etag(env) + if_none_match = env['HTTP_IF_NONE_MATCH'] + + if if_none_match == etag + Gitlab::Metrics.add_event(:etag_caching_cache_hit) + [304, { 'ETag' => etag }, ['']] + else + track_cache_miss(if_none_match, cached_value_present) + + status, headers, body = @app.call(env) + headers['ETag'] = etag + [status, headers, body] + end + end + + private + + def enabled_for_current_route?(env) + ROUTE_REGEXP.match(env['PATH_INFO']) + end + + def get_etag(env) + cache_key = env['PATH_INFO'] + store = Store.new + current_value = store.get(cache_key) + cached_value_present = current_value.present? + + unless cached_value_present + current_value = store.touch(cache_key, only_if_missing: true) + end + + [weak_etag_format(current_value), cached_value_present] + end + + def weak_etag_format(value) + %Q{W/"#{value}"} + end + + def track_cache_miss(if_none_match, cached_value_present) + if if_none_match.blank? + Gitlab::Metrics.add_event(:etag_caching_header_missing) + elsif !cached_value_present + Gitlab::Metrics.add_event(:etag_caching_key_not_found) + else + Gitlab::Metrics.add_event(:etag_caching_resource_changed) + end + end + end + end +end diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb new file mode 100644 index 00000000000..9532e432f78 --- /dev/null +++ b/lib/gitlab/etag_caching/store.rb @@ -0,0 +1,32 @@ +module Gitlab + module EtagCaching + class Store + EXPIRY_TIME = 10.minutes + REDIS_NAMESPACE = 'etag:'.freeze + + def get(key) + Gitlab::Redis.with { |redis| redis.get(redis_key(key)) } + end + + def touch(key, only_if_missing: false) + etag = generate_etag + + Gitlab::Redis.with do |redis| + redis.set(redis_key(key), etag, ex: EXPIRY_TIME, nx: only_if_missing) + end + + etag + end + + private + + def generate_etag + SecureRandom.hex + end + + def redis_key(key) + "#{REDIS_NAMESPACE}#{key}" + end + end + end +end -- cgit v1.2.3 From 6fd28ff24472b87802bf88d5306bbfebbf22f7d6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 1 Mar 2017 17:07:40 -0600 Subject: Use full group name in GFM group reference title --- lib/banzai/filter/user_reference_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb index c973897f420..849e1142841 100644 --- a/lib/banzai/filter/user_reference_filter.rb +++ b/lib/banzai/filter/user_reference_filter.rb @@ -133,7 +133,7 @@ module Banzai data = data_attribute(group: namespace.id) content = link_content || Group.reference_prefix + group - link_tag(url, data, content, namespace.name) + link_tag(url, data, content, namespace.full_name) end def link_to_user(user, namespace, link_content: nil) -- cgit v1.2.3 From fc287191451bb6aac80d759ab521b5834d27df43 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Thu, 16 Feb 2017 11:06:40 +0100 Subject: A VisibilityLevel also can be presented as string For the API, the VisibilityLevel will be exposed as String instead of Integer. So add the string values and method to translate a level integer to a string. --- lib/gitlab/visibility_level.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index b28708c34e1..a3047533ea9 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -35,6 +35,10 @@ module Gitlab class << self delegate :values, to: :options + def string_values + string_options.keys + end + def options { 'Private' => PRIVATE, @@ -43,6 +47,14 @@ module Gitlab } end + def string_options + { + 'private' => PRIVATE, + 'internal' => INTERNAL, + 'public' => PUBLIC + } + end + def highest_allowed_level restricted_levels = current_application_settings.restricted_visibility_levels @@ -82,6 +94,10 @@ module Gitlab level_name end + + def string_level(level) + string_options.key(level) + end end def private? -- cgit v1.2.3 From b2c2dfe545526a1857e9605761f6d256ef73e190 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Wed, 1 Mar 2017 16:16:29 +0100 Subject: Expose Project's & ProjectSnippet's VisibilityLevel as String Instead of exposing the VisibilityLevel as Integer, expose it as String `visibility` for Project and ProjectSnippet. Filter queries also accept the `visibility` as String instead of `visibility_level` as Integer. Also remove the `public` boolean. --- lib/api/entities.rb | 4 ++-- lib/api/helpers.rb | 8 ++++++++ lib/api/project_snippets.rb | 20 ++++++++------------ lib/api/projects.rb | 16 ++++++---------- 4 files changed, 24 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9dccaff369e..bcdd0573e57 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -69,9 +69,9 @@ module API class Project < Grape::Entity expose :id, :description, :default_branch, :tag_list - expose :public?, as: :public expose :archived?, as: :archived - expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url + expose :ssh_url_to_repo, :http_url_to_repo, :web_url + expose(:visibility) { |project, _options| Gitlab::VisibilityLevel.string_level(project.visibility_level) } expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :name, :name_with_namespace expose :path, :path_with_namespace diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 72d2b320077..7a39c640c5a 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -268,6 +268,14 @@ module API projects.reorder(params[:order_by] => params[:sort]) end + def map_visibility_level(attrs) + visibility = attrs.delete(:visibility) + if visibility + attrs[:visibility_level] = Gitlab::VisibilityLevel.string_options[visibility] + end + attrs + end + # file helpers def uploaded_file(field, uploads_path) diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 2a1cce73f3f..31919a6270d 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -50,15 +50,13 @@ module API requires :title, type: String, desc: 'The title of the snippet' requires :file_name, type: String, desc: 'The file name of the snippet' requires :code, type: String, desc: 'The content of the snippet' - requires :visibility_level, type: Integer, - values: [Gitlab::VisibilityLevel::PRIVATE, - Gitlab::VisibilityLevel::INTERNAL, - Gitlab::VisibilityLevel::PUBLIC], - desc: 'The visibility level of the snippet' + requires :visibility, type: String, + values: Gitlab::VisibilityLevel.string_values, + desc: 'The visibility of the snippet' end post ":id/snippets" do authorize! :create_project_snippet, user_project - snippet_params = declared_params.merge(request: request, api: true) + snippet_params = map_visibility_level(declared_params).merge(request: request, api: true) snippet_params[:content] = snippet_params.delete(:code) snippet = CreateSnippetService.new(user_project, current_user, snippet_params).execute @@ -80,11 +78,9 @@ module API optional :title, type: String, desc: 'The title of the snippet' optional :file_name, type: String, desc: 'The file name of the snippet' optional :code, type: String, desc: 'The content of the snippet' - optional :visibility_level, type: Integer, - values: [Gitlab::VisibilityLevel::PRIVATE, - Gitlab::VisibilityLevel::INTERNAL, - Gitlab::VisibilityLevel::PUBLIC], - desc: 'The visibility level of the snippet' + optional :visibility, type: String, + values: Gitlab::VisibilityLevel.string_values, + desc: 'The visibility of the snippet' at_least_one_of :title, :file_name, :code, :visibility_level end put ":id/snippets/:snippet_id" do @@ -93,7 +89,7 @@ module API authorize! :update_project_snippet, snippet - snippet_params = declared_params(include_missing: false) + snippet_params = map_visibility_level(declared_params(include_missing: false)) .merge(request: request, api: true) snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present? diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 996404e0e49..743dcac41f9 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -16,11 +16,7 @@ module API optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project' optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project' - optional :visibility_level, type: Integer, values: [ - Gitlab::VisibilityLevel::PRIVATE, - Gitlab::VisibilityLevel::INTERNAL, - Gitlab::VisibilityLevel::PUBLIC - ], desc: 'Create a public project. The same as visibility_level = 20.' + optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.' optional :public_builds, type: Boolean, desc: 'Perform public builds' optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed' @@ -48,7 +44,7 @@ module API params :filter_params do optional :archived, type: Boolean, default: false, desc: 'Limit by archived status' - optional :visibility, type: String, values: %w[public internal private], + optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'Limit by visibility' optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria' optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' @@ -101,7 +97,7 @@ module API use :create_params end post do - attrs = declared_params(include_missing: false) + attrs = map_visibility_level(declared_params(include_missing: false)) project = ::Projects::CreateService.new(current_user, attrs).execute if project.saved? @@ -130,7 +126,7 @@ module API user = User.find_by(id: params.delete(:user_id)) not_found!('User') unless user - attrs = declared_params(include_missing: false) + attrs = map_visibility_level(declared_params(include_missing: false)) project = ::Projects::CreateService.new(user, attrs).execute if project.saved? @@ -208,14 +204,14 @@ module API at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :shared_runners_enabled, :container_registry_enabled, - :lfs_enabled, :visibility_level, :public_builds, + :lfs_enabled, :visibility, :public_builds, :request_access_enabled, :only_allow_merge_if_pipeline_succeeds, :only_allow_merge_if_all_discussions_are_resolved, :path, :default_branch end put ':id' do authorize_admin_project - attrs = declared_params(include_missing: false) + attrs = map_visibility_level(declared_params(include_missing: false)) authorize! :rename_project, user_project if attrs[:name].present? authorize! :change_visibility_level, user_project if attrs[:visibility_level].present? -- cgit v1.2.3 From 52c4a7866ed010d8db67e5ca976d8c73d4084784 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 1 Mar 2017 20:34:29 +0100 Subject: Improve UX --- lib/mattermost/team.rb | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb index 52486fd1a54..69b0a3edf6f 100644 --- a/lib/mattermost/team.rb +++ b/lib/mattermost/team.rb @@ -7,20 +7,12 @@ module Mattermost # Creates a team on the linked Mattermost instance, the team admin will be the # `current_user` passed to the Mattermost::Client instance - def create(group) - session_post('/api/v3/teams/create', body: new_team_params(group).to_json) - end - - private - - MATTERMOST_TEAM_LENGTH_MAX = 59 - - def new_team_params(group) - { - name: group.path[0..MATTERMOST_TEAM_LENGTH_MAX], - display_name: group.name[0..MATTERMOST_TEAM_LENGTH_MAX], - type: group.public? ? 'O' : 'I' # Open vs Invite-only - } + def create(name:, display_name:, type:) + session_post('/api/v3/teams/create', body: { + name: name, + display_name: display_name, + type: type + }.to_json) end end end -- cgit v1.2.3 From f45cbc87015dd2e6369e758ca96132cb44c8983a Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Thu, 16 Feb 2017 14:56:14 +0100 Subject: Expose Group VisibilityLevel as String Instead of exposing the VisibilityLevel as Integer, expose it as String `visibility`. --- lib/api/entities.rb | 3 +- lib/api/groups.rb | 6 +- lib/api/v3/entities.rb | 24 ++++++++ lib/api/v3/groups.rb | 147 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 174 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index bcdd0573e57..fb067046fd2 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -132,7 +132,8 @@ module API end class Group < Grape::Entity - expose :id, :name, :path, :description, :visibility_level + expose :id, :name, :path, :description + expose(:visibility) { |group, _options| Gitlab::VisibilityLevel.string_level(group.visibility_level) } expose :lfs_enabled?, as: :lfs_enabled expose :avatar_url expose :web_url diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 9cffd6180ae..b862ff70b31 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -7,7 +7,7 @@ module API helpers do params :optional_params do optional :description, type: String, desc: 'The description of the group' - optional :visibility_level, type: Integer, desc: 'The visibility level of the group' + optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group' optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' end @@ -92,7 +92,7 @@ module API optional :name, type: String, desc: 'The name of the group' optional :path, type: String, desc: 'The path of the group' use :optional_params - at_least_one_of :name, :path, :description, :visibility_level, + at_least_one_of :name, :path, :description, :visibility, :lfs_enabled, :request_access_enabled end put ':id' do @@ -126,7 +126,7 @@ module API end params do optional :archived, type: Boolean, default: false, desc: 'Limit by archived status' - optional :visibility, type: String, values: %w[public internal private], + optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'Limit by visibility' optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria' optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at], diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 2a3dcb7f288..9c5a64a6c2f 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -125,6 +125,30 @@ module API Gitlab::UrlBuilder.build(merge_request) end end + + class Group < Grape::Entity + expose :id, :name, :path, :description, :visibility_level + expose :lfs_enabled?, as: :lfs_enabled + expose :avatar_url + expose :web_url + expose :request_access_enabled + expose :full_name, :full_path + expose :parent_id + + expose :statistics, if: :statistics do + with_options format_with: -> (value) { value.to_i } do + expose :storage_size + expose :repository_size + expose :lfs_objects_size + expose :build_artifacts_size + end + end + end + + class GroupDetail < Group + expose :projects, using: Entities::Project + expose :shared_projects, using: Entities::Project + end end end end diff --git a/lib/api/v3/groups.rb b/lib/api/v3/groups.rb index c826bc4fe0b..0aad87a3f58 100644 --- a/lib/api/v3/groups.rb +++ b/lib/api/v3/groups.rb @@ -6,13 +6,20 @@ module API before { authenticate! } helpers do + params :optional_params do + optional :description, type: String, desc: 'The description of the group' + optional :visibility_level, type: Integer, desc: 'The visibility level of the group' + optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' + optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' + end + params :statistics_params do optional :statistics, type: Boolean, default: false, desc: 'Include project statistics' end def present_groups(groups, options = {}) options = options.reverse_merge( - with: ::API::Entities::Group, + with: Entities::Group, current_user: current_user, ) @@ -22,8 +29,36 @@ module API end resource :groups do + desc 'Get a groups list' do + success Entities::Group + end + params do + use :statistics_params + optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list' + optional :all_available, type: Boolean, desc: 'Show all group that you have access to' + optional :search, type: String, desc: 'Search for a specific group' + optional :order_by, type: String, values: %w[name path], default: 'name', desc: 'Order by name or path' + optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' + use :pagination + end + get do + groups = if current_user.admin + Group.all + elsif params[:all_available] + GroupsFinder.new.execute(current_user) + else + current_user.groups + end + + groups = groups.search(params[:search]) if params[:search].present? + groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present? + groups = groups.reorder(params[:order_by] => params[:sort]) + + present_groups groups, statistics: params[:statistics] && current_user.is_admin? + end + desc 'Get list of owned groups for authenticated user' do - success ::API::Entities::Group + success Entities::Group end params do use :pagination @@ -32,6 +67,114 @@ module API get '/owned' do present_groups current_user.owned_groups, statistics: params[:statistics] end + + desc 'Create a group. Available only for users who can create groups.' do + success Entities::Group + end + params do + requires :name, type: String, desc: 'The name of the group' + requires :path, type: String, desc: 'The path of the group' + optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group' + use :optional_params + end + post do + authorize! :create_group + + group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute + + if group.persisted? + present group, with: Entities::Group, current_user: current_user + else + render_api_error!("Failed to save group #{group.errors.messages}", 400) + end + end + end + + params do + requires :id, type: String, desc: 'The ID of a group' + end + resource :groups do + desc 'Update a group. Available only for users who can administrate groups.' do + success Entities::Group + end + params do + optional :name, type: String, desc: 'The name of the group' + optional :path, type: String, desc: 'The path of the group' + use :optional_params + at_least_one_of :name, :path, :description, :visibility_level, + :lfs_enabled, :request_access_enabled + end + put ':id' do + group = find_group!(params[:id]) + authorize! :admin_group, group + + if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute + present group, with: Entities::GroupDetail, current_user: current_user + else + render_validation_error!(group) + end + end + + desc 'Get a single group, with containing projects.' do + success Entities::GroupDetail + end + get ":id" do + group = find_group!(params[:id]) + present group, with: Entities::GroupDetail, current_user: current_user + end + + desc 'Remove a group.' + delete ":id" do + group = find_group!(params[:id]) + authorize! :admin_group, group + present ::Groups::DestroyService.new(group, current_user).execute, with: Entities::GroupDetail, current_user: current_user + end + + desc 'Get a list of projects in this group.' do + success Entities::Project + end + params do + optional :archived, type: Boolean, default: false, desc: 'Limit by archived status' + optional :visibility, type: String, values: %w[public internal private], + desc: 'Limit by visibility' + optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria' + optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at], + default: 'created_at', desc: 'Return projects ordered by field' + optional :sort, type: String, values: %w[asc desc], default: 'desc', + desc: 'Return projects sorted in ascending and descending order' + optional :simple, type: Boolean, default: false, + desc: 'Return only the ID, URL, name, and path of each project' + optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' + optional :starred, type: Boolean, default: false, desc: 'Limit by starred status' + + use :pagination + end + get ":id/projects" do + group = find_group!(params[:id]) + projects = GroupProjectsFinder.new(group).execute(current_user) + projects = filter_projects(projects) + entity = params[:simple] ? ::API::Entities::BasicProjectDetails : Entities::Project + present paginate(projects), with: entity, current_user: current_user + end + + desc 'Transfer a project to the group namespace. Available only for admin.' do + success Entities::GroupDetail + end + params do + requires :project_id, type: String, desc: 'The ID or path of the project' + end + post ":id/projects/:project_id" do + authenticated_as_admin! + group = find_group!(params[:id]) + project = find_project!(params[:project_id]) + result = ::Projects::TransferService.new(project, current_user).execute(group) + + if result + present group, with: Entities::GroupDetail, current_user: current_user + else + render_api_error!("Failed to transfer project #{project.errors.messages}", 400) + end + end end end end -- cgit v1.2.3 From ed8f13c745da849c319eb8e9d8fd3e59bb804a08 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Thu, 16 Feb 2017 15:47:02 +0100 Subject: Ensure v3 environments endpoints remain unchanged Because environments also expose the project, ensure the projects are exposed as they were before in API v3. --- lib/api/v3/entities.rb | 4 +++ lib/api/v3/environments.rb | 62 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 9c5a64a6c2f..da5b58cab9c 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -149,6 +149,10 @@ module API expose :projects, using: Entities::Project expose :shared_projects, using: Entities::Project end + + class Environment < ::API::Entities::EnvironmentBasic + expose :project, using: Entities::Project + end end end end diff --git a/lib/api/v3/environments.rb b/lib/api/v3/environments.rb index 3effccfa708..3056b70e6ef 100644 --- a/lib/api/v3/environments.rb +++ b/lib/api/v3/environments.rb @@ -1,6 +1,7 @@ module API module V3 class Environments < Grape::API + include ::API::Helpers::CustomValidators include PaginationParams before { authenticate! } @@ -9,9 +10,66 @@ module API requires :id, type: String, desc: 'The project ID' end resource :projects do + desc 'Get all environments of the project' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Environment + end + params do + use :pagination + end + get ':id/environments' do + authorize! :read_environment, user_project + + present paginate(user_project.environments), with: Entities::Environment + end + + desc 'Creates a new environment' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Environment + end + params do + requires :name, type: String, desc: 'The name of the environment to be created' + optional :external_url, type: String, desc: 'URL on which this deployment is viewable' + optional :slug, absence: { message: "is automatically generated and cannot be changed" } + end + post ':id/environments' do + authorize! :create_environment, user_project + + environment = user_project.environments.create(declared_params) + + if environment.persisted? + present environment, with: Entities::Environment + else + render_validation_error!(environment) + end + end + + desc 'Updates an existing environment' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Environment + end + params do + requires :environment_id, type: Integer, desc: 'The environment ID' + optional :name, type: String, desc: 'The new environment name' + optional :external_url, type: String, desc: 'The new URL on which this deployment is viewable' + optional :slug, absence: { message: "is automatically generated and cannot be changed" } + end + put ':id/environments/:environment_id' do + authorize! :update_environment, user_project + + environment = user_project.environments.find(params[:environment_id]) + + update_params = declared_params(include_missing: false).extract!(:name, :external_url) + if environment.update(update_params) + present environment, with: Entities::Environment + else + render_validation_error!(environment) + end + end + desc 'Deletes an existing environment' do detail 'This feature was introduced in GitLab 8.11.' - success ::API::Entities::Environment + success Entities::Environment end params do requires :environment_id, type: Integer, desc: 'The environment ID' @@ -21,7 +79,7 @@ module API environment = user_project.environments.find(params[:environment_id]) - present environment.destroy, with: ::API::Entities::Environment + present environment.destroy, with: Entities::Environment end end end -- cgit v1.2.3 From 209856166e822aff46a7f9a5000896720e273265 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Thu, 16 Feb 2017 16:42:17 +0100 Subject: Expose Snippet VisibilityLevel as String --- lib/api/api.rb | 1 + lib/api/snippets.rb | 20 +++---- lib/api/v3/snippets.rb | 138 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 10 deletions(-) create mode 100644 lib/api/v3/snippets.rb (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index b27ac3f1d15..69d981c58b5 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -26,6 +26,7 @@ module API mount ::API::V3::Repositories mount ::API::V3::Runners mount ::API::V3::Services + mount ::API::V3::Snippets mount ::API::V3::Subscriptions mount ::API::V3::SystemHooks mount ::API::V3::Tags diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb index 0f86fdb3075..c5e55a5df4f 100644 --- a/lib/api/snippets.rb +++ b/lib/api/snippets.rb @@ -58,13 +58,13 @@ module API requires :title, type: String, desc: 'The title of a snippet' requires :file_name, type: String, desc: 'The name of a snippet file' requires :content, type: String, desc: 'The content of a snippet' - optional :visibility_level, type: Integer, - values: Gitlab::VisibilityLevel.values, - default: Gitlab::VisibilityLevel::INTERNAL, - desc: 'The visibility level of the snippet' + optional :visibility, type: String, + values: Gitlab::VisibilityLevel.string_values, + default: 'internal', + desc: 'The visibility of the snippet' end post do - attrs = declared_params(include_missing: false).merge(request: request, api: true) + attrs = map_visibility_level(declared_params(include_missing: false)).merge(request: request, api: true) snippet = CreateSnippetService.new(nil, current_user, attrs).execute render_spam_error! if snippet.spam? @@ -85,17 +85,17 @@ module API optional :title, type: String, desc: 'The title of a snippet' optional :file_name, type: String, desc: 'The name of a snippet file' optional :content, type: String, desc: 'The content of a snippet' - optional :visibility_level, type: Integer, - values: Gitlab::VisibilityLevel.values, - desc: 'The visibility level of the snippet' - at_least_one_of :title, :file_name, :content, :visibility_level + optional :visibility, type: String, + values: Gitlab::VisibilityLevel.string_values, + desc: 'The visibility of the snippet' + at_least_one_of :title, :file_name, :content, :visibility end put ':id' do snippet = snippets_for_current_user.find_by(id: params.delete(:id)) return not_found!('Snippet') unless snippet authorize! :update_personal_snippet, snippet - attrs = declared_params(include_missing: false).merge(request: request, api: true) + attrs = map_visibility_level(declared_params(include_missing: false).merge(request: request, api: true)) UpdateSnippetService.new(nil, current_user, snippet, attrs).execute diff --git a/lib/api/v3/snippets.rb b/lib/api/v3/snippets.rb new file mode 100644 index 00000000000..07dac7e9904 --- /dev/null +++ b/lib/api/v3/snippets.rb @@ -0,0 +1,138 @@ +module API + module V3 + class Snippets < Grape::API + include PaginationParams + + before { authenticate! } + + resource :snippets do + helpers do + def snippets_for_current_user + SnippetsFinder.new.execute(current_user, filter: :by_user, user: current_user) + end + + def public_snippets + SnippetsFinder.new.execute(current_user, filter: :public) + end + end + + desc 'Get a snippets list for authenticated user' do + detail 'This feature was introduced in GitLab 8.15.' + success ::API::Entities::PersonalSnippet + end + params do + use :pagination + end + get do + present paginate(snippets_for_current_user), with: ::API::Entities::PersonalSnippet + end + + desc 'List all public snippets current_user has access to' do + detail 'This feature was introduced in GitLab 8.15.' + success ::API::Entities::PersonalSnippet + end + params do + use :pagination + end + get 'public' do + present paginate(public_snippets), with: ::API::Entities::PersonalSnippet + end + + desc 'Get a single snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success ::API::Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + get ':id' do + snippet = snippets_for_current_user.find(params[:id]) + present snippet, with: ::API::Entities::PersonalSnippet + end + + desc 'Create new snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success ::API::Entities::PersonalSnippet + end + params do + requires :title, type: String, desc: 'The title of a snippet' + requires :file_name, type: String, desc: 'The name of a snippet file' + requires :content, type: String, desc: 'The content of a snippet' + optional :visibility_level, type: Integer, + values: Gitlab::VisibilityLevel.values, + default: Gitlab::VisibilityLevel::INTERNAL, + desc: 'The visibility level of the snippet' + end + post do + attrs = declared_params(include_missing: false).merge(request: request, api: true) + snippet = CreateSnippetService.new(nil, current_user, attrs).execute + + if snippet.persisted? + present snippet, with: ::API::Entities::PersonalSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Update an existing snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success ::API::Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + optional :title, type: String, desc: 'The title of a snippet' + optional :file_name, type: String, desc: 'The name of a snippet file' + optional :content, type: String, desc: 'The content of a snippet' + optional :visibility_level, type: Integer, + values: Gitlab::VisibilityLevel.values, + desc: 'The visibility level of the snippet' + at_least_one_of :title, :file_name, :content, :visibility_level + end + put ':id' do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + authorize! :update_personal_snippet, snippet + + attrs = declared_params(include_missing: false) + + UpdateSnippetService.new(nil, current_user, snippet, attrs).execute + if snippet.persisted? + present snippet, with: ::API::Entities::PersonalSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Remove snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success ::API::Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + delete ':id' do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + authorize! :destroy_personal_snippet, snippet + snippet.destroy + no_content! + end + + desc 'Get a raw snippet' do + detail 'This feature was introduced in GitLab 8.15.' + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + get ":id/raw" do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + + env['api.format'] = :txt + content_type 'text/plain' + present snippet.content + end + end + end + end +end -- cgit v1.2.3 From 260cc57838b9f7fa805fda339a5083b63209ba41 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Fri, 17 Feb 2017 14:50:02 +0100 Subject: Expose ApplicationSetting visibility settings as String Use strings for the ApplicationSetting properties: - restricted_visibility_levels - default_project_visibility - default_snippet_visibility - default_group_visibility --- lib/api/api.rb | 1 + lib/api/entities.rb | 10 ++-- lib/api/settings.rb | 24 +++++++-- lib/api/v3/entities.rb | 33 ++++++++++++ lib/api/v3/settings.rb | 137 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 lib/api/v3/settings.rb (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index 69d981c58b5..02f7bc2fbbf 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -26,6 +26,7 @@ module API mount ::API::V3::Repositories mount ::API::V3::Runners mount ::API::V3::Services + mount ::API::V3::Settings mount ::API::V3::Snippets mount ::API::V3::Subscriptions mount ::API::V3::SystemHooks diff --git a/lib/api/entities.rb b/lib/api/entities.rb index fb067046fd2..ca18eec2f39 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -553,12 +553,14 @@ module API expose :updated_at expose :home_page_url expose :default_branch_protection - expose :restricted_visibility_levels + expose(:restricted_visibility_levels) do |setting, _options| + setting.restricted_visibility_levels.map { |level| Gitlab::VisibilityLevel.string_level(level) } + end expose :max_attachment_size expose :session_expire_delay - expose :default_project_visibility - expose :default_snippet_visibility - expose :default_group_visibility + expose(:default_project_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_project_visibility) } + expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) } + expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) } expose :default_artifacts_expire_in expose :domain_whitelist expose :domain_blacklist_enabled diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 936c7e0930b..fef21f86f8c 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -7,6 +7,20 @@ module API @current_setting ||= (ApplicationSetting.current || ApplicationSetting.create_from_defaults) end + + def map_setting_visibility_levels(attrs) + [:default_project_visibility, :default_snippet_visibility, :default_group_visibility].each do |param| + visibility = attrs.delete(param) + if visibility + attrs[param] = Gitlab::VisibilityLevel.string_options[visibility] + end + end + restricted_levels = attrs.delete(:restricted_visibility_levels) + if restricted_levels + attrs[:restricted_visibility_levels] = Gitlab::VisibilityLevel.string_options.values_at(*restricted_levels) + end + attrs + end end desc 'Get the current application settings' do @@ -21,9 +35,9 @@ module API end params do optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master' - optional :default_project_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default project visibility' - optional :default_snippet_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default snippet visibility' - optional :default_group_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default group visibility' + optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility' + optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility' + optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility' optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.' optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project], desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com' @@ -128,7 +142,9 @@ module API :housekeeping_enabled, :terminal_max_session_time end put "application/settings" do - if current_settings.update_attributes(declared_params(include_missing: false)) + attrs = map_setting_visibility_levels(declared_params(include_missing: false)) + + if current_settings.update_attributes(attrs) present current_settings, with: Entities::ApplicationSetting else render_validation_error!(current_settings) diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index da5b58cab9c..270d99a2348 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -150,6 +150,39 @@ module API expose :shared_projects, using: Entities::Project end + class ApplicationSetting < Grape::Entity + expose :id + expose :default_projects_limit + expose :signup_enabled + expose :signin_enabled + expose :gravatar_enabled + expose :sign_in_text + expose :after_sign_up_text + expose :created_at + expose :updated_at + expose :home_page_url + expose :default_branch_protection + expose :restricted_visibility_levels + expose :max_attachment_size + expose :session_expire_delay + expose :default_project_visibility + expose :default_snippet_visibility + expose :default_group_visibility + expose :domain_whitelist + expose :domain_blacklist_enabled + expose :domain_blacklist + expose :user_oauth_applications + expose :after_sign_out_path + expose :container_registry_token_expire_delay + expose :repository_storage + expose :repository_storages + expose :koding_enabled + expose :koding_url + expose :plantuml_enabled + expose :plantuml_url + expose :terminal_max_session_time + end + class Environment < ::API::Entities::EnvironmentBasic expose :project, using: Entities::Project end diff --git a/lib/api/v3/settings.rb b/lib/api/v3/settings.rb new file mode 100644 index 00000000000..748d6b97d4f --- /dev/null +++ b/lib/api/v3/settings.rb @@ -0,0 +1,137 @@ +module API + module V3 + class Settings < Grape::API + before { authenticated_as_admin! } + + helpers do + def current_settings + @current_setting ||= + (ApplicationSetting.current || ApplicationSetting.create_from_defaults) + end + end + + desc 'Get the current application settings' do + success Entities::ApplicationSetting + end + get "application/settings" do + present current_settings, with: Entities::ApplicationSetting + end + + desc 'Modify application settings' do + success Entities::ApplicationSetting + end + params do + optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master' + optional :default_project_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default project visibility' + optional :default_snippet_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default snippet visibility' + optional :default_group_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default group visibility' + optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.' + optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project], + desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com' + optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources' + optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.' + optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled' + optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects' + optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB' + optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.' + optional :user_oauth_applications, type: Boolean, desc: 'Allow users to register any application to use GitLab as an OAuth provider' + optional :user_default_external, type: Boolean, desc: 'Newly registered users will by default be external' + optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled' + optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up' + optional :domain_whitelist, type: String, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com' + optional :domain_blacklist_enabled, type: Boolean, desc: 'Enable domain blacklist for sign ups' + given domain_blacklist_enabled: ->(val) { val } do + requires :domain_blacklist, type: String, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com' + end + optional :after_sign_up_text, type: String, desc: 'Text shown after sign up' + optional :signin_enabled, type: Boolean, desc: 'Flag indicating if sign in is enabled' + optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication' + given require_two_factor_authentication: ->(val) { val } do + requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication' + end + optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page' + optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out' + optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application' + optional :help_page_text, type: String, desc: 'Custom text displayed on the help page' + optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects' + given shared_runners_enabled: ->(val) { val } do + requires :shared_runners_text, type: String, desc: 'Shared runners text ' + end + optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have" + optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB' + optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)' + optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics' + given metrics_enabled: ->(val) { val } do + requires :metrics_host, type: String, desc: 'The InfluxDB host' + requires :metrics_port, type: Integer, desc: 'The UDP port to use for connecting to InfluxDB' + requires :metrics_pool_size, type: Integer, desc: 'The amount of InfluxDB connections to open' + requires :metrics_timeout, type: Integer, desc: 'The amount of seconds after which an InfluxDB connection will time out' + requires :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.' + requires :metrics_sample_interval, type: Integer, desc: 'The sampling interval in seconds' + requires :metrics_packet_size, type: Integer, desc: 'The amount of points to store in a single UDP packet' + end + optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling' + given sidekiq_throttling_enabled: ->(val) { val } do + requires :sidekiq_throttling_queus, type: Array[String], desc: 'Choose which queues you wish to throttle' + requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.' + end + optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts' + given recaptcha_enabled: ->(val) { val } do + requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha' + requires :recaptcha_private_key, type: String, desc: 'Generate private key at http://www.google.com/recaptcha' + end + optional :akismet_enabled, type: Boolean, desc: 'Helps prevent bots from creating issues' + given akismet_enabled: ->(val) { val } do + requires :akismet_api_key, type: String, desc: 'Generate API key at http://www.akismet.com' + end + optional :admin_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.' + optional :sentry_enabled, type: Boolean, desc: 'Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: https://getsentry.com' + given sentry_enabled: ->(val) { val } do + requires :sentry_dsn, type: String, desc: 'Sentry Data Source Name' + end + optional :repository_storage, type: String, desc: 'Storage paths for new projects' + optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues." + optional :koding_enabled, type: Boolean, desc: 'Enable Koding' + given koding_enabled: ->(val) { val } do + requires :koding_url, type: String, desc: 'The Koding team URL' + end + optional :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML' + given plantuml_enabled: ->(val) { val } do + requires :plantuml_url, type: String, desc: 'The PlantUML server URL' + end + optional :version_check_enabled, type: Boolean, desc: 'Let GitLab inform you when an update is available.' + optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.' + optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.' + optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)' + given housekeeping_enabled: ->(val) { val } do + requires :housekeeping_bitmaps_enabled, type: Boolean, desc: "Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance." + requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run." + requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run." + requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run." + end + optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.' + at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility, + :default_group_visibility, :restricted_visibility_levels, :import_sources, + :enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit, + :max_attachment_size, :session_expire_delay, :disabled_oauth_sign_in_sources, + :user_oauth_applications, :user_default_external, :signup_enabled, + :send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled, + :after_sign_up_text, :signin_enabled, :require_two_factor_authentication, + :home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text, + :shared_runners_enabled, :max_artifacts_size, :max_pages_size, :container_registry_token_expire_delay, + :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled, + :akismet_enabled, :admin_notification_email, :sentry_enabled, + :repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled, + :version_check_enabled, :email_author_in_body, :html_emails_enabled, + :housekeeping_enabled, :terminal_max_session_time + end + put "application/settings" do + if current_settings.update_attributes(declared_params(include_missing: false)) + present current_settings, with: Entities::ApplicationSetting + else + render_validation_error!(current_settings) + end + end + end + end +end -- cgit v1.2.3 From a3fdd6acd27f5aa98f13e7a0083d0c3208003ccb Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Wed, 1 Mar 2017 21:23:00 +0100 Subject: Use string based `visibility` getter & setter Add `visibility` & `visibility=` methods to the `Gitlab::VisibilityLevel` module so the `visibility_level` can be get/set with a string value. --- lib/api/entities.rb | 6 ++---- lib/api/helpers.rb | 8 -------- lib/api/project_snippets.rb | 4 ++-- lib/api/projects.rb | 8 ++++---- lib/api/snippets.rb | 4 ++-- lib/gitlab/visibility_level.rb | 23 ++++++++++++++++++++--- 6 files changed, 30 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index ca18eec2f39..d2d21f5d03a 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -70,8 +70,7 @@ module API class Project < Grape::Entity expose :id, :description, :default_branch, :tag_list expose :archived?, as: :archived - expose :ssh_url_to_repo, :http_url_to_repo, :web_url - expose(:visibility) { |project, _options| Gitlab::VisibilityLevel.string_level(project.visibility_level) } + expose :visibility, :ssh_url_to_repo, :http_url_to_repo, :web_url expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :name, :name_with_namespace expose :path, :path_with_namespace @@ -132,8 +131,7 @@ module API end class Group < Grape::Entity - expose :id, :name, :path, :description - expose(:visibility) { |group, _options| Gitlab::VisibilityLevel.string_level(group.visibility_level) } + expose :id, :name, :path, :description, :visibility expose :lfs_enabled?, as: :lfs_enabled expose :avatar_url expose :web_url diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7a39c640c5a..72d2b320077 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -268,14 +268,6 @@ module API projects.reorder(params[:order_by] => params[:sort]) end - def map_visibility_level(attrs) - visibility = attrs.delete(:visibility) - if visibility - attrs[:visibility_level] = Gitlab::VisibilityLevel.string_options[visibility] - end - attrs - end - # file helpers def uploaded_file(field, uploads_path) diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 31919a6270d..f57e7ea4032 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -56,7 +56,7 @@ module API end post ":id/snippets" do authorize! :create_project_snippet, user_project - snippet_params = map_visibility_level(declared_params).merge(request: request, api: true) + snippet_params = declared_params.merge(request: request, api: true) snippet_params[:content] = snippet_params.delete(:code) snippet = CreateSnippetService.new(user_project, current_user, snippet_params).execute @@ -89,7 +89,7 @@ module API authorize! :update_project_snippet, snippet - snippet_params = map_visibility_level(declared_params(include_missing: false)) + snippet_params = declared_params(include_missing: false) .merge(request: request, api: true) snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present? diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 743dcac41f9..f302496c12b 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -97,7 +97,7 @@ module API use :create_params end post do - attrs = map_visibility_level(declared_params(include_missing: false)) + attrs = declared_params(include_missing: false) project = ::Projects::CreateService.new(current_user, attrs).execute if project.saved? @@ -126,7 +126,7 @@ module API user = User.find_by(id: params.delete(:user_id)) not_found!('User') unless user - attrs = map_visibility_level(declared_params(include_missing: false)) + attrs = declared_params(include_missing: false) project = ::Projects::CreateService.new(user, attrs).execute if project.saved? @@ -211,9 +211,9 @@ module API end put ':id' do authorize_admin_project - attrs = map_visibility_level(declared_params(include_missing: false)) + attrs = declared_params(include_missing: false) authorize! :rename_project, user_project if attrs[:name].present? - authorize! :change_visibility_level, user_project if attrs[:visibility_level].present? + authorize! :change_visibility_level, user_project if attrs[:visibility].present? result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb index c5e55a5df4f..b93fdc62808 100644 --- a/lib/api/snippets.rb +++ b/lib/api/snippets.rb @@ -64,7 +64,7 @@ module API desc: 'The visibility of the snippet' end post do - attrs = map_visibility_level(declared_params(include_missing: false)).merge(request: request, api: true) + attrs = declared_params(include_missing: false).merge(request: request, api: true) snippet = CreateSnippetService.new(nil, current_user, attrs).execute render_spam_error! if snippet.spam? @@ -95,7 +95,7 @@ module API return not_found!('Snippet') unless snippet authorize! :update_personal_snippet, snippet - attrs = map_visibility_level(declared_params(include_missing: false).merge(request: request, api: true)) + attrs = declared_params(include_missing: false).merge(request: request, api: true) UpdateSnippetService.new(nil, current_user, snippet, attrs).execute diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index a3047533ea9..2248763c106 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -95,21 +95,38 @@ module Gitlab level_name end + def level_value(level) + return string_options[level] if level.is_a? String + level + end + def string_level(level) string_options.key(level) end end def private? - visibility_level_field == PRIVATE + visibility_level_value == PRIVATE end def internal? - visibility_level_field == INTERNAL + visibility_level_value == INTERNAL end def public? - visibility_level_field == PUBLIC + visibility_level_value == PUBLIC + end + + def visibility_level_value + self[visibility_level_field] + end + + def visibility + Gitlab::VisibilityLevel.string_level(visibility_level_value) + end + + def visibility=(level) + self[visibility_level_field] = Gitlab::VisibilityLevel.level_value(level) end end end -- cgit v1.2.3 From 17ee1e1a63520f88663697608920e816aa7128c4 Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Thu, 9 Feb 2017 15:28:19 +0100 Subject: Use iids as filter parameter --- lib/api/api.rb | 1 + lib/api/milestones.rb | 4 ++-- lib/api/v3/milestones.rb | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 lib/api/v3/milestones.rb (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index b27ac3f1d15..d4ee6e7a267 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -21,6 +21,7 @@ module API mount ::API::V3::MergeRequests mount ::API::V3::Notes mount ::API::V3::ProjectHooks + mount ::API::V3::Milestones mount ::API::V3::Projects mount ::API::V3::ProjectSnippets mount ::API::V3::Repositories diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index 44bdaea7fa4..bd74174c655 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -30,7 +30,7 @@ module API params do optional :state, type: String, values: %w[active closed all], default: 'all', desc: 'Return "active", "closed", or "all" milestones' - optional :iid, type: Array[Integer], desc: 'The IID of the milestone' + optional :iids, type: Array[Integer], desc: 'The IIDs of the milestones' optional :search, type: String, desc: 'The search criteria for the title or description of the milestone' use :pagination end @@ -39,7 +39,7 @@ module API milestones = user_project.milestones milestones = filter_milestones_state(milestones, params[:state]) - milestones = filter_by_iid(milestones, params[:iid]) if params[:iid].present? + milestones = filter_by_iid(milestones, params[:iids]) if params[:iids].present? milestones = filter_by_search(milestones, params[:search]) if params[:search] present paginate(milestones), with: Entities::Milestone diff --git a/lib/api/v3/milestones.rb b/lib/api/v3/milestones.rb new file mode 100644 index 00000000000..bbc29c40ee2 --- /dev/null +++ b/lib/api/v3/milestones.rb @@ -0,0 +1,43 @@ +module API + module V3 + class Milestones < Grape::API + include PaginationParams + + before { authenticate! } + + helpers do + def filter_milestones_state(milestones, state) + case state + when 'active' then milestones.active + when 'closed' then milestones.closed + else milestones + end + end + end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + desc 'Get a list of project milestones' do + success ::API::Entities::Milestone + end + params do + optional :state, type: String, values: %w[active closed all], default: 'all', + desc: 'Return "active", "closed", or "all" milestones' + optional :iid, type: Array[Integer], desc: 'The IID of the milestone' + use :pagination + end + get ":id/milestones" do + authorize! :read_milestone, user_project + + milestones = user_project.milestones + milestones = filter_milestones_state(milestones, params[:state]) + milestones = filter_by_iid(milestones, params[:iid]) if params[:iid].present? + + present paginate(milestones), with: ::API::Entities::Milestone + end + end + end + end +end -- cgit v1.2.3 From c3b1cb71f0726bd9cd3916507337650e6546141f Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Thu, 2 Mar 2017 09:22:44 +0100 Subject: Override setters so it also accepts string visibility levels Override the `ApplicationSetting` default visibility_level setters so they accept strings & integers for the levels. --- lib/api/settings.rb | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/api/settings.rb b/lib/api/settings.rb index fef21f86f8c..d4d3229f0d1 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -7,20 +7,6 @@ module API @current_setting ||= (ApplicationSetting.current || ApplicationSetting.create_from_defaults) end - - def map_setting_visibility_levels(attrs) - [:default_project_visibility, :default_snippet_visibility, :default_group_visibility].each do |param| - visibility = attrs.delete(param) - if visibility - attrs[param] = Gitlab::VisibilityLevel.string_options[visibility] - end - end - restricted_levels = attrs.delete(:restricted_visibility_levels) - if restricted_levels - attrs[:restricted_visibility_levels] = Gitlab::VisibilityLevel.string_options.values_at(*restricted_levels) - end - attrs - end end desc 'Get the current application settings' do @@ -142,7 +128,7 @@ module API :housekeeping_enabled, :terminal_max_session_time end put "application/settings" do - attrs = map_setting_visibility_levels(declared_params(include_missing: false)) + attrs = declared_params(include_missing: false) if current_settings.update_attributes(attrs) present current_settings, with: Entities::ApplicationSetting -- cgit v1.2.3 From 53760bb836766973d3a3eb68d12782858b35adf3 Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Wed, 15 Feb 2017 21:14:17 +0000 Subject: removes redundant code from gitlab database file --- lib/gitlab/database.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index d160cadc2d0..f3f417c1a63 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -24,7 +24,7 @@ module Gitlab def self.nulls_last_order(field, direction = 'ASC') order = "#{field} #{direction}" - if Gitlab::Database.postgresql? + if postgresql? order << ' NULLS LAST' else # `field IS NULL` will be `0` for non-NULL columns and `1` for NULL @@ -38,7 +38,7 @@ module Gitlab def self.nulls_first_order(field, direction = 'ASC') order = "#{field} #{direction}" - if Gitlab::Database.postgresql? + if postgresql? order << ' NULLS FIRST' else # `field IS NULL` will be `0` for non-NULL columns and `1` for NULL @@ -50,7 +50,7 @@ module Gitlab end def self.random - Gitlab::Database.postgresql? ? "RANDOM()" : "RAND()" + postgresql? ? "RANDOM()" : "RAND()" end def true_value -- cgit v1.2.3 From 6cc4cf1e151fb8da16796d7bbab16bc8a1ac08b6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 16 Feb 2017 18:24:56 -0600 Subject: Fix cherry-picking or reverting through an MR --- lib/api/commits.rb | 2 +- lib/api/v3/commits.rb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/api/commits.rb b/lib/api/commits.rb index fd03e92264d..b0aa10f8bf2 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -127,7 +127,7 @@ module API commit_params = { commit: commit, - create_merge_request: false, + start_branch: params[:branch], target_branch: params[:branch] } diff --git a/lib/api/v3/commits.rb b/lib/api/v3/commits.rb index 506204b3517..d254d247042 100644 --- a/lib/api/v3/commits.rb +++ b/lib/api/v3/commits.rb @@ -130,9 +130,7 @@ module API commit_params = { commit: commit, - create_merge_request: false, - source_project: user_project, - source_branch: commit.cherry_pick_branch_name, + start_branch: params[:branch], target_branch: params[:branch] } -- cgit v1.2.3 From a0ab45d0c3d9625e8cff994173b6441e9623770f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 2 Mar 2017 18:18:50 -0500 Subject: Fix the `Gitlab::Seeder` monkey patch to disable mail delivery --- lib/gitlab/seeder.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb index b7f825e8284..823f697f51c 100644 --- a/lib/gitlab/seeder.rb +++ b/lib/gitlab/seeder.rb @@ -1,24 +1,23 @@ +module DeliverNever + def deliver_later + self + end +end + module Gitlab class Seeder def self.quiet mute_mailer SeedFu.quiet = true + yield + SeedFu.quiet = false puts "\nOK".color(:green) end - def self.by_user(user) - yield - end - def self.mute_mailer - code = <<-eos -def Notify.deliver_later - self -end - eos - eval(code) # rubocop:disable Security/Eval + ActionMailer::MessageDelivery.prepend(DeliverNever) end end end -- cgit v1.2.3 From 59e7d04bc78a69137b649dc8ac470a6c3eedbd3c Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Mon, 30 Jan 2017 12:11:58 +0100 Subject: Expose pipelines as PipelineBasic `projects/:id/pipelines` The `projects/:id/pipelines` exposed a lot of extra details that are superfluous and it was taking extra resources to fetch them. To get more details about a pipeline, use `projects/:id/pipelines/:pipeline_id`. --- lib/api/api.rb | 1 + lib/api/pipelines.rb | 4 ++-- lib/api/v3/pipelines.rb | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 lib/api/v3/pipelines.rb (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index 3e53ab693ab..89449ce8813 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -20,6 +20,7 @@ module API mount ::API::V3::MergeRequestDiffs mount ::API::V3::MergeRequests mount ::API::V3::Notes + mount ::API::V3::Pipelines mount ::API::V3::ProjectHooks mount ::API::V3::Milestones mount ::API::V3::Projects diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb index 3afc1e385fe..0721b975ba4 100644 --- a/lib/api/pipelines.rb +++ b/lib/api/pipelines.rb @@ -10,7 +10,7 @@ module API resource :projects do desc 'Get all Pipelines of the project' do detail 'This feature was introduced in GitLab 8.11.' - success Entities::Pipeline + success Entities::PipelineBasic end params do use :pagination @@ -21,7 +21,7 @@ module API authorize! :read_pipeline, user_project pipelines = PipelinesFinder.new(user_project).execute(scope: params[:scope]) - present paginate(pipelines), with: Entities::Pipeline + present paginate(pipelines), with: Entities::PipelineBasic end desc 'Create a new pipeline' do diff --git a/lib/api/v3/pipelines.rb b/lib/api/v3/pipelines.rb new file mode 100644 index 00000000000..2c26a5f7d35 --- /dev/null +++ b/lib/api/v3/pipelines.rb @@ -0,0 +1,36 @@ +module API + module V3 + class Pipelines < Grape::API + include PaginationParams + + before { authenticate! } + + params do + requires :id, type: String, desc: 'The project ID' + end + resource :projects do + desc 'Get all Pipelines of the project' do + detail 'This feature was introduced in GitLab 8.11.' + success ::API::Entities::Pipeline + end + params do + use :pagination + optional :scope, type: String, values: %w(running branches tags), + desc: 'Either running, branches, or tags' + end + get ':id/pipelines' do + authorize! :read_pipeline, user_project + + pipelines = PipelinesFinder.new(user_project).execute(scope: params[:scope]) + present paginate(pipelines), with: ::API::Entities::Pipeline + end + end + + helpers do + def pipeline + @pipeline ||= user_project.pipelines.find(params[:pipeline_id]) + end + end + end + end +end -- cgit v1.2.3 From 06e96907eef49c3be1b3608e77420e42d554943a Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Thu, 2 Mar 2017 16:47:10 -0300 Subject: Add filter param for authorized projects for current_user for V4 --- lib/api/helpers.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 4600abc7dc7..cf57cb1b825 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -252,6 +252,10 @@ module API # project helpers def filter_projects(projects) + if params[:authorized] + projects = projects.merge(current_user.authorized_projects) + end + if params[:owned] projects = projects.merge(current_user.owned_projects) end -- cgit v1.2.3 From 76e96878aad0a281f8c32ef98a276b499e2581ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chojnacki?= Date: Fri, 3 Mar 2017 11:05:24 +0000 Subject: Stop setting Strict-Transport-Securty header from within the app --- lib/support/nginx/gitlab-ssl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 5661394058d..330031aaddc 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -82,6 +82,9 @@ server { ## # ssl_dhparam /etc/ssl/certs/dhparam.pem; + ## [Optional] Enable HTTP Strict Transport Security + # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; + ## Individual nginx logs for this GitLab vhost access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; -- cgit v1.2.3 From 2b27a98db32d788796c4495f5405ba09436fd8ce Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 28 Feb 2017 12:07:04 +0100 Subject: Add support for Workhorse notifications --- lib/gitlab/workhorse.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 3ff9f9eb5e7..ca4eba48a8b 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -8,6 +8,7 @@ module Gitlab VERSION_FILE = 'GITLAB_WORKHORSE_VERSION'.freeze INTERNAL_API_CONTENT_TYPE = 'application/vnd.gitlab-workhorse+json'.freeze INTERNAL_API_REQUEST_HEADER = 'Gitlab-Workhorse-Api-Request'.freeze + NOTIFICATION_CHANNEL = 'workhorse:notifications'.freeze # Supposedly the effective key size for HMAC-SHA256 is 256 bits, i.e. 32 # bytes https://tools.ietf.org/html/rfc4868#section-2.6 @@ -154,6 +155,19 @@ module Gitlab Rails.root.join('.gitlab_workhorse_secret') end + def ensure_and_notify(key, value, expire: nil, overwrite: true) + Gitlab::Redis.with do |redis| + result = redis.set(key, value, ex: expire, nx: !overwrite) + if result + payload = "#{key}=#{value}" + redis.publish(RUNNER_NOTIFICATION_CHANNEL, payload) + value + else + redis.get(key) + end + end + end + protected def encode(hash) -- cgit v1.2.3 From 6357635686fafb2fc9af5090c1edabfe25649085 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Fri, 3 Mar 2017 12:00:34 +0100 Subject: Rename query parameter to `membership` The query parameter `membership` should be more self-explaining. --- lib/api/helpers.rb | 2 +- lib/api/projects.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index cf57cb1b825..9c41146f1e3 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -252,7 +252,7 @@ module API # project helpers def filter_projects(projects) - if params[:authorized] + if params[:membership] projects = projects.merge(current_user.authorized_projects) end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index f302496c12b..63a4cdd5954 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -46,9 +46,10 @@ module API optional :archived, type: Boolean, default: false, desc: 'Limit by archived status' optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'Limit by visibility' - optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria' + optional :search, type: String, desc: 'Return list of projects matching the search criteria' optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' optional :starred, type: Boolean, default: false, desc: 'Limit by starred status' + optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of' end params :statistics_params do -- cgit v1.2.3 From 3f5191de6db80872e3e712247b5582bdc4eec296 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 3 Mar 2017 13:51:49 +0100 Subject: Add specs for notifications --- lib/gitlab/workhorse.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index ca4eba48a8b..34fbb227f7b 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -160,7 +160,7 @@ module Gitlab result = redis.set(key, value, ex: expire, nx: !overwrite) if result payload = "#{key}=#{value}" - redis.publish(RUNNER_NOTIFICATION_CHANNEL, payload) + redis.publish(NOTIFICATION_CHANNEL, payload) value else redis.get(key) -- cgit v1.2.3 From d13669c98b5b2c15cfff8b65e12ce1ef0911f1cd Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Fri, 24 Feb 2017 13:18:07 +0100 Subject: Remove remnants of git annex --- lib/backup/repository.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index d16d5ba4960..3c4ba5d50e6 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -180,9 +180,8 @@ module Backup return unless Dir.exist?(path) dir_entries = Dir.entries(path) - %w[annex custom_hooks].each do |entry| - yield(entry) if dir_entries.include?(entry) - end + + yield('custom_hooks') if dir_entries.include?('custom_hooks') end def prepare -- cgit v1.2.3 From ffd970d97dc9faf82752e6494aab8ba6fdce759a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 3 Mar 2017 14:12:35 -0800 Subject: Make SidekiqStatus able to count number of jobs completed/running --- lib/gitlab/sidekiq_status.rb | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb index aadc401ff8d..11e5f1b645c 100644 --- a/lib/gitlab/sidekiq_status.rb +++ b/lib/gitlab/sidekiq_status.rb @@ -44,19 +44,42 @@ module Gitlab # Returns true if all the given job have been completed. # - # jids - The Sidekiq job IDs to check. + # job_ids - The Sidekiq job IDs to check. # # Returns true or false. - def self.all_completed?(jids) - keys = jids.map { |jid| key_for(jid) } + def self.all_completed?(job_ids) + self.num_running(job_ids).zero? + end + + # Returns the number of jobs that are running. + # + # job_ids - The Sidekiq job IDs to check. + def self.num_running(job_ids) + responses = self.job_status(job_ids) - responses = Sidekiq.redis do |redis| + responses.select(&:present?).count + end + + # Returns the number of jobs that have completed. + # + # job_ids - The Sidekiq job IDs to check. + def self.num_completed(job_ids) + job_ids.size - self.num_running(job_ids) + end + + # Returns the job status for each of the given job IDs. + # + # job_ids - The Sidekiq job IDs to check. + # + # Returns an array of true or false indicating job completion. + def self.job_status(job_ids) + keys = job_ids.map { |jid| key_for(jid) } + + Sidekiq.redis do |redis| redis.pipelined do keys.each { |key| redis.exists(key) } end end - - responses.all? { |value| !value } end def self.key_for(jid) -- cgit v1.2.3 From c33f09d2549d2228a5ac7ceb7cb099774fbd826e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 5 Mar 2017 18:49:30 +0100 Subject: Update triggers API --- lib/api/entities.rb | 4 --- lib/api/triggers.rb | 68 ++++++++++++++++++++++++++++++++++++-------- lib/api/v3/entities.rb | 8 ++++++ lib/api/v3/triggers.rb | 77 ++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d2d21f5d03a..82d05d85ead 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -592,10 +592,6 @@ module API end end - class TriggerRequest < Grape::Entity - expose :id, :variables - end - class Runner < Grape::Entity expose :id expose :description diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index b7c9c5f2b7f..c324708a16d 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -6,7 +6,7 @@ module API requires :id, type: String, desc: 'The ID of a project' end resource :projects do - desc 'Trigger a GitLab project build' do + desc 'Trigger a GitLab project pipeline' do success Entities::TriggerRequest end params do @@ -14,7 +14,7 @@ module API requires :token, type: String, desc: 'The unique token of trigger' optional :variables, type: Hash, desc: 'The list of variables to be injected into build' end - post ":id/(ref/:ref/)trigger/builds" do + post ":id/(ref/:ref/)trigger/pipeline" do project = find_project(params[:id]) trigger = Ci::Trigger.find_by_token(params[:token].to_s) not_found! unless project && trigger @@ -29,9 +29,9 @@ module API # create request and trigger builds trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) if trigger_request - present trigger_request, with: Entities::TriggerRequest + present trigger_request.pipeline, with: Entities::Pipeline else - errors = 'No builds created' + errors = 'No pipeline create' render_api_error!(errors, 400) end end @@ -55,13 +55,13 @@ module API success Entities::Trigger end params do - requires :token, type: String, desc: 'The unique token of trigger' + requires :trigger_id, type: Integer, desc: 'The trigger ID' end - get ':id/triggers/:token' do + get ':id/triggers/:trigger_id' do authenticate! authorize! :admin_build, user_project - trigger = user_project.triggers.find_by(token: params[:token].to_s) + trigger = user_project.triggers.find(params[:trigger_id]) return not_found!('Trigger') unless trigger present trigger, with: Entities::Trigger @@ -70,26 +70,72 @@ module API desc 'Create a trigger' do success Entities::Trigger end + params do + requires :description, type: String, desc: 'The trigger description' + end post ':id/triggers' do authenticate! authorize! :admin_build, user_project - trigger = user_project.triggers.create + trigger = user_project.triggers.create( + declared_params(include_missing: false).merge(owner: current_user)) + + if trigger.valid? + present trigger, with: Entities::Trigger + else + render_validation_error!(trigger) + end + end + + desc 'Update a trigger' do + success Entities::Trigger + end + params do + requires :trigger_id, type: Integer, desc: 'The trigger ID' + optional :description, type: String, desc: 'The trigger description' + end + delete ':id/triggers/:trigger_id' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.find(params[:trigger_id]) + return not_found!('Trigger') unless trigger + trigger = trigger.update(declared_params(include_missing: false)) present trigger, with: Entities::Trigger end + desc 'Take ownership of trigger' do + success Entities::Trigger + end + params do + requires :trigger_id, type: Integer, desc: 'The trigger ID' + end + post ':id/triggers/:trigger_id/take' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.find(params[:trigger_id]) + return not_found!('Trigger') unless trigger + + if trigger.update(owner: current_user) + present trigger, with: Entities::Trigger + else + render_validation_error!(trigger) + end + end + desc 'Delete a trigger' do success Entities::Trigger end params do - requires :token, type: String, desc: 'The unique token of trigger' + requires :trigger_id, type: Integer, desc: 'The trigger ID' end - delete ':id/triggers/:token' do + delete ':id/triggers/:trigger_id' do authenticate! authorize! :admin_build, user_project - trigger = user_project.triggers.find_by(token: params[:token].to_s) + trigger = user_project.triggers.find(params[:trigger_id]) return not_found!('Trigger') unless trigger trigger.destroy diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 270d99a2348..29a44d4c7e5 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -186,6 +186,14 @@ module API class Environment < ::API::Entities::EnvironmentBasic expose :project, using: Entities::Project end + + class Trigger < Grape::Entity + expose :token, :created_at, :updated_at, :deleted_at, :last_used + end + + class TriggerRequest < Grape::Entity + expose :id, :variables + end end end end diff --git a/lib/api/v3/triggers.rb b/lib/api/v3/triggers.rb index 4051d4bca8d..1dfdb6a5956 100644 --- a/lib/api/v3/triggers.rb +++ b/lib/api/v3/triggers.rb @@ -7,8 +7,81 @@ module API requires :id, type: String, desc: 'The ID of a project' end resource :projects do + desc 'Trigger a GitLab project build' do + success ::API::V3::Entities::TriggerRequest + end + params do + requires :ref, type: String, desc: 'The commit sha or name of a branch or tag' + requires :token, type: String, desc: 'The unique token of trigger' + optional :variables, type: Hash, desc: 'The list of variables to be injected into build' + end + post ":id/(ref/:ref/)trigger/builds" do + project = find_project(params[:id]) + trigger = Ci::Trigger.find_by_token(params[:token].to_s) + not_found! unless project && trigger + unauthorized! unless trigger.project == project + + # validate variables + variables = params[:variables].to_h + unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } + render_api_error!('variables needs to be a map of key-valued strings', 400) + end + + # create request and trigger builds + trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) + if trigger_request + present trigger_request, with: ::API::V3::Entities::TriggerRequest + else + errors = 'No builds created' + render_api_error!(errors, 400) + end + end + + desc 'Get triggers list' do + success ::API::V3::Entities::Trigger + end + params do + use :pagination + end + get ':id/triggers' do + authenticate! + authorize! :admin_build, user_project + + triggers = user_project.triggers.includes(:trigger_requests) + + present paginate(triggers), with: ::API::V3::Entities::Trigger + end + + desc 'Get specific trigger of a project' do + success ::API::V3::Entities::Trigger + end + params do + requires :token, type: String, desc: 'The unique token of trigger' + end + get ':id/triggers/:token' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.find_by(token: params[:token].to_s) + return not_found!('Trigger') unless trigger + + present trigger, with: ::API::V3::Entities::Trigger + end + + desc 'Create a trigger' do + success ::API::V3::Entities::Trigger + end + post ':id/triggers' do + authenticate! + authorize! :admin_build, user_project + + trigger = user_project.triggers.create + + present trigger, with: ::API::V3::Entities::Trigger + end + desc 'Delete a trigger' do - success ::API::Entities::Trigger + success ::API::V3::Entities::Trigger end params do requires :token, type: String, desc: 'The unique token of trigger' @@ -22,7 +95,7 @@ module API trigger.destroy - present trigger, with: ::API::Entities::Trigger + present trigger, with: ::API::V3::Entities::Trigger end end end -- cgit v1.2.3 From 140b51ce980bc519f3478bf4321dfd4a35d6bd3c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 5 Mar 2017 20:58:08 +0100 Subject: Introduce tests for pipeline triggers --- lib/api/entities.rb | 4 +++- lib/api/triggers.rb | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 82d05d85ead..c0c94044ced 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -639,7 +639,9 @@ module API end class Trigger < Grape::Entity - expose :token, :created_at, :updated_at, :deleted_at, :last_used + expose :token, :description + expose :created_at, :updated_at, :deleted_at, :last_used + expose :owner, using: Entities::UserBasic end class Variable < Grape::Entity diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index c324708a16d..157f3cef1fd 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -7,7 +7,7 @@ module API end resource :projects do desc 'Trigger a GitLab project pipeline' do - success Entities::TriggerRequest + success Entities::Pipeline end params do requires :ref, type: String, desc: 'The commit sha or name of a branch or tag' @@ -31,7 +31,7 @@ module API if trigger_request present trigger_request.pipeline, with: Entities::Pipeline else - errors = 'No pipeline create' + errors = 'No pipeline created' render_api_error!(errors, 400) end end @@ -61,7 +61,7 @@ module API authenticate! authorize! :admin_build, user_project - trigger = user_project.triggers.find(params[:trigger_id]) + trigger = user_project.triggers.find(params.delete(:trigger_id)) return not_found!('Trigger') unless trigger present trigger, with: Entities::Trigger @@ -94,15 +94,18 @@ module API requires :trigger_id, type: Integer, desc: 'The trigger ID' optional :description, type: String, desc: 'The trigger description' end - delete ':id/triggers/:trigger_id' do + put ':id/triggers/:trigger_id' do authenticate! authorize! :admin_build, user_project - trigger = user_project.triggers.find(params[:trigger_id]) + trigger = user_project.triggers.find(params.delete(:trigger_id)) return not_found!('Trigger') unless trigger - trigger = trigger.update(declared_params(include_missing: false)) - present trigger, with: Entities::Trigger + if trigger.update(declared_params(include_missing: false)) + present trigger, with: Entities::Trigger + else + render_validation_error!(trigger) + end end desc 'Take ownership of trigger' do @@ -115,10 +118,11 @@ module API authenticate! authorize! :admin_build, user_project - trigger = user_project.triggers.find(params[:trigger_id]) + trigger = user_project.triggers.find(params.delete(:trigger_id)) return not_found!('Trigger') unless trigger if trigger.update(owner: current_user) + status :ok present trigger, with: Entities::Trigger else render_validation_error!(trigger) @@ -135,7 +139,7 @@ module API authenticate! authorize! :admin_build, user_project - trigger = user_project.triggers.find(params[:trigger_id]) + trigger = user_project.triggers.find(params.delete(:trigger_id)) return not_found!('Trigger') unless trigger trigger.destroy -- cgit v1.2.3 From b565ee4912d742ef01d10e9a6fae64fe79d6b7bf Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 5 Mar 2017 21:18:00 +0100 Subject: Update documentation and expose ID --- lib/api/entities.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index c0c94044ced..98ef9d4118e 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -639,6 +639,7 @@ module API end class Trigger < Grape::Entity + expose :id expose :token, :description expose :created_at, :updated_at, :deleted_at, :last_used expose :owner, using: Entities::UserBasic -- cgit v1.2.3 From 2cc6485518c332d1452316e24155e921020886d9 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 15 Feb 2017 13:49:14 +0000 Subject: Improved team selection Review changes --- lib/mattermost/team.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb index 09dfd082b3a..afc152aa02e 100644 --- a/lib/mattermost/team.rb +++ b/lib/mattermost/team.rb @@ -1,7 +1,7 @@ module Mattermost class Team < Client def all - session_get('/api/v3/teams/all') + session_get('/api/v3/teams/all').values end end end -- cgit v1.2.3 From be039d22d71afa7c8b2635cd8820b8b4566d15b8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 28 Feb 2017 16:48:39 +0100 Subject: Make manual actions blocking --- lib/gitlab/ci/config/entry/job.rb | 4 ++++ lib/gitlab/ci/status/manual.rb | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 lib/gitlab/ci/status/manual.rb (limited to 'lib') diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb index 7f7662f2776..69e2a8a2563 100644 --- a/lib/gitlab/ci/config/entry/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -104,6 +104,10 @@ module Gitlab (before_script_value.to_a + script_value.to_a).join("\n") end + def allow_failure + super || self.when == 'manual' + end + private def inherit!(deps) diff --git a/lib/gitlab/ci/status/manual.rb b/lib/gitlab/ci/status/manual.rb new file mode 100644 index 00000000000..741b3dd96e9 --- /dev/null +++ b/lib/gitlab/ci/status/manual.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Manual < Status::Core + def text + 'manual' + end + + def label + 'manual' + end + + def icon + 'icon_status_manual' + end + end + end + end +end -- cgit v1.2.3 From 79ea01bfaffa743e98716cf9f9f811c6843dea4b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 2 Mar 2017 13:45:01 +0100 Subject: Refactor code related to pipeline blocking actions --- lib/gitlab/ci/config/entry/job.rb | 4 ---- 1 file changed, 4 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb index 69e2a8a2563..7f7662f2776 100644 --- a/lib/gitlab/ci/config/entry/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -104,10 +104,6 @@ module Gitlab (before_script_value.to_a + script_value.to_a).join("\n") end - def allow_failure - super || self.when == 'manual' - end - private def inherit!(deps) -- cgit v1.2.3 From 14351f97672a79a54b4431472506b67577818ddf Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 3 Mar 2017 10:24:59 +0100 Subject: Introduce core status for blocked manual actions --- lib/gitlab/ci/status/blocked.rb | 19 +++++++++++++++++++ lib/gitlab/ci/status/manual.rb | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 lib/gitlab/ci/status/blocked.rb delete mode 100644 lib/gitlab/ci/status/manual.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/blocked.rb b/lib/gitlab/ci/status/blocked.rb new file mode 100644 index 00000000000..d17f4ea77b9 --- /dev/null +++ b/lib/gitlab/ci/status/blocked.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Blocked < Status::Core + def text + 'blocked' + end + + def label + 'blocked action' + end + + def icon + 'icon_status_manual' + end + end + end + end +end diff --git a/lib/gitlab/ci/status/manual.rb b/lib/gitlab/ci/status/manual.rb deleted file mode 100644 index 741b3dd96e9..00000000000 --- a/lib/gitlab/ci/status/manual.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab - module Ci - module Status - class Manual < Status::Core - def text - 'manual' - end - - def label - 'manual' - end - - def icon - 'icon_status_manual' - end - end - end - end -end -- cgit v1.2.3 From ac5bd3b73c0255bb9307913a2d4338d0a431cac6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 3 Mar 2017 14:35:19 +0100 Subject: Reinstitute a core `manual` status for manual actions --- lib/gitlab/ci/status/blocked.rb | 19 ------------------- lib/gitlab/ci/status/manual.rb | 19 +++++++++++++++++++ lib/gitlab/data_builder/pipeline.rb | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) delete mode 100644 lib/gitlab/ci/status/blocked.rb create mode 100644 lib/gitlab/ci/status/manual.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/blocked.rb b/lib/gitlab/ci/status/blocked.rb deleted file mode 100644 index d17f4ea77b9..00000000000 --- a/lib/gitlab/ci/status/blocked.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab - module Ci - module Status - class Blocked < Status::Core - def text - 'blocked' - end - - def label - 'blocked action' - end - - def icon - 'icon_status_manual' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/manual.rb b/lib/gitlab/ci/status/manual.rb new file mode 100644 index 00000000000..5f28521901d --- /dev/null +++ b/lib/gitlab/ci/status/manual.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Manual < Status::Core + def text + 'manual' + end + + def label + 'manual action' + end + + def icon + 'icon_status_manual' + end + end + end + end +end diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb index e50e54b6e99..182a30fd74d 100644 --- a/lib/gitlab/data_builder/pipeline.rb +++ b/lib/gitlab/data_builder/pipeline.rb @@ -39,7 +39,7 @@ module Gitlab started_at: build.started_at, finished_at: build.finished_at, when: build.when, - manual: build.manual?, + manual: build.action?, user: build.user.try(:hook_attrs), runner: build.runner && runner_hook_attrs(build.runner), artifacts_file: { -- cgit v1.2.3 From 2d652fcf5a1c801c11ce11414f21234d409315f4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 6 Mar 2017 11:44:45 +0100 Subject: Update notification code --- lib/gitlab/workhorse.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 34fbb227f7b..eae1a0abf06 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -155,12 +155,11 @@ module Gitlab Rails.root.join('.gitlab_workhorse_secret') end - def ensure_and_notify(key, value, expire: nil, overwrite: true) + def set_key_and_notify(key, value, expire: nil, overwrite: true) Gitlab::Redis.with do |redis| result = redis.set(key, value, ex: expire, nx: !overwrite) if result - payload = "#{key}=#{value}" - redis.publish(NOTIFICATION_CHANNEL, payload) + redis.publish(NOTIFICATION_CHANNEL, "#{key}=#{value}") value else redis.get(key) -- cgit v1.2.3 From d5f7060400a06d98f9e7107949aca8d89eaba7a8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 6 Mar 2017 12:00:15 +0100 Subject: Rename `/take` to `/take_ownership`, expose `owner` in `v3`. --- lib/api/triggers.rb | 2 +- lib/api/v3/entities.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 157f3cef1fd..119e9024712 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -114,7 +114,7 @@ module API params do requires :trigger_id, type: Integer, desc: 'The trigger ID' end - post ':id/triggers/:trigger_id/take' do + post ':id/triggers/:trigger_id/take_ownership' do authenticate! authorize! :admin_build, user_project diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 29a44d4c7e5..2492481e4f0 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -189,6 +189,7 @@ module API class Trigger < Grape::Entity expose :token, :created_at, :updated_at, :deleted_at, :last_used + expose :owner, using: Entities::UserBasic end class TriggerRequest < Grape::Entity -- cgit v1.2.3 From 66dd20966166695695079958d36527606425cb88 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 6 Mar 2017 12:01:33 +0100 Subject: Ignore job by default if it is a manual action This makes it possible to maintain backwards compatibility with configs created when manual actions were non-blocking. From now manual actions are blocking if configured with `allow_failure: false`, otherwise manual actions are optional, and their status is ignored. --- lib/ci/gitlab_ci_yaml_processor.rb | 2 +- lib/gitlab/ci/config/entry/job.rb | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index e390919ae1d..15a461a16dd 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -58,7 +58,7 @@ module Ci commands: job[:commands], tag_list: job[:tags] || [], name: job[:name].to_s, - allow_failure: job[:allow_failure] || false, + allow_failure: job[:ignore], when: job[:when] || 'on_success', environment: job[:environment_name], coverage_regex: job[:coverage], diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb index 7f7662f2776..176301bcca1 100644 --- a/lib/gitlab/ci/config/entry/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -104,6 +104,14 @@ module Gitlab (before_script_value.to_a + script_value.to_a).join("\n") end + def manual_action? + self.when == 'manual' + end + + def ignored? + allow_failure.nil? ? manual_action? : allow_failure + end + private def inherit!(deps) @@ -135,7 +143,8 @@ module Gitlab environment_name: environment_defined? ? environment_value[:name] : nil, coverage: coverage_defined? ? coverage_value : nil, artifacts: artifacts_value, - after_script: after_script_value } + after_script: after_script_value, + ignore: ignored? } end end end -- cgit v1.2.3 From 20feaa9eff410cba133e5598f643883e603ba5da Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 6 Mar 2017 12:27:55 +0100 Subject: Fix UserBasic --- lib/api/v3/entities.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 2492481e4f0..69853d33bec 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -189,7 +189,7 @@ module API class Trigger < Grape::Entity expose :token, :created_at, :updated_at, :deleted_at, :last_used - expose :owner, using: Entities::UserBasic + expose :owner, using: ::API::Entities::UserBasic end class TriggerRequest < Grape::Entity -- cgit v1.2.3 From bb2460c1ea0e6dbff8e62dba2a7b59acd969a1b1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 6 Mar 2017 14:08:55 +0100 Subject: Improve specs for detailed statuses with manual actions --- lib/gitlab/ci/status/build/play.rb | 12 ------------ lib/gitlab/ci/status/build/stop.rb | 12 ------------ 2 files changed, 24 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb index 0f4b7b24cef..3495b8d0448 100644 --- a/lib/gitlab/ci/status/build/play.rb +++ b/lib/gitlab/ci/status/build/play.rb @@ -5,22 +5,10 @@ module Gitlab class Play < SimpleDelegator include Status::Extended - def text - 'manual' - end - def label 'manual play action' end - def icon - 'icon_status_manual' - end - - def group - 'manual' - end - def has_action? can?(user, :update_build, subject) end diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb index 90401cad0d2..e8530f2aaae 100644 --- a/lib/gitlab/ci/status/build/stop.rb +++ b/lib/gitlab/ci/status/build/stop.rb @@ -5,22 +5,10 @@ module Gitlab class Stop < SimpleDelegator include Status::Extended - def text - 'manual' - end - def label 'manual stop action' end - def icon - 'icon_status_manual' - end - - def group - 'manual' - end - def has_action? can?(user, :update_build, subject) end -- cgit v1.2.3 From c727d4328fa14c4e90eb47c04f045c0f54224018 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Mon, 6 Mar 2017 12:48:10 +0100 Subject: Remove "subscribed" field from API responses returning list of issues or merge requests --- lib/api/entities.rb | 22 ++++++++++++++-------- lib/api/helpers.rb | 8 -------- lib/api/issues.rb | 12 ++++++------ lib/api/merge_requests.rb | 12 ++++++++++-- lib/api/milestones.rb | 11 +++++++---- lib/api/v3/merge_requests.rb | 8 ++++++++ lib/api/v3/milestones.rb | 21 +++++++++++++++++++++ 7 files changed, 66 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 98ef9d4118e..6e251b36bda 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -249,14 +249,11 @@ module API expose :start_date end - class Issue < ProjectEntity + class IssueBasic < ProjectEntity expose :label_names, as: :labels expose :milestone, using: Entities::Milestone expose :assignee, :author, using: Entities::UserBasic - expose :subscribed do |issue, options| - issue.subscribed?(options[:current_user], options[:project] || issue.project) - end expose :user_notes_count expose :upvotes, :downvotes expose :due_date @@ -267,6 +264,12 @@ module API end end + class Issue < IssueBasic + expose :subscribed do |issue, options| + issue.subscribed?(options[:current_user], options[:project] || issue.project) + end + end + class IssuableTimeStats < Grape::Entity expose :time_estimate expose :total_time_spent @@ -279,7 +282,7 @@ module API expose :id end - class MergeRequest < ProjectEntity + class MergeRequestBasic < ProjectEntity expose :target_branch, :source_branch expose :upvotes, :downvotes expose :author, :assignee, using: Entities::UserBasic @@ -291,9 +294,6 @@ module API expose :merge_status expose :diff_head_sha, as: :sha expose :merge_commit_sha - expose :subscribed do |merge_request, options| - merge_request.subscribed?(options[:current_user], options[:project]) - end expose :user_notes_count expose :should_remove_source_branch?, as: :should_remove_source_branch expose :force_remove_source_branch?, as: :force_remove_source_branch @@ -303,6 +303,12 @@ module API end end + class MergeRequest < MergeRequestBasic + expose :subscribed do |merge_request, options| + merge_request.subscribed?(options[:current_user], options[:project]) + end + end + class MergeRequestChanges < MergeRequest expose :diffs, as: :changes, using: Entities::RepoDiff do |compare, _| compare.raw_diffs(all_diffs: true).to_a diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 9c41146f1e3..a43252a4661 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -388,14 +388,6 @@ module API header(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format)) end - def issue_entity(project) - if project.has_external_issue_tracker? - Entities::ExternalIssue - else - Entities::Issue - end - end - # The Grape Error Middleware only has access to env but no params. We workaround this by # defining a method that returns the right value. def define_params_for_grape_middleware diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 1d6d0b05750..bda74069ad5 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -41,7 +41,7 @@ module API resource :issues do desc "Get currently authenticated user's issues" do - success Entities::Issue + success Entities::IssueBasic end params do optional :state, type: String, values: %w[opened closed all], default: 'all', @@ -51,7 +51,7 @@ module API get do issues = find_issues(scope: 'authored') - present paginate(issues), with: Entities::Issue, current_user: current_user + present paginate(issues), with: Entities::IssueBasic, current_user: current_user end end @@ -60,7 +60,7 @@ module API end resource :groups do desc 'Get a list of group issues' do - success Entities::Issue + success Entities::IssueBasic end params do optional :state, type: String, values: %w[opened closed all], default: 'opened', @@ -72,7 +72,7 @@ module API issues = find_issues(group_id: group.id, state: params[:state] || 'opened') - present paginate(issues), with: Entities::Issue, current_user: current_user + present paginate(issues), with: Entities::IssueBasic, current_user: current_user end end @@ -83,7 +83,7 @@ module API include TimeTrackingEndpoints desc 'Get a list of project issues' do - success Entities::Issue + success Entities::IssueBasic end params do optional :state, type: String, values: %w[opened closed all], default: 'all', @@ -95,7 +95,7 @@ module API issues = find_issues(project_id: project.id) - present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project + present paginate(issues), with: Entities::IssueBasic, current_user: current_user, project: user_project end desc 'Get a single project issue' do diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 4638a66811d..6fc33a7a54a 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -25,6 +25,14 @@ module API render_api_error!(errors, 400) end + def issue_entity(project) + if project.has_external_issue_tracker? + Entities::ExternalIssue + else + Entities::IssueBasic + end + end + params :optional_params do optional :description, type: String, desc: 'The description of the merge request' optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' @@ -35,7 +43,7 @@ module API end desc 'List merge requests' do - success Entities::MergeRequest + success Entities::MergeRequestBasic end params do optional :state, type: String, values: %w[opened closed merged all], default: 'all', @@ -62,7 +70,7 @@ module API end merge_requests = merge_requests.reorder(params[:order_by] => params[:sort]) - present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user, project: user_project + present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project end desc 'Create a merge request' do diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index bd74174c655..e7f7edd95c7 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -103,7 +103,7 @@ module API end desc 'Get all issues for a single project milestone' do - success Entities::Issue + success Entities::IssueBasic end params do requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' @@ -120,12 +120,12 @@ module API } issues = IssuesFinder.new(current_user, finder_params).execute - present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project + present paginate(issues), with: Entities::IssueBasic, current_user: current_user, project: user_project end desc 'Get all merge requests for a single project milestone' do detail 'This feature was introduced in GitLab 9.' - success Entities::MergeRequest + success Entities::MergeRequestBasic end params do requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' @@ -142,7 +142,10 @@ module API } merge_requests = MergeRequestsFinder.new(current_user, finder_params).execute - present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user, project: user_project + present paginate(merge_requests), + with: Entities::MergeRequestBasic, + current_user: current_user, + project: user_project end end end diff --git a/lib/api/v3/merge_requests.rb b/lib/api/v3/merge_requests.rb index 654e818e1b5..7dbd4691a94 100644 --- a/lib/api/v3/merge_requests.rb +++ b/lib/api/v3/merge_requests.rb @@ -28,6 +28,14 @@ module API render_api_error!(errors, 400) end + def issue_entity(project) + if project.has_external_issue_tracker? + ::API::Entities::ExternalIssue + else + ::API::Entities::Issue + end + end + params :optional_params do optional :description, type: String, desc: 'The description of the merge request' optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' diff --git a/lib/api/v3/milestones.rb b/lib/api/v3/milestones.rb index bbc29c40ee2..2a850a08a8a 100644 --- a/lib/api/v3/milestones.rb +++ b/lib/api/v3/milestones.rb @@ -37,6 +37,27 @@ module API present paginate(milestones), with: ::API::Entities::Milestone end + + desc 'Get all issues for a single project milestone' do + success ::API::Entities::Issue + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' + use :pagination + end + get ':id/milestones/:milestone_id/issues' do + authorize! :read_milestone, user_project + + milestone = user_project.milestones.find(params[:milestone_id]) + + finder_params = { + project_id: user_project.id, + milestone_title: milestone.title + } + + issues = IssuesFinder.new(current_user, finder_params).execute + present paginate(issues), with: ::API::Entities::Issue, current_user: current_user, project: user_project + end end end end -- cgit v1.2.3 From 111748ea89752825d6baba783b3b687c48e9e830 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 22 Feb 2017 11:13:59 +0100 Subject: Rename Builds to Jobs in the API Fixes gitlab-org/gitlab-ce#28515 [ci skip] --- lib/api/api.rb | 3 +- lib/api/builds.rb | 261 ------------------------------------------------ lib/api/entities.rb | 24 +++-- lib/api/jobs.rb | 262 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/api/v3/builds.rb | 263 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/api/v3/entities.rb | 10 ++ 6 files changed, 552 insertions(+), 271 deletions(-) delete mode 100644 lib/api/builds.rb create mode 100644 lib/api/jobs.rb create mode 100644 lib/api/v3/builds.rb (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index 89449ce8813..91ca8c66344 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -9,6 +9,7 @@ module API mount ::API::V3::Boards mount ::API::V3::Branches mount ::API::V3::BroadcastMessages + mount ::API::V3::Builds mount ::API::V3::Commits mount ::API::V3::DeployKeys mount ::API::V3::Environments @@ -77,7 +78,6 @@ module API mount ::API::Boards mount ::API::Branches mount ::API::BroadcastMessages - mount ::API::Builds mount ::API::Commits mount ::API::CommitStatuses mount ::API::DeployKeys @@ -87,6 +87,7 @@ module API mount ::API::Groups mount ::API::Internal mount ::API::Issues + mount ::API::Jobs mount ::API::Keys mount ::API::Labels mount ::API::Lint diff --git a/lib/api/builds.rb b/lib/api/builds.rb deleted file mode 100644 index 5b76913fe45..00000000000 --- a/lib/api/builds.rb +++ /dev/null @@ -1,261 +0,0 @@ -module API - class Builds < Grape::API - include PaginationParams - - before { authenticate! } - - params do - requires :id, type: String, desc: 'The ID of a project' - end - resource :projects do - helpers do - params :optional_scope do - optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', - values: ::CommitStatus::AVAILABLE_STATUSES, - coerce_with: ->(scope) { - if scope.is_a?(String) - [scope] - elsif scope.is_a?(Hashie::Mash) - scope.values - else - ['unknown'] - end - } - end - end - - desc 'Get a project builds' do - success Entities::Build - end - params do - use :optional_scope - use :pagination - end - get ':id/builds' do - builds = user_project.builds.order('id DESC') - builds = filter_builds(builds, params[:scope]) - - present paginate(builds), with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - - desc 'Get builds for a specific commit of a project' do - success Entities::Build - end - params do - requires :sha, type: String, desc: 'The SHA id of a commit' - use :optional_scope - use :pagination - end - get ':id/repository/commits/:sha/builds' do - authorize_read_builds! - - return not_found! unless user_project.commit(params[:sha]) - - pipelines = user_project.pipelines.where(sha: params[:sha]) - builds = user_project.builds.where(pipeline: pipelines).order('id DESC') - builds = filter_builds(builds, params[:scope]) - - present paginate(builds), with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - - desc 'Get a specific build of a project' do - success Entities::Build - end - params do - requires :build_id, type: Integer, desc: 'The ID of a build' - end - get ':id/builds/:build_id' do - authorize_read_builds! - - build = get_build!(params[:build_id]) - - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - - desc 'Download the artifacts file from build' do - detail 'This feature was introduced in GitLab 8.5' - end - params do - requires :build_id, type: Integer, desc: 'The ID of a build' - end - get ':id/builds/:build_id/artifacts' do - authorize_read_builds! - - build = get_build!(params[:build_id]) - - present_artifacts!(build.artifacts_file) - end - - desc 'Download the artifacts file from build' do - detail 'This feature was introduced in GitLab 8.10' - end - params do - requires :ref_name, type: String, desc: 'The ref from repository' - requires :job, type: String, desc: 'The name for the build' - end - get ':id/builds/artifacts/:ref_name/download', - requirements: { ref_name: /.+/ } do - authorize_read_builds! - - builds = user_project.latest_successful_builds_for(params[:ref_name]) - latest_build = builds.find_by!(name: params[:job]) - - present_artifacts!(latest_build.artifacts_file) - end - - # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace - # is saved in the DB instead of file). But before that, we need to consider how to replace the value of - # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. - desc 'Get a trace of a specific build of a project' - params do - requires :build_id, type: Integer, desc: 'The ID of a build' - end - get ':id/builds/:build_id/trace' do - authorize_read_builds! - - build = get_build!(params[:build_id]) - - header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" - content_type 'text/plain' - env['api.format'] = :binary - - trace = build.trace - body trace - end - - desc 'Cancel a specific build of a project' do - success Entities::Build - end - params do - requires :build_id, type: Integer, desc: 'The ID of a build' - end - post ':id/builds/:build_id/cancel' do - authorize_update_builds! - - build = get_build!(params[:build_id]) - - build.cancel - - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - - desc 'Retry a specific build of a project' do - success Entities::Build - end - params do - requires :build_id, type: Integer, desc: 'The ID of a build' - end - post ':id/builds/:build_id/retry' do - authorize_update_builds! - - build = get_build!(params[:build_id]) - return forbidden!('Build is not retryable') unless build.retryable? - - build = Ci::Build.retry(build, current_user) - - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - - desc 'Erase build (remove artifacts and build trace)' do - success Entities::Build - end - params do - requires :build_id, type: Integer, desc: 'The ID of a build' - end - post ':id/builds/:build_id/erase' do - authorize_update_builds! - - build = get_build!(params[:build_id]) - return forbidden!('Build is not erasable!') unless build.erasable? - - build.erase(erased_by: current_user) - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) - end - - desc 'Keep the artifacts to prevent them from being deleted' do - success Entities::Build - end - params do - requires :build_id, type: Integer, desc: 'The ID of a build' - end - post ':id/builds/:build_id/artifacts/keep' do - authorize_update_builds! - - build = get_build!(params[:build_id]) - return not_found!(build) unless build.artifacts? - - build.keep_artifacts! - - status 200 - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - - desc 'Trigger a manual build' do - success Entities::Build - detail 'This feature was added in GitLab 8.11' - end - params do - requires :build_id, type: Integer, desc: 'The ID of a Build' - end - post ":id/builds/:build_id/play" do - authorize_read_builds! - - build = get_build!(params[:build_id]) - - bad_request!("Unplayable Job") unless build.playable? - - build.play(current_user) - - status 200 - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - end - - helpers do - def get_build(id) - user_project.builds.find_by(id: id.to_i) - end - - def get_build!(id) - get_build(id) || not_found! - end - - def present_artifacts!(artifacts_file) - if !artifacts_file.file_storage? - redirect_to(build.artifacts_file.url) - elsif artifacts_file.exists? - present_file!(artifacts_file.path, artifacts_file.filename) - else - not_found! - end - end - - def filter_builds(builds, scope) - return builds if scope.nil? || scope.empty? - - available_statuses = ::CommitStatus::AVAILABLE_STATUSES - - unknown = scope - available_statuses - render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty? - - builds.where(status: available_statuses && scope) - end - - def authorize_read_builds! - authorize! :read_build, user_project - end - - def authorize_update_builds! - authorize! :update_build, user_project - end - end - end -end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 98ef9d4118e..22e2c4088d7 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -49,7 +49,8 @@ module API class ProjectHook < Hook expose :project_id, :issues_events, :merge_requests_events - expose :note_events, :build_events, :pipeline_events, :wiki_page_events + expose :note_events, :pipeline_events, :wiki_page_events + expose :job_events, as: :build_events end class BasicProjectDetails < Grape::Entity @@ -80,7 +81,7 @@ module API expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) } expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) } expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } - expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } + expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } expose :created_at, :last_activity_at @@ -93,7 +94,7 @@ module API expose :star_count, :forks_count expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } - expose :public_builds + expose :public_jobs, as: :public_builds expose :shared_with_groups do |project, options| SharedGroup.represent(project.project_group_links.all, options) end @@ -109,7 +110,7 @@ module API expose :storage_size expose :repository_size expose :lfs_objects_size - expose :build_artifacts_size + expose :job_artifacts_size, as: :build_artifacts_size end class Member < UserBasic @@ -144,7 +145,7 @@ module API expose :storage_size expose :repository_size expose :lfs_objects_size - expose :build_artifacts_size + expose :job_artifacts_size, as: :build_artifacts_size end end end @@ -448,7 +449,8 @@ module API class ProjectService < Grape::Entity expose :id, :title, :created_at, :updated_at, :active expose :push_events, :issues_events, :merge_requests_events - expose :tag_push_events, :note_events, :build_events, :pipeline_events + expose :tag_push_events, :note_events, :pipeline_events + expose :job_events, as: :build_events # Expose serialized properties expose :properties do |service, options| field_names = service.fields. @@ -616,11 +618,15 @@ module API end end +<<<<<<< HEAD class RunnerRegistrationDetails < Grape::Entity expose :id, :token end class BuildArtifactFile < Grape::Entity +======= + class JobArtifactFile < Grape::Entity +>>>>>>> 239b5f49c5... Rename Builds to Jobs in the API expose :filename, :size end @@ -628,11 +634,11 @@ module API expose :id, :sha, :ref, :status end - class Build < Grape::Entity + class Job < Grape::Entity expose :id, :status, :stage, :name, :ref, :tag, :coverage expose :created_at, :started_at, :finished_at expose :user, with: User - expose :artifacts_file, using: BuildArtifactFile, if: -> (build, opts) { build.artifacts? } + expose :artifacts_file, using: JobArtifactFile, if: -> (build, opts) { build.artifacts? } expose :commit, with: RepoCommit expose :runner, with: Runner expose :pipeline, with: PipelineBasic @@ -670,7 +676,7 @@ module API expose :id, :iid, :ref, :sha, :created_at expose :user, using: Entities::UserBasic expose :environment, using: Entities::EnvironmentBasic - expose :deployable, using: Entities::Build + expose :deployable, using: Entities::Job end class RepoLicense < Grape::Entity diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb new file mode 100644 index 00000000000..b48574e173f --- /dev/null +++ b/lib/api/jobs.rb @@ -0,0 +1,262 @@ +module API + class Jobs < Grape::API + include PaginationParams + + before { authenticate! } + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + helpers do + params :optional_scope do + optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', + values: ::CommitStatus::AVAILABLE_STATUSES, + coerce_with: ->(scope) { + case scope + when String + [scope] + when Hashie::Mash + scope.values + else + ['unknown'] + end + } + end + end + + desc 'Get a projects jobs' do + success Entities::Job + end + params do + use :optional_scope + use :pagination + end + get ':id/jobs' do + builds = user_project.builds.order('id DESC') + builds = filter_builds(builds, params[:scope]) + + present paginate(builds), with: Entities::Job, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Get jobs for a specific commit of a project' do + success Entities::Job + end + params do + requires :sha, type: String, desc: 'The SHA id of a commit' + use :optional_scope + use :pagination + end + get ':id/repository/commits/:sha/jobs' do + authorize_read_builds! + + return not_found! unless user_project.commit(params[:sha]) + + pipelines = user_project.pipelines.where(sha: params[:sha]) + builds = user_project.builds.where(pipeline: pipelines).order('id DESC') + builds = filter_builds(builds, params[:scope]) + + present paginate(builds), with: Entities::Job, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Get a specific job of a project' do + success Entities::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + get ':id/jobs/:job_id' do + authorize_read_builds! + + build = get_build!(params[:job_id]) + + present build, with: Entities::Job, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Download the artifacts file from a job' do + detail 'This feature was introduced in GitLab 8.5' + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + get ':id/jobs/:job_id/artifacts' do + authorize_read_builds! + + build = get_build!(params[:job_id]) + + present_artifacts!(build.artifacts_file) + end + + desc 'Download the artifacts file from a job' do + detail 'This feature was introduced in GitLab 8.10' + end + params do + requires :ref_name, type: String, desc: 'The ref from repository' + requires :job, type: String, desc: 'The name for the job' + end + get ':id/jobs/artifacts/:ref_name/download', + requirements: { ref_name: /.+/ } do + authorize_read_builds! + + builds = user_project.latest_successful_builds_for(params[:ref_name]) + latest_build = builds.find_by!(name: params[:job]) + + present_artifacts!(latest_build.artifacts_file) + end + + # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace + # is saved in the DB instead of file). But before that, we need to consider how to replace the value of + # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. + desc 'Get a trace of a specific job of a project' + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + get ':id/jobs/:job_id/trace' do + authorize_read_builds! + + build = get_build!(params[:job_id]) + + header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" + content_type 'text/plain' + env['api.format'] = :binary + + trace = build.trace + body trace + end + + desc 'Cancel a specific job of a project' do + success Entities::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + post ':id/jobs/:job_id/cancel' do + authorize_update_builds! + + build = get_build!(params[:job_id]) + + build.cancel + + present build, with: Entities::Job, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Retry a specific build of a project' do + success Entities::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a build' + end + post ':id/jobs/:job_id/retry' do + authorize_update_builds! + + build = get_build!(params[:job_id]) + return forbidden!('Job is not retryable') unless build.retryable? + + build = Ci::Build.retry(build, current_user) + + present build, with: Entities::Job, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Erase job (remove artifacts and the trace)' do + success Entities::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a build' + end + post ':id/jobs/:job_id/erase' do + authorize_update_builds! + + build = get_build!(params[:job_id]) + return forbidden!('Job is not erasable!') unless build.erasable? + + build.erase(erased_by: current_user) + present build, with: Entities::Job, + user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) + end + + desc 'Keep the artifacts to prevent them from being deleted' do + success Entities::Job + end + params do + requires :job_id, type: Integer, desc: 'The ID of a job' + end + post ':id/jobs/:job_id/artifacts/keep' do + authorize_update_builds! + + build = get_build!(params[:job_id]) + return not_found!(build) unless build.artifacts? + + build.keep_artifacts! + + status 200 + present build, with: Entities::Job, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Trigger a manual job' do + success Entities::Job + detail 'This feature was added in GitLab 8.11' + end + params do + requires :job_id, type: Integer, desc: 'The ID of a Job' + end + post ":id/jobs/:job_id/play" do + authorize_read_builds! + + build = get_build!(params[:job_id]) + + bad_request!("Unplayable Job") unless build.playable? + + build.play(current_user) + + status 200 + present build, with: Entities::Job, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + end + + helpers do + def get_build(id) + user_project.builds.find_by(id: id.to_i) + end + + def get_build!(id) + get_build(id) || not_found! + end + + def present_artifacts!(artifacts_file) + if !artifacts_file.file_storage? + redirect_to(build.artifacts_file.url) + elsif artifacts_file.exists? + present_file!(artifacts_file.path, artifacts_file.filename) + else + not_found! + end + end + + def filter_builds(builds, scope) + return builds if scope.nil? || scope.empty? + + available_statuses = ::CommitStatus::AVAILABLE_STATUSES + + unknown = scope - available_statuses + render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty? + + builds.where(status: available_statuses && scope) + end + + def authorize_read_builds! + authorize! :read_build, user_project + end + + def authorize_update_builds! + authorize! :update_build, user_project + end + end + end +end diff --git a/lib/api/v3/builds.rb b/lib/api/v3/builds.rb new file mode 100644 index 00000000000..33f9cfa6927 --- /dev/null +++ b/lib/api/v3/builds.rb @@ -0,0 +1,263 @@ +module API + module V3 + class Builds < Grape::API + include PaginationParams + + before { authenticate! } + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + helpers do + params :optional_scope do + optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', + values: ['pending', 'running', 'failed', 'success', 'canceled'], + coerce_with: ->(scope) { + if scope.is_a?(String) + [scope] + elsif scope.is_a?(Hashie::Mash) + scope.values + else + ['unknown'] + end + } + end + end + + desc 'Get a project builds' do + success V3::Entities::Build + end + params do + use :optional_scope + use :pagination + end + get ':id/builds' do + builds = user_project.builds.order('id DESC') + builds = filter_builds(builds, params[:scope]) + + present paginate(builds), with: Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Get builds for a specific commit of a project' do + success Entities::Build + end + params do + requires :sha, type: String, desc: 'The SHA id of a commit' + use :optional_scope + use :pagination + end + get ':id/repository/commits/:sha/builds' do + authorize_read_builds! + + return not_found! unless user_project.commit(params[:sha]) + + pipelines = user_project.pipelines.where(sha: params[:sha]) + builds = user_project.builds.where(pipeline: pipelines).order('id DESC') + builds = filter_builds(builds, params[:scope]) + + present paginate(builds), with: Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Get a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end + get ':id/builds/:build_id' do + authorize_read_builds! + + build = get_build!(params[:build_id]) + + present build, with: Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Download the artifacts file from build' do + detail 'This feature was introduced in GitLab 8.5' + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end + get ':id/builds/:build_id/artifacts' do + authorize_read_builds! + + build = get_build!(params[:build_id]) + + present_artifacts!(build.artifacts_file) + end + + desc 'Download the artifacts file from build' do + detail 'This feature was introduced in GitLab 8.10' + end + params do + requires :ref_name, type: String, desc: 'The ref from repository' + requires :job, type: String, desc: 'The name for the build' + end + get ':id/builds/artifacts/:ref_name/download', + requirements: { ref_name: /.+/ } do + authorize_read_builds! + + builds = user_project.latest_successful_builds_for(params[:ref_name]) + latest_build = builds.find_by!(name: params[:job]) + + present_artifacts!(latest_build.artifacts_file) + end + + # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace + # is saved in the DB instead of file). But before that, we need to consider how to replace the value of + # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. + desc 'Get a trace of a specific build of a project' + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end + get ':id/builds/:build_id/trace' do + authorize_read_builds! + + build = get_build!(params[:build_id]) + + header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" + content_type 'text/plain' + env['api.format'] = :binary + + trace = build.trace + body trace + end + + desc 'Cancel a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end + post ':id/builds/:build_id/cancel' do + authorize_update_builds! + + build = get_build!(params[:build_id]) + + build.cancel + + present build, with: Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Retry a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end + post ':id/builds/:build_id/retry' do + authorize_update_builds! + + build = get_build!(params[:build_id]) + return forbidden!('Build is not retryable') unless build.retryable? + + build = Ci::Build.retry(build, current_user) + + present build, with: Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Erase build (remove artifacts and build trace)' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end + post ':id/builds/:build_id/erase' do + authorize_update_builds! + + build = get_build!(params[:build_id]) + return forbidden!('Build is not erasable!') unless build.erasable? + + build.erase(erased_by: current_user) + present build, with: Entities::Build, + user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) + end + + desc 'Keep the artifacts to prevent them from being deleted' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end + post ':id/builds/:build_id/artifacts/keep' do + authorize_update_builds! + + build = get_build!(params[:build_id]) + return not_found!(build) unless build.artifacts? + + build.keep_artifacts! + + status 200 + present build, with: Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + + desc 'Trigger a manual build' do + success Entities::Build + detail 'This feature was added in GitLab 8.11' + end + params do + requires :build_id, type: Integer, desc: 'The ID of a Build' + end + post ":id/builds/:build_id/play" do + authorize_read_builds! + + build = get_build!(params[:build_id]) + + bad_request!("Unplayable Job") unless build.playable? + + build.play(current_user) + + status 200 + present build, with: Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) + end + end + + helpers do + def get_build(id) + user_project.builds.find_by(id: id.to_i) + end + + def get_build!(id) + get_build(id) || not_found! + end + + def present_artifacts!(artifacts_file) + if !artifacts_file.file_storage? + redirect_to(build.artifacts_file.url) + elsif artifacts_file.exists? + present_file!(artifacts_file.path, artifacts_file.filename) + else + not_found! + end + end + + def filter_builds(builds, scope) + return builds if scope.nil? || scope.empty? + + available_statuses = ::CommitStatus::AVAILABLE_STATUSES + + unknown = scope - available_statuses + render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty? + + builds.where(status: available_statuses && scope) + end + + def authorize_read_builds! + authorize! :read_build, user_project + end + + def authorize_update_builds! + authorize! :update_build, user_project + end + end + end + end +end diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 69853d33bec..7c7bcad490b 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -195,6 +195,16 @@ module API class TriggerRequest < Grape::Entity expose :id, :variables end + + class Build < Grape::Entity + expose :id, :status, :stage, :name, :ref, :tag, :coverage + expose :created_at, :started_at, :finished_at + expose :user, with: ::API::Entities::User + expose :artifacts_file, using: ::API::Entities::JobArtifactFile, if: -> (build, opts) { build.artifacts? } + expose :commit, with: ::API::Entities::RepoCommit + expose :runner, with: ::API::Entities::Runner + expose :pipeline, with: ::API::Entities::PipelineBasic + end end end end -- cgit v1.2.3 From b5b93f80e57437163c45f8fef056a2a0b58e643e Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 23 Feb 2017 09:16:20 +0100 Subject: Update entities, rename from builds to jobs This commit only renames the commits, the cascading effects will be dealt with later. --- lib/api/entities.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 22e2c4088d7..3db67ff455b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -50,7 +50,7 @@ module API class ProjectHook < Hook expose :project_id, :issues_events, :merge_requests_events expose :note_events, :pipeline_events, :wiki_page_events - expose :job_events, as: :build_events + expose :build_events, as: :job_events end class BasicProjectDetails < Grape::Entity @@ -94,7 +94,7 @@ module API expose :star_count, :forks_count expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } - expose :public_jobs, as: :public_builds + expose :public_builds, as: :public_jobs expose :shared_with_groups do |project, options| SharedGroup.represent(project.project_group_links.all, options) end @@ -110,7 +110,7 @@ module API expose :storage_size expose :repository_size expose :lfs_objects_size - expose :job_artifacts_size, as: :build_artifacts_size + expose :build_artifacts_size, as: :job_artifacts_size end class Member < UserBasic @@ -145,7 +145,7 @@ module API expose :storage_size expose :repository_size expose :lfs_objects_size - expose :job_artifacts_size, as: :build_artifacts_size + expose :build_artifacts_size, as: :job_artifacts_size end end end @@ -450,7 +450,7 @@ module API expose :id, :title, :created_at, :updated_at, :active expose :push_events, :issues_events, :merge_requests_events expose :tag_push_events, :note_events, :pipeline_events - expose :job_events, as: :build_events + expose :build_events, as: :job_events # Expose serialized properties expose :properties do |service, options| field_names = service.fields. @@ -618,15 +618,11 @@ module API end end -<<<<<<< HEAD class RunnerRegistrationDetails < Grape::Entity expose :id, :token end - class BuildArtifactFile < Grape::Entity -======= class JobArtifactFile < Grape::Entity ->>>>>>> 239b5f49c5... Rename Builds to Jobs in the API expose :filename, :size end -- cgit v1.2.3 From 0fb022773892c76c8a6d7a8cf049d9c6fc2c8891 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 23 Feb 2017 12:14:27 +0100 Subject: Keep entities the same for API v3 --- lib/api/v3/entities.rb | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) (limited to 'lib') diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 7c7bcad490b..7b81d159b06 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -205,6 +205,149 @@ module API expose :runner, with: ::API::Entities::Runner expose :pipeline, with: ::API::Entities::PipelineBasic end + + class BuildArtifactFile < Grape::Entity + expose :filename, :size + end + + class EnvironmentBasic < Grape::Entity + expose :id, :name, :slug, :external_url + end + + class Environment < EnvironmentBasic + expose :project, using: Entities::Project + end + + class Deployment < Grape::Entity + expose :id, :iid, :ref, :sha, :created_at + expose :user, using: Entities::UserBasic + expose :environment, using: Entities::EnvironmentBasic + expose :deployable, using: Entities::Build + end + + class Group < Grape::Entity + expose :id, :name, :path, :description, :visibility_level + expose :lfs_enabled?, as: :lfs_enabled + expose :avatar_url + expose :web_url + expose :request_access_enabled + expose :statistics, if: :statistics do + with_options format_with: -> (value) { value.to_i } do + expose :storage_size + expose :repository_size + expose :lfs_objects_size + expose :build_artifacts_size + end + end + end + + class GroupDetail < Group + expose :projects, using: Entities::Project + expose :shared_projects, using: Entities::Project + end + + class MergeRequest < ProjectEntity + expose :target_branch, :source_branch + expose :upvotes, :downvotes + expose :author, :assignee, using: Entities::UserBasic + expose :source_project_id, :target_project_id + expose :label_names, as: :labels + expose :work_in_progress?, as: :work_in_progress + expose :milestone, using: Entities::Milestone + expose :merge_when_build_succeeds + expose :merge_status + expose :diff_head_sha, as: :sha + expose :merge_commit_sha + expose :subscribed do |merge_request, options| + merge_request.subscribed?(options[:current_user], options[:project]) + end + expose :user_notes_count + expose :should_remove_source_branch?, as: :should_remove_source_branch + expose :force_remove_source_branch?, as: :force_remove_source_branch + expose :web_url do |merge_request, options| + Gitlab::UrlBuilder.build(merge_request) + end + end + + class MergeRequestChanges < MergeRequest + expose :diffs, as: :changes, using: Entities::RepoDiff do |compare, _| + compare.raw_diffs(all_diffs: true).to_a + end + end + + class Project < Grape::Entity + expose :id, :description, :default_branch, :tag_list + expose :public?, as: :public + expose :archived?, as: :archived + expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url + expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } + expose :name, :name_with_namespace + expose :path, :path_with_namespace + expose :container_registry_enabled + # Expose old field names with the new permissions methods to keep API compatible + expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) } + expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) } + expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } + expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } + expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } + expose :created_at, :last_activity_at + expose :shared_runners_enabled + expose :lfs_enabled?, as: :lfs_enabled + expose :creator_id + expose :namespace, using: 'API::Entities::Namespace' + expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? } + expose :avatar_url + expose :star_count, :forks_count + expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? } + expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } + expose :public_builds + expose :shared_with_groups do |project, options| + SharedGroup.represent(project.project_group_links.all, options) + end + expose :only_allow_merge_if_build_succeeds + expose :request_access_enabled + expose :only_allow_merge_if_all_discussions_are_resolved + expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics + end + + class ProjectStatistics < Grape::Entity + expose :commit_count + expose :storage_size + expose :repository_size + expose :lfs_objects_size + expose :build_artifacts_size + end + + class ProjectService < Grape::Entity + expose :id, :title, :created_at, :updated_at, :active + expose :push_events, :issues_events, :merge_requests_events + expose :tag_push_events, :note_events, :build_events, :pipeline_events + # Expose serialized properties + expose :properties do |service, options| + field_names = service.fields. + select { |field| options[:include_passwords] || field[:type] != 'password' }. + map { |field| field[:name] } + service.properties.slice(*field_names) + end + end + + class ProjectHook < Hook + expose :project_id, :issues_events, :merge_requests_events + expose :note_events, :build_events, :pipeline_events, :wiki_page_events + end + + class ProjectWithAccess < Project + expose :permissions do + expose :project_access, using: Entities::ProjectAccess do |project, options| + project.project_members.find_by(user_id: options[:current_user].id) + end + expose :group_access, using: Entities::GroupAccess do |project, options| + if project.group + project.group.group_members.find_by(user_id: options[:current_user].id) + end + end + end + end end end end -- cgit v1.2.3 From f44ab8e8ec49e643cd7fea20092b63e2603bb8bd Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 23 Feb 2017 12:41:07 +0100 Subject: Pick API files from 8.16.6 --- lib/api/v3/deployments.rb | 41 +++++++++++++++ lib/api/v3/merge_request_diffs.rb | 41 +++++++++++++++ lib/api/v3/project_hooks.rb | 104 ++++++++++++++++++++++++++++++++++++++ lib/api/v3/services.rb | 53 ++++++++++++++++++- 4 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 lib/api/v3/deployments.rb create mode 100644 lib/api/v3/merge_request_diffs.rb create mode 100644 lib/api/v3/project_hooks.rb (limited to 'lib') diff --git a/lib/api/v3/deployments.rb b/lib/api/v3/deployments.rb new file mode 100644 index 00000000000..c5feb49b22f --- /dev/null +++ b/lib/api/v3/deployments.rb @@ -0,0 +1,41 @@ +module API + # Deployments RESTfull API endpoints + class Deployments < Grape::API + include PaginationParams + + before { authenticate! } + + params do + requires :id, type: String, desc: 'The project ID' + end + resource :projects do + desc 'Get all deployments of the project' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Deployment + end + params do + use :pagination + end + get ':id/deployments' do + authorize! :read_deployment, user_project + + present paginate(user_project.deployments), with: Entities::Deployment + end + + desc 'Gets a specific deployment' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Deployment + end + params do + requires :deployment_id, type: Integer, desc: 'The deployment ID' + end + get ':id/deployments/:deployment_id' do + authorize! :read_deployment, user_project + + deployment = user_project.deployments.find(params[:deployment_id]) + + present deployment, with: Entities::Deployment + end + end + end +end diff --git a/lib/api/v3/merge_request_diffs.rb b/lib/api/v3/merge_request_diffs.rb new file mode 100644 index 00000000000..bc3d69f6904 --- /dev/null +++ b/lib/api/v3/merge_request_diffs.rb @@ -0,0 +1,41 @@ +module API + # MergeRequestDiff API + class MergeRequestDiffs < Grape::API + before { authenticate! } + + resource :projects do + desc 'Get a list of merge request diff versions' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::MergeRequestDiff + end + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + end + + get ":id/merge_requests/:merge_request_id/versions" do + merge_request = find_merge_request_with_access(params[:merge_request_id]) + + present merge_request.merge_request_diffs, with: Entities::MergeRequestDiff + end + + desc 'Get a single merge request diff version' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::MergeRequestDiffFull + end + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + requires :version_id, type: Integer, desc: 'The ID of a merge request diff version' + end + + get ":id/merge_requests/:merge_request_id/versions/:version_id" do + merge_request = find_merge_request_with_access(params[:merge_request_id]) + + present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull + end + end + end +end diff --git a/lib/api/v3/project_hooks.rb b/lib/api/v3/project_hooks.rb new file mode 100644 index 00000000000..cb679e6658a --- /dev/null +++ b/lib/api/v3/project_hooks.rb @@ -0,0 +1,104 @@ +module API + class ProjectHooks < Grape::API + include PaginationParams + + before { authenticate! } + before { authorize_admin_project } + + helpers do + params :project_hook_properties do + requires :url, type: String, desc: "The URL to send the request to" + optional :push_events, type: Boolean, desc: "Trigger hook on push events" + optional :issues_events, type: Boolean, desc: "Trigger hook on issues events" + optional :merge_requests_events, type: Boolean, desc: "Trigger hook on merge request events" + optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events" + optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events" + optional :build_events, type: Boolean, desc: "Trigger hook on build events" + optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events" + optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events" + optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook" + optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response" + end + end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + desc 'Get project hooks' do + success Entities::ProjectHook + end + params do + use :pagination + end + get ":id/hooks" do + hooks = paginate user_project.hooks + + present hooks, with: Entities::ProjectHook + end + + desc 'Get a project hook' do + success Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: 'The ID of a project hook' + end + get ":id/hooks/:hook_id" do + hook = user_project.hooks.find(params[:hook_id]) + present hook, with: Entities::ProjectHook + end + + desc 'Add hook to project' do + success Entities::ProjectHook + end + params do + use :project_hook_properties + end + post ":id/hooks" do + hook = user_project.hooks.new(declared_params(include_missing: false)) + + if hook.save + present hook, with: Entities::ProjectHook + else + error!("Invalid url given", 422) if hook.errors[:url].present? + + not_found!("Project hook #{hook.errors.messages}") + end + end + + desc 'Update an existing project hook' do + success Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: "The ID of the hook to update" + use :project_hook_properties + end + put ":id/hooks/:hook_id" do + hook = user_project.hooks.find(params.delete(:hook_id)) + + if hook.update_attributes(declared_params(include_missing: false)) + present hook, with: Entities::ProjectHook + else + error!("Invalid url given", 422) if hook.errors[:url].present? + + not_found!("Project hook #{hook.errors.messages}") + end + end + + desc 'Deletes project hook' do + success Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: 'The ID of the hook to delete' + end + delete ":id/hooks/:hook_id" do + begin + present user_project.hooks.destroy(params[:hook_id]), with: Entities::ProjectHook + rescue + # ProjectHook can raise Error if hook_id not found + not_found!("Error deleting hook #{params[:hook_id]}") + end + end + end + end +end diff --git a/lib/api/v3/services.rb b/lib/api/v3/services.rb index af0a058f69b..de24e6418c7 100644 --- a/lib/api/v3/services.rb +++ b/lib/api/v3/services.rb @@ -561,13 +561,62 @@ module API end if service.update_attributes(attrs.merge(active: false)) - status(200) true else render_api_error!('400 Bad Request', 400) end end + + desc 'Get the service settings for project' do + success Entities::ProjectService + end + params do + requires :service_slug, type: String, values: services.keys, desc: 'The name of the service' + end + get ":id/services/:service_slug" do + service = user_project.find_or_initialize_service(params[:service_slug].underscore) + present service, with: Entities::ProjectService, include_passwords: current_user.is_admin? + end + end + + trigger_services.each do |service_slug, settings| + helpers do + def chat_command_service(project, service_slug, params) + project.services.active.where(template: false).find do |service| + service.try(:token) == params[:token] && service.to_param == service_slug.underscore + end + end + end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + desc "Trigger a slash command for #{service_slug}" do + detail 'Added in GitLab 8.13' + end + params do + settings.each do |setting| + requires setting[:name], type: setting[:type], desc: setting[:desc] + end + end + post ":id/services/#{service_slug.underscore}/trigger" do + project = find_project(params[:id]) + + # This is not accurate, but done to prevent leakage of the project names + not_found!('Service') unless project + + service = chat_command_service(project, service_slug, params) + result = service.try(:trigger, params) + + if result + status result[:status] || 200 + present result + else + not_found!('Service') + end + end + end end end end -end -- cgit v1.2.3 From 9e942b59720a4e22a16f71de66a8cf4706f3c92b Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Fri, 3 Mar 2017 16:39:29 +0100 Subject: Fix all tests This commit was about 6 commits before squashing, with the main goal to make all tests green. Now, after pushing this commit we'll see what the CI has to say about that. --- lib/api/v3/builds.rb | 68 ++++++++-------- lib/api/v3/entities.rb | 110 ++----------------------- lib/api/v3/merge_request_diffs.rb | 74 ++++++++--------- lib/api/v3/project_hooks.rb | 164 +++++++++++++++++++------------------- lib/api/v3/services.rb | 19 +++++ 5 files changed, 179 insertions(+), 256 deletions(-) (limited to 'lib') diff --git a/lib/api/v3/builds.rb b/lib/api/v3/builds.rb index 33f9cfa6927..c8feba13527 100644 --- a/lib/api/v3/builds.rb +++ b/lib/api/v3/builds.rb @@ -12,21 +12,21 @@ module API helpers do params :optional_scope do optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', - values: ['pending', 'running', 'failed', 'success', 'canceled'], - coerce_with: ->(scope) { - if scope.is_a?(String) - [scope] - elsif scope.is_a?(Hashie::Mash) - scope.values - else - ['unknown'] - end - } + values: %w(pending running failed success canceled skipped), + coerce_with: ->(scope) { + if scope.is_a?(String) + [scope] + elsif scope.is_a?(Hashie::Mash) + scope.values + else + ['unknown'] + end + } end end desc 'Get a project builds' do - success V3::Entities::Build + success ::API::V3::Entities::Build end params do use :optional_scope @@ -36,12 +36,12 @@ module API builds = user_project.builds.order('id DESC') builds = filter_builds(builds, params[:scope]) - present paginate(builds), with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) + present paginate(builds), with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) end desc 'Get builds for a specific commit of a project' do - success Entities::Build + success ::API::V3::Entities::Build end params do requires :sha, type: String, desc: 'The SHA id of a commit' @@ -57,12 +57,12 @@ module API builds = user_project.builds.where(pipeline: pipelines).order('id DESC') builds = filter_builds(builds, params[:scope]) - present paginate(builds), with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) + present paginate(builds), with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) end desc 'Get a specific build of a project' do - success Entities::Build + success ::API::V3::Entities::Build end params do requires :build_id, type: Integer, desc: 'The ID of a build' @@ -72,8 +72,8 @@ module API build = get_build!(params[:build_id]) - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) + present build, with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) end desc 'Download the artifacts file from build' do @@ -128,7 +128,7 @@ module API end desc 'Cancel a specific build of a project' do - success Entities::Build + success ::API::V3::Entities::Build end params do requires :build_id, type: Integer, desc: 'The ID of a build' @@ -140,12 +140,12 @@ module API build.cancel - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) + present build, with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) end desc 'Retry a specific build of a project' do - success Entities::Build + success ::API::V3::Entities::Build end params do requires :build_id, type: Integer, desc: 'The ID of a build' @@ -158,12 +158,12 @@ module API build = Ci::Build.retry(build, current_user) - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) + present build, with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) end desc 'Erase build (remove artifacts and build trace)' do - success Entities::Build + success ::API::V3::Entities::Build end params do requires :build_id, type: Integer, desc: 'The ID of a build' @@ -175,12 +175,12 @@ module API return forbidden!('Build is not erasable!') unless build.erasable? build.erase(erased_by: current_user) - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) + present build, with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) end desc 'Keep the artifacts to prevent them from being deleted' do - success Entities::Build + success ::API::V3::Entities::Build end params do requires :build_id, type: Integer, desc: 'The ID of a build' @@ -194,12 +194,12 @@ module API build.keep_artifacts! status 200 - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) + present build, with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) end desc 'Trigger a manual build' do - success Entities::Build + success ::API::V3::Entities::Build detail 'This feature was added in GitLab 8.11' end params do @@ -215,8 +215,8 @@ module API build.play(current_user) status 200 - present build, with: Entities::Build, - user_can_download_artifacts: can?(current_user, :read_build, user_project) + present build, with: ::API::V3::Entities::Build, + user_can_download_artifacts: can?(current_user, :read_build, user_project) end end diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 7b81d159b06..832b4bdeb4f 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -81,7 +81,7 @@ module API expose :request_access_enabled expose :only_allow_merge_if_all_discussions_are_resolved - expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics + expose :statistics, using: '::API::V3::Entities::ProjectStatistics', if: :statistics end class ProjectWithAccess < Project @@ -210,106 +210,19 @@ module API expose :filename, :size end - class EnvironmentBasic < Grape::Entity - expose :id, :name, :slug, :external_url - end - - class Environment < EnvironmentBasic - expose :project, using: Entities::Project - end - class Deployment < Grape::Entity expose :id, :iid, :ref, :sha, :created_at - expose :user, using: Entities::UserBasic - expose :environment, using: Entities::EnvironmentBasic + expose :user, using: ::API::Entities::UserBasic + expose :environment, using: ::API::Entities::EnvironmentBasic expose :deployable, using: Entities::Build end - class Group < Grape::Entity - expose :id, :name, :path, :description, :visibility_level - expose :lfs_enabled?, as: :lfs_enabled - expose :avatar_url - expose :web_url - expose :request_access_enabled - expose :statistics, if: :statistics do - with_options format_with: -> (value) { value.to_i } do - expose :storage_size - expose :repository_size - expose :lfs_objects_size - expose :build_artifacts_size - end - end - end - - class GroupDetail < Group - expose :projects, using: Entities::Project - expose :shared_projects, using: Entities::Project - end - - class MergeRequest < ProjectEntity - expose :target_branch, :source_branch - expose :upvotes, :downvotes - expose :author, :assignee, using: Entities::UserBasic - expose :source_project_id, :target_project_id - expose :label_names, as: :labels - expose :work_in_progress?, as: :work_in_progress - expose :milestone, using: Entities::Milestone - expose :merge_when_build_succeeds - expose :merge_status - expose :diff_head_sha, as: :sha - expose :merge_commit_sha - expose :subscribed do |merge_request, options| - merge_request.subscribed?(options[:current_user], options[:project]) - end - expose :user_notes_count - expose :should_remove_source_branch?, as: :should_remove_source_branch - expose :force_remove_source_branch?, as: :force_remove_source_branch - expose :web_url do |merge_request, options| - Gitlab::UrlBuilder.build(merge_request) - end - end - class MergeRequestChanges < MergeRequest - expose :diffs, as: :changes, using: Entities::RepoDiff do |compare, _| + expose :diffs, as: :changes, using: ::API::Entities::RepoDiff do |compare, _| compare.raw_diffs(all_diffs: true).to_a end end - class Project < Grape::Entity - expose :id, :description, :default_branch, :tag_list - expose :public?, as: :public - expose :archived?, as: :archived - expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url - expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } - expose :name, :name_with_namespace - expose :path, :path_with_namespace - expose :container_registry_enabled - # Expose old field names with the new permissions methods to keep API compatible - expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) } - expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) } - expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } - expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } - expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } - expose :created_at, :last_activity_at - expose :shared_runners_enabled - expose :lfs_enabled?, as: :lfs_enabled - expose :creator_id - expose :namespace, using: 'API::Entities::Namespace' - expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? } - expose :avatar_url - expose :star_count, :forks_count - expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? } - expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } - expose :public_builds - expose :shared_with_groups do |project, options| - SharedGroup.represent(project.project_group_links.all, options) - end - expose :only_allow_merge_if_build_succeeds - expose :request_access_enabled - expose :only_allow_merge_if_all_discussions_are_resolved - expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics - end - class ProjectStatistics < Grape::Entity expose :commit_count expose :storage_size @@ -331,23 +244,10 @@ module API end end - class ProjectHook < Hook + class ProjectHook < ::API::Entities::Hook expose :project_id, :issues_events, :merge_requests_events expose :note_events, :build_events, :pipeline_events, :wiki_page_events end - - class ProjectWithAccess < Project - expose :permissions do - expose :project_access, using: Entities::ProjectAccess do |project, options| - project.project_members.find_by(user_id: options[:current_user].id) - end - expose :group_access, using: Entities::GroupAccess do |project, options| - if project.group - project.group.group_members.find_by(user_id: options[:current_user].id) - end - end - end - end end end end diff --git a/lib/api/v3/merge_request_diffs.rb b/lib/api/v3/merge_request_diffs.rb index bc3d69f6904..a462803e26c 100644 --- a/lib/api/v3/merge_request_diffs.rb +++ b/lib/api/v3/merge_request_diffs.rb @@ -1,40 +1,42 @@ module API - # MergeRequestDiff API - class MergeRequestDiffs < Grape::API - before { authenticate! } - - resource :projects do - desc 'Get a list of merge request diff versions' do - detail 'This feature was introduced in GitLab 8.12.' - success Entities::MergeRequestDiff - end - - params do - requires :id, type: String, desc: 'The ID of a project' - requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' - end - - get ":id/merge_requests/:merge_request_id/versions" do - merge_request = find_merge_request_with_access(params[:merge_request_id]) - - present merge_request.merge_request_diffs, with: Entities::MergeRequestDiff - end - - desc 'Get a single merge request diff version' do - detail 'This feature was introduced in GitLab 8.12.' - success Entities::MergeRequestDiffFull - end - - params do - requires :id, type: String, desc: 'The ID of a project' - requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' - requires :version_id, type: Integer, desc: 'The ID of a merge request diff version' - end - - get ":id/merge_requests/:merge_request_id/versions/:version_id" do - merge_request = find_merge_request_with_access(params[:merge_request_id]) - - present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull + module V3 + # MergeRequestDiff API + class MergeRequestDiffs < Grape::API + before { authenticate! } + + resource :projects do + desc 'Get a list of merge request diff versions' do + detail 'This feature was introduced in GitLab 8.12.' + success ::API::Entities::MergeRequestDiff + end + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + end + + get ":id/merge_requests/:merge_request_id/versions" do + merge_request = find_merge_request_with_access(params[:merge_request_id]) + + present merge_request.merge_request_diffs, with: ::API::Entities::MergeRequestDiff + end + + desc 'Get a single merge request diff version' do + detail 'This feature was introduced in GitLab 8.12.' + success ::API::Entities::MergeRequestDiffFull + end + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + requires :version_id, type: Integer, desc: 'The ID of a merge request diff version' + end + + get ":id/merge_requests/:merge_request_id/versions/:version_id" do + merge_request = find_merge_request_with_access(params[:merge_request_id]) + + present merge_request.merge_request_diffs.find(params[:version_id]), with: ::API::Entities::MergeRequestDiffFull + end end end end diff --git a/lib/api/v3/project_hooks.rb b/lib/api/v3/project_hooks.rb index cb679e6658a..861b991b8e1 100644 --- a/lib/api/v3/project_hooks.rb +++ b/lib/api/v3/project_hooks.rb @@ -1,102 +1,104 @@ module API - class ProjectHooks < Grape::API - include PaginationParams + module V3 + class ProjectHooks < Grape::API + include PaginationParams - before { authenticate! } - before { authorize_admin_project } + before { authenticate! } + before { authorize_admin_project } - helpers do - params :project_hook_properties do - requires :url, type: String, desc: "The URL to send the request to" - optional :push_events, type: Boolean, desc: "Trigger hook on push events" - optional :issues_events, type: Boolean, desc: "Trigger hook on issues events" - optional :merge_requests_events, type: Boolean, desc: "Trigger hook on merge request events" - optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events" - optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events" - optional :build_events, type: Boolean, desc: "Trigger hook on build events" - optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events" - optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events" - optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook" - optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response" + helpers do + params :project_hook_properties do + requires :url, type: String, desc: "The URL to send the request to" + optional :push_events, type: Boolean, desc: "Trigger hook on push events" + optional :issues_events, type: Boolean, desc: "Trigger hook on issues events" + optional :merge_requests_events, type: Boolean, desc: "Trigger hook on merge request events" + optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events" + optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events" + optional :build_events, type: Boolean, desc: "Trigger hook on build events" + optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events" + optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events" + optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook" + optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response" + end end - end - params do - requires :id, type: String, desc: 'The ID of a project' - end - resource :projects do - desc 'Get project hooks' do - success Entities::ProjectHook - end params do - use :pagination + requires :id, type: String, desc: 'The ID of a project' end - get ":id/hooks" do - hooks = paginate user_project.hooks + resource :projects do + desc 'Get project hooks' do + success ::API::V3::Entities::ProjectHook + end + params do + use :pagination + end + get ":id/hooks" do + hooks = paginate user_project.hooks - present hooks, with: Entities::ProjectHook - end + present hooks, with: ::API::V3::Entities::ProjectHook + end - desc 'Get a project hook' do - success Entities::ProjectHook - end - params do - requires :hook_id, type: Integer, desc: 'The ID of a project hook' - end - get ":id/hooks/:hook_id" do - hook = user_project.hooks.find(params[:hook_id]) - present hook, with: Entities::ProjectHook - end + desc 'Get a project hook' do + success ::API::V3::Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: 'The ID of a project hook' + end + get ":id/hooks/:hook_id" do + hook = user_project.hooks.find(params[:hook_id]) + present hook, with: ::API::V3::Entities::ProjectHook + end - desc 'Add hook to project' do - success Entities::ProjectHook - end - params do - use :project_hook_properties - end - post ":id/hooks" do - hook = user_project.hooks.new(declared_params(include_missing: false)) + desc 'Add hook to project' do + success ::API::V3::Entities::ProjectHook + end + params do + use :project_hook_properties + end + post ":id/hooks" do + hook = user_project.hooks.new(declared_params(include_missing: false)) - if hook.save - present hook, with: Entities::ProjectHook - else - error!("Invalid url given", 422) if hook.errors[:url].present? + if hook.save + present hook, with: ::API::V3::Entities::ProjectHook + else + error!("Invalid url given", 422) if hook.errors[:url].present? - not_found!("Project hook #{hook.errors.messages}") + not_found!("Project hook #{hook.errors.messages}") + end end - end - desc 'Update an existing project hook' do - success Entities::ProjectHook - end - params do - requires :hook_id, type: Integer, desc: "The ID of the hook to update" - use :project_hook_properties - end - put ":id/hooks/:hook_id" do - hook = user_project.hooks.find(params.delete(:hook_id)) + desc 'Update an existing project hook' do + success ::API::V3::Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: "The ID of the hook to update" + use :project_hook_properties + end + put ":id/hooks/:hook_id" do + hook = user_project.hooks.find(params.delete(:hook_id)) - if hook.update_attributes(declared_params(include_missing: false)) - present hook, with: Entities::ProjectHook - else - error!("Invalid url given", 422) if hook.errors[:url].present? + if hook.update_attributes(declared_params(include_missing: false)) + present hook, with: ::API::V3::Entities::ProjectHook + else + error!("Invalid url given", 422) if hook.errors[:url].present? - not_found!("Project hook #{hook.errors.messages}") + not_found!("Project hook #{hook.errors.messages}") + end end - end - desc 'Deletes project hook' do - success Entities::ProjectHook - end - params do - requires :hook_id, type: Integer, desc: 'The ID of the hook to delete' - end - delete ":id/hooks/:hook_id" do - begin - present user_project.hooks.destroy(params[:hook_id]), with: Entities::ProjectHook - rescue - # ProjectHook can raise Error if hook_id not found - not_found!("Error deleting hook #{params[:hook_id]}") + desc 'Deletes project hook' do + success ::API::V3::Entities::ProjectHook + end + params do + requires :hook_id, type: Integer, desc: 'The ID of the hook to delete' + end + delete ":id/hooks/:hook_id" do + begin + present user_project.hooks.destroy(params[:hook_id]), with: ::API::V3::Entities::ProjectHook + rescue + # ProjectHook can raise Error if hook_id not found + not_found!("Error deleting hook #{params[:hook_id]}") + end end end end diff --git a/lib/api/v3/services.rb b/lib/api/v3/services.rb index de24e6418c7..d77185ffe5a 100644 --- a/lib/api/v3/services.rb +++ b/lib/api/v3/services.rb @@ -537,6 +537,23 @@ module API ] } + trigger_services = { + 'mattermost-slash-commands' => [ + { + name: :token, + type: String, + desc: 'The Mattermost token' + } + ], + 'slack-slash-commands' => [ + { + name: :token, + type: String, + desc: 'The Slack token' + } + ] + }.freeze + resource :projects do before { authenticate! } before { authorize_admin_project } @@ -561,6 +578,7 @@ module API end if service.update_attributes(attrs.merge(active: false)) + status(200) true else render_api_error!('400 Bad Request', 400) @@ -620,3 +638,4 @@ module API end end end +end -- cgit v1.2.3 From e25bb81be2227feff54bd9e9a490994f32004d56 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Mon, 6 Mar 2017 09:31:37 +0100 Subject: Add changelog entry [ci skip] --- lib/api/entities.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3db67ff455b..e0de5aeddee 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -634,7 +634,7 @@ module API expose :id, :status, :stage, :name, :ref, :tag, :coverage expose :created_at, :started_at, :finished_at expose :user, with: User - expose :artifacts_file, using: JobArtifactFile, if: -> (build, opts) { build.artifacts? } + expose :artifacts_file, using: JobArtifactFile, if: -> (job, opts) { job.artifacts? } expose :commit, with: RepoCommit expose :runner, with: Runner expose :pipeline, with: PipelineBasic -- cgit v1.2.3 From 194223476b8df57a56ba096c7a300d51d2e3e750 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Mon, 6 Mar 2017 10:24:03 +0100 Subject: Rename build to job in the docs --- lib/api/services.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/api/services.rb b/lib/api/services.rb index 79a5f27dc4d..1cf29d9a1a3 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -122,9 +122,9 @@ module API }, { required: false, - name: :notify_only_broken_builds, + name: :notify_only_broken_jobs, type: Boolean, - desc: 'Notify only broken builds' + desc: 'Notify only broken jobs' } ], 'campfire' => [ @@ -403,9 +403,9 @@ module API }, { required: false, - name: :notify_only_broken_builds, + name: :notify_only_broken_jobs, type: Boolean, - desc: 'Notify only broken builds' + desc: 'Notify only broken jobs' } ], 'pivotaltracker' => [ @@ -611,7 +611,7 @@ module API desc "Set #{service_slug} service for project" params do service_classes.each do |service| - event_names = service.try(:event_names) || [] + event_names = service.try(:event_names) || next event_names.each do |event_name| services[service.to_param.tr("_", "-")] << { required: false, -- cgit v1.2.3 From 75123fa9f706a6ab7cdd5dba4393ad9d8bac0947 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Mon, 6 Mar 2017 11:50:32 +0100 Subject: Incorporate review, drop old endpoint The endpoint dropped, get ':id/repository/commits/:sha/jobs', should be replace by a new endpoint. --- lib/api/jobs.rb | 21 --------------------- lib/api/v3/deployments.rb | 8 ++++---- 2 files changed, 4 insertions(+), 25 deletions(-) (limited to 'lib') diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb index b48574e173f..33c05e8aa63 100644 --- a/lib/api/jobs.rb +++ b/lib/api/jobs.rb @@ -40,27 +40,6 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - desc 'Get jobs for a specific commit of a project' do - success Entities::Job - end - params do - requires :sha, type: String, desc: 'The SHA id of a commit' - use :optional_scope - use :pagination - end - get ':id/repository/commits/:sha/jobs' do - authorize_read_builds! - - return not_found! unless user_project.commit(params[:sha]) - - pipelines = user_project.pipelines.where(sha: params[:sha]) - builds = user_project.builds.where(pipeline: pipelines).order('id DESC') - builds = filter_builds(builds, params[:scope]) - - present paginate(builds), with: Entities::Job, - user_can_download_artifacts: can?(current_user, :read_build, user_project) - end - desc 'Get a specific job of a project' do success Entities::Job end diff --git a/lib/api/v3/deployments.rb b/lib/api/v3/deployments.rb index c5feb49b22f..545485fac0a 100644 --- a/lib/api/v3/deployments.rb +++ b/lib/api/v3/deployments.rb @@ -11,7 +11,7 @@ module API resource :projects do desc 'Get all deployments of the project' do detail 'This feature was introduced in GitLab 8.11.' - success Entities::Deployment + success ::API::V3::Deployments end params do use :pagination @@ -19,12 +19,12 @@ module API get ':id/deployments' do authorize! :read_deployment, user_project - present paginate(user_project.deployments), with: Entities::Deployment + present paginate(user_project.deployments), with: ::API::V3::Deployments end desc 'Gets a specific deployment' do detail 'This feature was introduced in GitLab 8.11.' - success Entities::Deployment + success ::API::V3::Deployments end params do requires :deployment_id, type: Integer, desc: 'The deployment ID' @@ -34,7 +34,7 @@ module API deployment = user_project.deployments.find(params[:deployment_id]) - present deployment, with: Entities::Deployment + present deployment, with: ::API::V3::Deployments end end end -- cgit v1.2.3 From e5cf3f51fb568361a247d715facb6cd9bb15bb16 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 6 Feb 2017 13:48:46 +0100 Subject: Allow limiting logging in users from too many different IPs. --- lib/gitlab/auth.rb | 22 ++++++----- lib/gitlab/auth/unique_ips_limiter.rb | 70 +++++++++++++++++++++++++++++++++++ lib/gitlab/request_context.rb | 25 +++++++++++++ 3 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 lib/gitlab/auth/unique_ips_limiter.rb create mode 100644 lib/gitlab/request_context.rb (limited to 'lib') diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 0a5abc92190..be055080853 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -22,23 +22,27 @@ module Gitlab user_with_password_for_git(login, password) || Gitlab::Auth::Result.new + Gitlab::Auth::UniqueIpsLimiter.limit_user! { result.actor } + rate_limit!(ip, success: result.success?, login: login) result end def find_with_user_password(login, password) - user = User.by_login(login) + Gitlab::Auth::UniqueIpsLimiter.limit_user! do + user = User.by_login(login) - # If no user is found, or it's an LDAP server, try LDAP. - # LDAP users are only authenticated via LDAP - if user.nil? || user.ldap_user? - # Second chance - try LDAP authentication - return nil unless Gitlab::LDAP::Config.enabled? + # If no user is found, or it's an LDAP server, try LDAP. + # LDAP users are only authenticated via LDAP + if user.nil? || user.ldap_user? + # Second chance - try LDAP authentication + return nil unless Gitlab::LDAP::Config.enabled? - Gitlab::LDAP::Authentication.login(login, password) - else - user if user.valid_password?(password) + Gitlab::LDAP::Authentication.login(login, password) + else + user if user.valid_password?(password) + end end end diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb new file mode 100644 index 00000000000..21307eb35e4 --- /dev/null +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -0,0 +1,70 @@ +module Gitlab + module Auth + class TooManyIps < StandardError + attr_reader :user_id, :ip, :unique_ips_count + + def initialize(user_id, ip, unique_ips_count) + @user_id = user_id + @ip = ip + @unique_ips_count = unique_ips_count + end + + def message + "User #{user_id} from IP: #{ip} tried logging from too many ips: #{unique_ips_count}" + end + end + + class UniqueIpsLimiter + USER_UNIQUE_IPS_PREFIX = 'user_unique_ips' + + class << self + def limit_user_id!(user_id) + if config.unique_ips_limit_enabled + ip = RequestContext.client_ip + unique_ips = count_unique_ips(user_id, ip) + raise TooManyIps.new(user_id, ip, unique_ips) if unique_ips > config.unique_ips_limit_per_user + end + end + + def limit_user!(user = nil) + user = yield if user.nil? + limit_user_id!(user.id) unless user.nil? + user + end + + def config + Gitlab::CurrentSettings.current_application_settings + end + + def count_unique_ips(user_id, ip) + time = Time.now.to_i + key = "#{USER_UNIQUE_IPS_PREFIX}:#{user_id}" + + Gitlab::Redis.with do |redis| + unique_ips_count = nil + redis.multi do |r| + r.zadd(key, time, ip) + r.zremrangebyscore(key, 0, time - config.unique_ips_limit_time_window) + unique_ips_count = r.zcard(key) + end + unique_ips_count.value + end + end + end + + def initialize(app) + @app = app + end + + def call(env) + begin + @app.call(env) + rescue TooManyIps => ex + + Rails.logger.info ex.message + [429, {'Content-Type' => 'text/plain', 'Retry-After' => UniqueIpsLimiter.config.unique_ips_limit_time_window }, ["Retry later\n"]] + end + end + end + end +end diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb new file mode 100644 index 00000000000..5daf04dc92b --- /dev/null +++ b/lib/gitlab/request_context.rb @@ -0,0 +1,25 @@ +module Gitlab + class RequestStoreNotActive < StandardError + end + + class RequestContext + class << self + def client_ip + RequestStore[:client_ip] + end + end + + def initialize(app) + @app = app + end + + def call(env) + raise RequestStoreNotActive.new unless RequestStore.active? + req = Rack::Request.new(env) + + RequestStore[:client_ip] = req.ip + + @app.call(env) + end + end +end \ No newline at end of file -- cgit v1.2.3 From 66dc71599cb698d380e14be7230ae3495c78d266 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 16 Feb 2017 11:21:30 +0100 Subject: Cleanup formatting --- lib/gitlab/auth/unique_ips_limiter.rb | 2 +- lib/gitlab/request_context.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb index 21307eb35e4..01850ae31e8 100644 --- a/lib/gitlab/auth/unique_ips_limiter.rb +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -62,7 +62,7 @@ module Gitlab rescue TooManyIps => ex Rails.logger.info ex.message - [429, {'Content-Type' => 'text/plain', 'Retry-After' => UniqueIpsLimiter.config.unique_ips_limit_time_window }, ["Retry later\n"]] + [429, { 'Content-Type' => 'text/plain', 'Retry-After' => UniqueIpsLimiter.config.unique_ips_limit_time_window }, ["Retry later\n"]] end end end diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb index 5daf04dc92b..36a5d94d98a 100644 --- a/lib/gitlab/request_context.rb +++ b/lib/gitlab/request_context.rb @@ -22,4 +22,4 @@ module Gitlab @app.call(env) end end -end \ No newline at end of file +end -- cgit v1.2.3 From 8993801f0cefdc64b46b8fe30622cc78eaa03173 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Fri, 17 Feb 2017 12:52:27 +0100 Subject: Test various login scenarios if the limit gets enforced --- lib/api/api.rb | 4 ++++ lib/api/helpers.rb | 15 ++++++++------- lib/gitlab/auth.rb | 2 +- lib/gitlab/auth/unique_ips_limiter.rb | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index 89449ce8813..6f37fa9d8e9 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -60,6 +60,10 @@ module API error! e.message, e.status, e.headers end + rescue_from Gitlab::Auth::TooManyIps do |e| + rack_response({'message'=>'403 Forbidden'}.to_json, 403) + end + rescue_from :all do |exception| handle_api_exception(exception) end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index a43252a4661..f325f0a3050 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -336,16 +336,17 @@ module API def initial_current_user return @initial_current_user if defined?(@initial_current_user) + Gitlab::Auth::UniqueIpsLimiter.limit_user! do + @initial_current_user ||= find_user_by_private_token(scopes: @scopes) + @initial_current_user ||= doorkeeper_guard(scopes: @scopes) + @initial_current_user ||= find_user_from_warden - @initial_current_user ||= find_user_by_private_token(scopes: @scopes) - @initial_current_user ||= doorkeeper_guard(scopes: @scopes) - @initial_current_user ||= find_user_from_warden + unless @initial_current_user && Gitlab::UserAccess.new(@initial_current_user).allowed? + @initial_current_user = nil + end - unless @initial_current_user && Gitlab::UserAccess.new(@initial_current_user).allowed? - @initial_current_user = nil + @initial_current_user end - - @initial_current_user end def sudo! diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index be055080853..8e2aee2d7a0 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -22,7 +22,7 @@ module Gitlab user_with_password_for_git(login, password) || Gitlab::Auth::Result.new - Gitlab::Auth::UniqueIpsLimiter.limit_user! { result.actor } + Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor) rate_limit!(ip, success: result.success?, login: login) diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb index 01850ae31e8..7f849ef4c38 100644 --- a/lib/gitlab/auth/unique_ips_limiter.rb +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -62,7 +62,7 @@ module Gitlab rescue TooManyIps => ex Rails.logger.info ex.message - [429, { 'Content-Type' => 'text/plain', 'Retry-After' => UniqueIpsLimiter.config.unique_ips_limit_time_window }, ["Retry later\n"]] + [403, { 'Content-Type' => 'text/plain', 'Retry-After' => UniqueIpsLimiter.config.unique_ips_limit_time_window }, ["Too many logins from different IPs\n"]] end end end -- cgit v1.2.3 From 9cc0ff8f468c54e23172492d97f6d9b428d3ad2e Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Fri, 17 Feb 2017 14:44:57 +0100 Subject: Cleanup common code in Unique Ips tests --- lib/api/api.rb | 2 +- lib/gitlab/auth/unique_ips_limiter.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index 6f37fa9d8e9..efbb56ecd2c 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -61,7 +61,7 @@ module API end rescue_from Gitlab::Auth::TooManyIps do |e| - rack_response({'message'=>'403 Forbidden'}.to_json, 403) + rack_response({ 'message' => '403 Forbidden' }.to_json, 403) end rescue_from :all do |exception| diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb index 7f849ef4c38..4b2b758be8a 100644 --- a/lib/gitlab/auth/unique_ips_limiter.rb +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -27,7 +27,7 @@ module Gitlab end def limit_user!(user = nil) - user = yield if user.nil? + user = yield if user.nil? && block_given? limit_user_id!(user.id) unless user.nil? user end -- cgit v1.2.3 From 0ef8a643489ad1a3da4431155326f0a6e206d870 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 20 Feb 2017 15:09:05 +0100 Subject: Remove unecessary calls to limit_user!, UniqueIps Middleware, and address MR review - cleanup formating in haml - clarify time window is in seconds - cleanup straneous chunks in db/schema - rename count_uniqe_ips to update_and_return_ips_count - other --- lib/gitlab/auth.rb | 3 +-- lib/gitlab/auth/too_many_ips.rb | 17 +++++++++++++++++ lib/gitlab/auth/unique_ips_limiter.rb | 34 +++------------------------------- lib/gitlab/request_context.rb | 1 - 4 files changed, 21 insertions(+), 34 deletions(-) create mode 100644 lib/gitlab/auth/too_many_ips.rb (limited to 'lib') diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index 8e2aee2d7a0..0a0bd0e781c 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -22,9 +22,8 @@ module Gitlab user_with_password_for_git(login, password) || Gitlab::Auth::Result.new - Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor) - rate_limit!(ip, success: result.success?, login: login) + Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor) result end diff --git a/lib/gitlab/auth/too_many_ips.rb b/lib/gitlab/auth/too_many_ips.rb new file mode 100644 index 00000000000..ed862791551 --- /dev/null +++ b/lib/gitlab/auth/too_many_ips.rb @@ -0,0 +1,17 @@ +module Gitlab + module Auth + class TooManyIps < StandardError + attr_reader :user_id, :ip, :unique_ips_count + + def initialize(user_id, ip, unique_ips_count) + @user_id = user_id + @ip = ip + @unique_ips_count = unique_ips_count + end + + def message + "User #{user_id} from IP: #{ip} tried logging from too many ips: #{unique_ips_count}" + end + end + end +end diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb index 4b2b758be8a..7b1aa736769 100644 --- a/lib/gitlab/auth/unique_ips_limiter.rb +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -1,19 +1,5 @@ module Gitlab module Auth - class TooManyIps < StandardError - attr_reader :user_id, :ip, :unique_ips_count - - def initialize(user_id, ip, unique_ips_count) - @user_id = user_id - @ip = ip - @unique_ips_count = unique_ips_count - end - - def message - "User #{user_id} from IP: #{ip} tried logging from too many ips: #{unique_ips_count}" - end - end - class UniqueIpsLimiter USER_UNIQUE_IPS_PREFIX = 'user_unique_ips' @@ -21,7 +7,7 @@ module Gitlab def limit_user_id!(user_id) if config.unique_ips_limit_enabled ip = RequestContext.client_ip - unique_ips = count_unique_ips(user_id, ip) + unique_ips = update_and_return_ips_count(user_id, ip) raise TooManyIps.new(user_id, ip, unique_ips) if unique_ips > config.unique_ips_limit_per_user end end @@ -36,8 +22,8 @@ module Gitlab Gitlab::CurrentSettings.current_application_settings end - def count_unique_ips(user_id, ip) - time = Time.now.to_i + def update_and_return_ips_count(user_id, ip) + time = Time.now.utc.to_i key = "#{USER_UNIQUE_IPS_PREFIX}:#{user_id}" Gitlab::Redis.with do |redis| @@ -51,20 +37,6 @@ module Gitlab end end end - - def initialize(app) - @app = app - end - - def call(env) - begin - @app.call(env) - rescue TooManyIps => ex - - Rails.logger.info ex.message - [403, { 'Content-Type' => 'text/plain', 'Retry-After' => UniqueIpsLimiter.config.unique_ips_limit_time_window }, ["Too many logins from different IPs\n"]] - end - end end end end diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb index 36a5d94d98a..548adf4775a 100644 --- a/lib/gitlab/request_context.rb +++ b/lib/gitlab/request_context.rb @@ -14,7 +14,6 @@ module Gitlab end def call(env) - raise RequestStoreNotActive.new unless RequestStore.active? req = Rack::Request.new(env) RequestStore[:client_ip] = req.ip -- cgit v1.2.3 From 8a9bc24ef87739580c19ee8455bd8224d3c18b3e Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 28 Feb 2017 11:12:11 +0100 Subject: align schema.rb with upstream and fix rubocop warning about not freezing mutable constants and empty error classes --- lib/gitlab/auth/unique_ips_limiter.rb | 2 +- lib/gitlab/request_context.rb | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb index 7b1aa736769..4d401eb1b5d 100644 --- a/lib/gitlab/auth/unique_ips_limiter.rb +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -1,7 +1,7 @@ module Gitlab module Auth class UniqueIpsLimiter - USER_UNIQUE_IPS_PREFIX = 'user_unique_ips' + USER_UNIQUE_IPS_PREFIX = 'user_unique_ips'.freeze class << self def limit_user_id!(user_id) diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb index 548adf4775a..1dce18d1733 100644 --- a/lib/gitlab/request_context.rb +++ b/lib/gitlab/request_context.rb @@ -1,6 +1,5 @@ module Gitlab - class RequestStoreNotActive < StandardError - end + RequestStoreNotActive = Class.new(StandardError) class RequestContext class << self -- cgit v1.2.3 From 70b9d8da4c24bc2317220bedb81b5d2ecf34c351 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Fri, 3 Mar 2017 18:10:22 +0100 Subject: Remove unecessary defaults for uniq ip block, cleanup refactoring leftovers --- lib/gitlab/auth/unique_ips_limiter.rb | 3 ++- lib/gitlab/request_context.rb | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb index 4d401eb1b5d..bf2239ca150 100644 --- a/lib/gitlab/auth/unique_ips_limiter.rb +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -8,12 +8,13 @@ module Gitlab if config.unique_ips_limit_enabled ip = RequestContext.client_ip unique_ips = update_and_return_ips_count(user_id, ip) + raise TooManyIps.new(user_id, ip, unique_ips) if unique_ips > config.unique_ips_limit_per_user end end def limit_user!(user = nil) - user = yield if user.nil? && block_given? + user ||= yield if block_given? limit_user_id!(user.id) unless user.nil? user end diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb index 1dce18d1733..fef536ecb0b 100644 --- a/lib/gitlab/request_context.rb +++ b/lib/gitlab/request_context.rb @@ -1,6 +1,4 @@ module Gitlab - RequestStoreNotActive = Class.new(StandardError) - class RequestContext class << self def client_ip -- cgit v1.2.3 From 00f5cb84d55036542165c756e235619631bc7dee Mon Sep 17 00:00:00 2001 From: James Date: Wed, 28 Sep 2016 12:46:11 +0100 Subject: SanitizationFilter allows html5 details and summary (Issue #21605) Also adds details/summary tags to Copy-as-GFM --- lib/banzai/filter/sanitization_filter.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index af1e575fc89..d5f9e252f62 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -35,6 +35,10 @@ module Banzai # Allow span elements whitelist[:elements].push('span') + # Allow html5 details/summary elements + whitelist[:elements].push('details') + whitelist[:elements].push('summary') + # Allow abbr elements with title attribute whitelist[:elements].push('abbr') whitelist[:attributes]['abbr'] = %w(title) -- cgit v1.2.3 From fa0c0bb00692c77c0d01865dc17ec2b5e71bcc91 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Thu, 2 Mar 2017 17:36:21 -0300 Subject: Narrow environment payload by using basic project details resource --- lib/api/entities.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 965f8fbab8f..2230aa0706b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -671,7 +671,7 @@ module API end class Environment < EnvironmentBasic - expose :project, using: Entities::Project + expose :project, using: Entities::BasicProjectDetails end class Deployment < Grape::Entity -- cgit v1.2.3 From 2f040916a1efb2c04a82d3e9936f0bcfe35ea7f6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 6 Mar 2017 12:19:15 -0500 Subject: Move `api/v3/deployments` to the correct namespace --- lib/api/deployments.rb | 2 +- lib/api/v3/deployments.rb | 58 ++++++++++++++++++++++++----------------------- 2 files changed, 31 insertions(+), 29 deletions(-) (limited to 'lib') diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb index c5feb49b22f..2f1ad12c38c 100644 --- a/lib/api/deployments.rb +++ b/lib/api/deployments.rb @@ -1,5 +1,5 @@ module API - # Deployments RESTfull API endpoints + # Deployments RESTful API endpoints class Deployments < Grape::API include PaginationParams diff --git a/lib/api/v3/deployments.rb b/lib/api/v3/deployments.rb index 545485fac0a..95114ad1fe1 100644 --- a/lib/api/v3/deployments.rb +++ b/lib/api/v3/deployments.rb @@ -1,40 +1,42 @@ module API - # Deployments RESTfull API endpoints - class Deployments < Grape::API - include PaginationParams + module V3 + # Deployments RESTful API endpoints + class Deployments < Grape::API + include PaginationParams - before { authenticate! } + before { authenticate! } - params do - requires :id, type: String, desc: 'The project ID' - end - resource :projects do - desc 'Get all deployments of the project' do - detail 'This feature was introduced in GitLab 8.11.' - success ::API::V3::Deployments - end params do - use :pagination + requires :id, type: String, desc: 'The project ID' end - get ':id/deployments' do - authorize! :read_deployment, user_project + resource :projects do + desc 'Get all deployments of the project' do + detail 'This feature was introduced in GitLab 8.11.' + success ::API::V3::Deployments + end + params do + use :pagination + end + get ':id/deployments' do + authorize! :read_deployment, user_project - present paginate(user_project.deployments), with: ::API::V3::Deployments - end + present paginate(user_project.deployments), with: ::API::V3::Deployments + end - desc 'Gets a specific deployment' do - detail 'This feature was introduced in GitLab 8.11.' - success ::API::V3::Deployments - end - params do - requires :deployment_id, type: Integer, desc: 'The deployment ID' - end - get ':id/deployments/:deployment_id' do - authorize! :read_deployment, user_project + desc 'Gets a specific deployment' do + detail 'This feature was introduced in GitLab 8.11.' + success ::API::V3::Deployments + end + params do + requires :deployment_id, type: Integer, desc: 'The deployment ID' + end + get ':id/deployments/:deployment_id' do + authorize! :read_deployment, user_project - deployment = user_project.deployments.find(params[:deployment_id]) + deployment = user_project.deployments.find(params[:deployment_id]) - present deployment, with: ::API::V3::Deployments + present deployment, with: ::API::V3::Deployments + end end end end -- cgit v1.2.3 From e6fc0207cb37666cdf606c03641f2afbb5646213 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Mon, 27 Feb 2017 22:44:34 -0600 Subject: Use native unicode emojis - gl_emoji for falling back to image/css-sprite when the browser doesn't support an emoji - Markdown rendering (Banzai filter) - Autocomplete - Award emoji menu - Perceived perf - Immediate response because we now build client-side - Update `digests.json` generation in gemojione rake task to be more useful and include `unicodeVersion` MR: !9437 See issues - #26371 - #27250 - #22474 --- lib/banzai/filter/emoji_filter.rb | 64 +++++----------------------- lib/gitlab/award_emoji.rb | 73 +------------------------------ lib/gitlab/emoji.rb | 25 ++++++++++- lib/gitlab/gon_helper.rb | 1 - lib/tasks/gemojione.rake | 90 ++++++++++++++++++++++++++++----------- 5 files changed, 102 insertions(+), 151 deletions(-) (limited to 'lib') diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb index a8c1ca0c60a..d6138816e70 100644 --- a/lib/banzai/filter/emoji_filter.rb +++ b/lib/banzai/filter/emoji_filter.rb @@ -17,8 +17,8 @@ module Banzai next unless content.include?(':') || node.text.match(emoji_unicode_pattern) - html = emoji_name_image_filter(content) - html = emoji_unicode_image_filter(html) + html = emoji_unicode_element_unicode_filter(content) + html = emoji_name_element_unicode_filter(html) next if html == content @@ -27,33 +27,30 @@ module Banzai doc end - # Replace :emoji: with corresponding images. + # Replace :emoji: with corresponding gl-emoji unicode. # # text - String text to replace :emoji: in. # - # Returns a String with :emoji: replaced with images. - def emoji_name_image_filter(text) + # Returns a String with :emoji: replaced with gl-emoji unicode. + def emoji_name_element_unicode_filter(text) text.gsub(emoji_pattern) do |match| name = $1 - emoji_image_tag(name, emoji_url(name)) + Gitlab::Emoji.gl_emoji_tag(name) end end - # Replace unicode emoji with corresponding images if they exist. + # Replace unicode emoji with corresponding gl-emoji unicode. # # text - String text to replace unicode emoji in. # - # Returns a String with unicode emoji replaced with images. - def emoji_unicode_image_filter(text) + # Returns a String with unicode emoji replaced with gl-emoji unicode. + def emoji_unicode_element_unicode_filter(text) text.gsub(emoji_unicode_pattern) do |moji| - emoji_image_tag(Gitlab::Emoji.emojis_by_moji[moji]['name'], emoji_unicode_url(moji)) + emoji_info = Gitlab::Emoji.emojis_by_moji[moji] + Gitlab::Emoji.gl_emoji_tag(emoji_info['name']) end end - def emoji_image_tag(emoji_name, emoji_url) - ":#{emoji_name}:" - end - # Build a regexp that matches all valid :emoji: names. def self.emoji_pattern @emoji_pattern ||= /:(#{Gitlab::Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):/ @@ -66,52 +63,13 @@ module Banzai private - def emoji_url(name) - emoji_path = emoji_filename(name) - - if context[:asset_host] - # Asset host is specified. - url_to_image(emoji_path) - elsif context[:asset_root] - # Gitlab url is specified - File.join(context[:asset_root], url_to_image(emoji_path)) - else - # All other cases - url_to_image(emoji_path) - end - end - - def emoji_unicode_url(moji) - emoji_unicode_path = emoji_unicode_filename(moji) - - if context[:asset_host] - url_to_image(emoji_unicode_path) - elsif context[:asset_root] - File.join(context[:asset_root], url_to_image(emoji_unicode_path)) - else - url_to_image(emoji_unicode_path) - end - end - - def url_to_image(image) - ActionController::Base.helpers.url_to_image(image) - end - def emoji_pattern self.class.emoji_pattern end - def emoji_filename(name) - "#{Gitlab::Emoji.emoji_filename(name)}.png" - end - def emoji_unicode_pattern self.class.emoji_unicode_pattern end - - def emoji_unicode_filename(name) - "#{Gitlab::Emoji.emoji_unicode_filename(name)}.png" - end end end end diff --git a/lib/gitlab/award_emoji.rb b/lib/gitlab/award_emoji.rb index 7555326d384..6f104850d34 100644 --- a/lib/gitlab/award_emoji.rb +++ b/lib/gitlab/award_emoji.rb @@ -1,84 +1,15 @@ module Gitlab class AwardEmoji - CATEGORIES = { - objects: "Objects", - travel: "Travel", - symbols: "Symbols", - nature: "Nature", - people: "People", - activity: "Activity", - flags: "Flags", - food: "Food" - }.with_indifferent_access - def self.normalize_emoji_name(name) aliases[name] || name end - def self.emoji_by_category - unless @emoji_by_category - @emoji_by_category = Hash.new { |h, key| h[key] = [] } - - emojis.each do |emoji_name, data| - data["name"] = emoji_name - - # Skip Fitzpatrick(tone) modifiers - next if data["category"] == "modifier" - - category = data["category"] - - @emoji_by_category[category] << data - end - - @emoji_by_category = @emoji_by_category.sort.to_h - end - - @emoji_by_category - end - def self.emojis - @emojis ||= - begin - json_path = File.join(Rails.root, 'fixtures', 'emojis', 'index.json' ) - JSON.parse(File.read(json_path)) - end + Gitlab::Emoji.emojis end def self.aliases - @aliases ||= - begin - json_path = File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json') - JSON.parse(File.read(json_path)) - end - end - - # Returns an Array of Emoji names and their asset URLs. - def self.urls - @urls ||= begin - path = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json') - # Construct the full asset path ourselves because - # ActionView::Helpers::AssetUrlHelper.asset_url is slow for hundreds - # of entries since it has to do a lot of extra work (e.g. regexps). - prefix = Gitlab::Application.config.assets.prefix - digest = Gitlab::Application.config.assets.digest - base = - if defined?(Gitlab::Application.config.relative_url_root) && Gitlab::Application.config.relative_url_root - Gitlab::Application.config.relative_url_root - else - '' - end - - JSON.parse(File.read(path)).map do |hash| - fname = - if digest - "#{hash['unicode']}-#{hash['digest']}" - else - hash['unicode'] - end - - { name: hash['name'], path: File.join(base, prefix, "#{fname}.png") } - end - end + Gitlab::Emoji.emojis_aliases end end end diff --git a/lib/gitlab/emoji.rb b/lib/gitlab/emoji.rb index bbbca8acc40..35712c214fc 100644 --- a/lib/gitlab/emoji.rb +++ b/lib/gitlab/emoji.rb @@ -1,7 +1,9 @@ module Gitlab module Emoji extend self - + @emoji_unicode_version = JSON.parse(File.read(File.absolute_path(File.dirname(__FILE__) + '/../../node_modules/emoji-unicode-version/emoji-unicode-version-map.json'))) + @emoji_aliases = JSON.parse(File.read(File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json'))) + def emojis Gemojione.index.instance_variable_get(:@emoji_by_name) end @@ -18,6 +20,10 @@ module Gitlab emojis.keys end + def emojis_aliases + @emoji_aliases + end + def emoji_filename(name) emojis[name]["unicode"] end @@ -25,5 +31,22 @@ module Gitlab def emoji_unicode_filename(moji) emojis_by_moji[moji]["unicode"] end + + def emoji_unicode_version(name) + @emoji_unicode_version[name] + end + + def emoji_image_tag(name, src) + ":#{name}:" + end + + # CSS sprite fallback takes precedence over image fallback + def gl_emoji_tag(name, sprite: false, force_fallback: false) + emoji_name = emojis_aliases[name] || name + emoji_info = emojis[emoji_name] + emoji_fallback_image_source = ActionController::Base.helpers.asset_url("emoji/#{emoji_info['name']}.png") + emoji_fallback_sprite_class = "emoji-#{emoji_name}" + "#{force_fallback && sprite === false ? emoji_image_tag(emoji_name, emoji_fallback_image_source) : emoji_info['moji']}" + end end end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index 9c384069661..57fc4eb7c18 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -7,7 +7,6 @@ module Gitlab gon.relative_url_root = Gitlab.config.gitlab.relative_url_root gon.shortcuts_path = help_page_path('shortcuts') gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class - gon.award_menu_url = emojis_path gon.katex_css_url = ActionController::Base.helpers.asset_path('katex.css') gon.katex_js_url = ActionController::Base.helpers.asset_path('katex.js') diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake index 993112aee3b..b8a6ba7e77c 100644 --- a/lib/tasks/gemojione.rake +++ b/lib/tasks/gemojione.rake @@ -5,29 +5,29 @@ namespace :gemojione do require 'json' dir = Gemojione.images_path - digests = [] - aliases = Hash.new { |hash, key| hash[key] = [] } - aliases_path = File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json') - - JSON.parse(File.read(aliases_path)).each do |alias_name, real_name| - aliases[real_name] << alias_name - end - - Gitlab::AwardEmoji.emojis.map do |name, emoji_hash| - fpath = File.join(dir, "#{emoji_hash['unicode']}.png") - digest = Digest::SHA256.file(fpath).hexdigest - - digests << { name: name, unicode: emoji_hash['unicode'], digest: digest } + resultant_emoji_map = {} + + Gitlab::Emoji.emojis.map do |name, emoji_hash| + # Ignore aliases + unless Gitlab::Emoji.emojis_aliases.key?(name) + fpath = File.join(dir, "#{emoji_hash['unicode']}.png") + hash_digest = Digest::SHA256.file(fpath).hexdigest + + entry = { + category: emoji_hash['category'], + moji: emoji_hash['moji'], + unicodeVersion: Gitlab::Emoji.emoji_unicode_version(name), + digest: hash_digest, + } - aliases[name].each do |alias_name| - digests << { name: alias_name, unicode: emoji_hash['unicode'], digest: digest } + resultant_emoji_map[name] = entry end end out = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json') File.open(out, 'w') do |handle| - handle.write(JSON.pretty_generate(digests)) + handle.write(JSON.pretty_generate(resultant_emoji_map)) end end @@ -55,21 +55,42 @@ namespace :gemojione do SPRITESHEET_WIDTH = 860 SPRITESHEET_HEIGHT = 840 + # Setup a map to rename image files + emoji_uncicode_string_to_name_map = {} + Gitlab::Emoji.emojis.map do |name, emoji_hash| + # Ignore aliases + unless Gitlab::Emoji.emojis_aliases.key?(name) + emoji_uncicode_string_to_name_map[emoji_hash['unicode']] = name + end + end + + # Copy the Gemojione assets to the temporary folder for renaming + emoji_dir = "app/assets/images/emoji" + FileUtils.rm_rf(emoji_dir) + FileUtils.mkdir_p(emoji_dir, mode: 0700) + FileUtils.cp_r(File.join(Gemojione.images_path, '.'), emoji_dir) + Dir.chdir(emoji_dir) do + Dir["**/*.png"].each do |png| + image_path = File.join(Dir.pwd, png) + rename_to_named_emoji_image!(emoji_uncicode_string_to_name_map, image_path) + end + end + Dir.mktmpdir do |tmpdir| - # Copy the Gemojione assets to the temporary folder for resizing - FileUtils.cp_r(Gemojione.images_path, tmpdir) + FileUtils.cp_r(File.join(emoji_dir, '.'), tmpdir) Dir.chdir(tmpdir) do Dir["**/*.png"].each do |png| - resize!(File.join(tmpdir, png), SIZE) + tmp_image_path = File.join(tmpdir, png) + resize!(tmp_image_path, SIZE) end end - style_path = Rails.root.join(*%w(app assets stylesheets pages emojis.scss)) + style_path = Rails.root.join(*%w(app assets stylesheets framework emoji-sprites.scss)) # Combine the resized assets into a packed sprite and re-generate the SCSS SpriteFactory.cssurl = "image-url('$IMAGE')" - SpriteFactory.run!(File.join(tmpdir, 'png'), { + SpriteFactory.run!(tmpdir, { output_style: style_path, output_image: "app/assets/images/emoji.png", selector: '.emoji-', @@ -83,7 +104,7 @@ namespace :gemojione do # let's simplify it system(%Q(sed -i '' "s/width: #{SIZE}px; height: #{SIZE}px; background: image-url('emoji.png')/background-position:/" #{style_path})) system(%Q(sed -i '' "s/ no-repeat//" #{style_path})) - system(%Q(sed -i '' "s/ 0px/ 0/" #{style_path})) + system(%Q(sed -i '' "s/ 0px/ 0/g" #{style_path})) # Append a generic rule that applies to all Emojis File.open(style_path, 'a') do |f| @@ -92,6 +113,8 @@ namespace :gemojione do .emoji-icon { background-image: image-url('emoji.png'); background-repeat: no-repeat; + color: transparent; + text-indent: -99em; height: #{SIZE}px; width: #{SIZE}px; @@ -112,16 +135,17 @@ namespace :gemojione do # Now do it again but for Retina Dir.mktmpdir do |tmpdir| # Copy the Gemojione assets to the temporary folder for resizing - FileUtils.cp_r(Gemojione.images_path, tmpdir) + FileUtils.cp_r(File.join(emoji_dir, '.'), tmpdir) Dir.chdir(tmpdir) do Dir["**/*.png"].each do |png| - resize!(File.join(tmpdir, png), RETINA) + tmp_image_path = File.join(tmpdir, png) + resize!(tmp_image_path, RETINA) end end # Combine the resized assets into a packed sprite and re-generate the SCSS - SpriteFactory.run!(File.join(tmpdir), { + SpriteFactory.run!(tmpdir, { output_image: "app/assets/images/emoji@2x.png", style: false, nocomments: true, @@ -155,4 +179,20 @@ namespace :gemojione do image.write(image_path) { self.quality = 100 } image.destroy! end + + EMOJI_IMAGE_PATH_RE = /(.*?)(([0-9a-f]-?)+)\.png$/i + def rename_to_named_emoji_image!(emoji_uncicode_string_to_name_map, image_path) + # Rename file from unicode to emoji name + matches = EMOJI_IMAGE_PATH_RE.match(image_path) + preceding_path = matches[1] + unicode_string = matches[2] + name = emoji_uncicode_string_to_name_map[unicode_string] + if name + new_png_path = File.join(preceding_path, "#{name}.png") + FileUtils.mv(image_path, new_png_path) + new_png_path + else + puts "Warning: emoji_uncicode_string_to_name_map missing entry for #{unicode_string}. Full path: #{image_path}" + end + end end -- cgit v1.2.3 From f602efea65c2a816c7e29be546d2eb412fe538cc Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 3 Mar 2017 11:56:02 -0600 Subject: Fix wrong image src with cached gl-emoji and relative root --- lib/gitlab/emoji.rb | 6 +++--- lib/gitlab/gon_helper.rb | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/emoji.rb b/lib/gitlab/emoji.rb index 35712c214fc..3ef92abd3cf 100644 --- a/lib/gitlab/emoji.rb +++ b/lib/gitlab/emoji.rb @@ -41,12 +41,12 @@ module Gitlab end # CSS sprite fallback takes precedence over image fallback - def gl_emoji_tag(name, sprite: false, force_fallback: false) + def gl_emoji_tag(name, image: false, sprite: false, force_fallback: false) emoji_name = emojis_aliases[name] || name emoji_info = emojis[emoji_name] - emoji_fallback_image_source = ActionController::Base.helpers.asset_url("emoji/#{emoji_info['name']}.png") + emoji_fallback_image_source = ActionController::Base.helpers.url_to_image("emoji/#{emoji_info['name']}.png") emoji_fallback_sprite_class = "emoji-#{emoji_name}" - "#{force_fallback && sprite === false ? emoji_image_tag(emoji_name, emoji_fallback_image_source) : emoji_info['moji']}" + "#{force_fallback && sprite === false ? emoji_image_tag(emoji_name, emoji_fallback_image_source) : emoji_info['moji']}" end end end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index 57fc4eb7c18..1cfede5460f 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -4,6 +4,7 @@ module Gitlab gon.api_version = 'v3' # v4 Is not officially released yet, therefore can't be considered as "frozen" gon.default_avatar_url = URI.join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s gon.max_file_size = current_application_settings.max_attachment_size + gon.asset_host = ActionController::Base.asset_host gon.relative_url_root = Gitlab.config.gitlab.relative_url_root gon.shortcuts_path = help_page_path('shortcuts') gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class -- cgit v1.2.3 From 96cbad23378e492f2564a8ec4121b7b3548a7df3 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Mon, 6 Mar 2017 11:59:26 -0600 Subject: Fix up @DouweM review --- lib/gitlab/award_emoji.rb | 15 --------------- lib/gitlab/emoji.rb | 28 +++++++++++++++++++++++----- lib/tasks/gemojione.rake | 22 ++++++++++------------ 3 files changed, 33 insertions(+), 32 deletions(-) delete mode 100644 lib/gitlab/award_emoji.rb (limited to 'lib') diff --git a/lib/gitlab/award_emoji.rb b/lib/gitlab/award_emoji.rb deleted file mode 100644 index 6f104850d34..00000000000 --- a/lib/gitlab/award_emoji.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Gitlab - class AwardEmoji - def self.normalize_emoji_name(name) - aliases[name] || name - end - - def self.emojis - Gitlab::Emoji.emojis - end - - def self.aliases - Gitlab::Emoji.emojis_aliases - end - end -end diff --git a/lib/gitlab/emoji.rb b/lib/gitlab/emoji.rb index 3ef92abd3cf..42703545c4f 100644 --- a/lib/gitlab/emoji.rb +++ b/lib/gitlab/emoji.rb @@ -1,8 +1,6 @@ module Gitlab module Emoji extend self - @emoji_unicode_version = JSON.parse(File.read(File.absolute_path(File.dirname(__FILE__) + '/../../node_modules/emoji-unicode-version/emoji-unicode-version-map.json'))) - @emoji_aliases = JSON.parse(File.read(File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json'))) def emojis Gemojione.index.instance_variable_get(:@emoji_by_name) @@ -21,7 +19,7 @@ module Gitlab end def emojis_aliases - @emoji_aliases + @emoji_aliases ||= JSON.parse(File.read(Rails.root.join('fixtures', 'emojis', 'aliases.json'))) end def emoji_filename(name) @@ -33,7 +31,12 @@ module Gitlab end def emoji_unicode_version(name) - @emoji_unicode_version[name] + @emoji_unicode_versions_by_name ||= JSON.parse(File.read(Rails.root.join('node_modules', 'emoji-unicode-version', 'emoji-unicode-version-map.json'))) + @emoji_unicode_versions_by_name[name] + end + + def normalize_emoji_name(name) + emojis_aliases[name] || name end def emoji_image_tag(name, src) @@ -46,7 +49,22 @@ module Gitlab emoji_info = emojis[emoji_name] emoji_fallback_image_source = ActionController::Base.helpers.url_to_image("emoji/#{emoji_info['name']}.png") emoji_fallback_sprite_class = "emoji-#{emoji_name}" - "#{force_fallback && sprite === false ? emoji_image_tag(emoji_name, emoji_fallback_image_source) : emoji_info['moji']}" + + data = { + name: emoji_name, + unicode_version: emoji_unicode_version(emoji_name) + } + data[:fallback_src] = emoji_fallback_image_source if image + data[:fallback_sprite_class] = emoji_fallback_sprite_class if sprite + ActionController::Base.helpers.content_tag 'gl-emoji', + class: ("emoji-icon #{emoji_fallback_sprite_class}" if force_fallback && sprite), + data: data do + if force_fallback && !sprite + emoji_image_tag(emoji_name, emoji_fallback_image_source) + else + emoji_info['moji'] + end + end end end end diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake index b8a6ba7e77c..1f93b5a4dd2 100644 --- a/lib/tasks/gemojione.rake +++ b/lib/tasks/gemojione.rake @@ -7,7 +7,7 @@ namespace :gemojione do dir = Gemojione.images_path resultant_emoji_map = {} - Gitlab::Emoji.emojis.map do |name, emoji_hash| + Gitlab::Emoji.emojis.each do |name, emoji_hash| # Ignore aliases unless Gitlab::Emoji.emojis_aliases.key?(name) fpath = File.join(dir, "#{emoji_hash['unicode']}.png") @@ -56,11 +56,11 @@ namespace :gemojione do SPRITESHEET_HEIGHT = 840 # Setup a map to rename image files - emoji_uncicode_string_to_name_map = {} - Gitlab::Emoji.emojis.map do |name, emoji_hash| + emoji_unicode_string_to_name_map = {} + Gitlab::Emoji.emojis.each do |name, emoji_hash| # Ignore aliases unless Gitlab::Emoji.emojis_aliases.key?(name) - emoji_uncicode_string_to_name_map[emoji_hash['unicode']] = name + emoji_unicode_string_to_name_map[emoji_hash['unicode']] = name end end @@ -69,11 +69,9 @@ namespace :gemojione do FileUtils.rm_rf(emoji_dir) FileUtils.mkdir_p(emoji_dir, mode: 0700) FileUtils.cp_r(File.join(Gemojione.images_path, '.'), emoji_dir) - Dir.chdir(emoji_dir) do - Dir["**/*.png"].each do |png| - image_path = File.join(Dir.pwd, png) - rename_to_named_emoji_image!(emoji_uncicode_string_to_name_map, image_path) - end + Dir[File.join(emoji_dir, "**/*.png")].each do |png| + image_path = png + rename_to_named_emoji_image!(emoji_unicode_string_to_name_map, image_path) end Dir.mktmpdir do |tmpdir| @@ -181,18 +179,18 @@ namespace :gemojione do end EMOJI_IMAGE_PATH_RE = /(.*?)(([0-9a-f]-?)+)\.png$/i - def rename_to_named_emoji_image!(emoji_uncicode_string_to_name_map, image_path) + def rename_to_named_emoji_image!(emoji_unicode_string_to_name_map, image_path) # Rename file from unicode to emoji name matches = EMOJI_IMAGE_PATH_RE.match(image_path) preceding_path = matches[1] unicode_string = matches[2] - name = emoji_uncicode_string_to_name_map[unicode_string] + name = emoji_unicode_string_to_name_map[unicode_string] if name new_png_path = File.join(preceding_path, "#{name}.png") FileUtils.mv(image_path, new_png_path) new_png_path else - puts "Warning: emoji_uncicode_string_to_name_map missing entry for #{unicode_string}. Full path: #{image_path}" + puts "Warning: emoji_unicode_string_to_name_map missing entry for #{unicode_string}. Full path: #{image_path}" end end end -- cgit v1.2.3 From 4f143aa8f255be6b7c5695868dbd51bd3684b202 Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Wed, 22 Feb 2017 18:26:32 +1100 Subject: re-add Assign to Me link on new MR/Issue forms --- lib/gitlab/gon_helper.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index 9c384069661..744fa1f9981 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -14,6 +14,7 @@ module Gitlab if current_user gon.current_user_id = current_user.id gon.current_username = current_user.username + gon.current_user_fullname = current_user.name end end end -- cgit v1.2.3