diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-10 18:10:16 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-10 18:10:16 +0000 |
commit | cba8ff64401258aa68eebd7603d4022884ed0a45 (patch) | |
tree | 39d46d3dc50523a36816942b8cf079daf93dfb70 /app | |
parent | 8f143a46faf2e7b594301512757edf372c294a0c (diff) | |
download | gitlab-ce-cba8ff64401258aa68eebd7603d4022884ed0a45.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
19 files changed, 281 insertions, 91 deletions
diff --git a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue index 902dd0b8eec..a5b594fbd88 100644 --- a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue +++ b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue @@ -50,7 +50,6 @@ export default { :aria-label="$options.SIMPLE_BLOB_VIEWER_TITLE" :title="$options.SIMPLE_BLOB_VIEWER_TITLE" :selected="isSimpleViewer" - :class="{ active: isSimpleViewer }" icon="code" category="primary" variant="default" @@ -61,7 +60,6 @@ export default { :aria-label="$options.RICH_BLOB_VIEWER_TITLE" :title="$options.RICH_BLOB_VIEWER_TITLE" :selected="isRichViewer" - :class="{ active: isRichViewer }" icon="document" category="primary" variant="default" diff --git a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue index e2fbb074fcd..de7fbc4241c 100644 --- a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue +++ b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue @@ -1,66 +1,77 @@ <script> import { mapState } from 'vuex'; -import { GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui'; -import { parseSortParam, buildSortUrl } from '~/members/utils'; +import { GlSorting, GlSortingItem } from '@gitlab/ui'; +import { visitUrl } from '~/lib/utils/url_utility'; +import { parseSortParam, buildSortHref } from '~/members/utils'; import { FIELDS } from '~/members/constants'; export default { name: 'SortDropdown', - components: { GlDropdown, GlDropdownItem, GlFormGroup }, + components: { GlSorting, GlSortingItem }, computed: { ...mapState(['tableSortableFields', 'filteredSearchBar']), sort() { return parseSortParam(this.tableSortableFields); }, + activeOption() { + return FIELDS.find(field => field.key === this.sort.sortByKey); + }, + activeOptionLabel() { + return this.activeOption?.label; + }, + isAscending() { + return !this.sort.sortDesc; + }, filteredOptions() { - const buildOption = (field, sortDesc) => ({ - ...(sortDesc ? field.sort.desc : field.sort.asc), - key: field.key, - sortDesc, - url: buildSortUrl({ - sortBy: field.key, - sortDesc, - filteredSearchBarTokens: this.filteredSearchBar.tokens, - filteredSearchBarSearchParam: this.filteredSearchBar.searchParam, + return FIELDS.filter(field => this.tableSortableFields.includes(field.key) && field.sort).map( + field => ({ + key: field.key, + label: field.label, + href: buildSortHref({ + sortBy: field.key, + sortDesc: false, + filteredSearchBarTokens: this.filteredSearchBar.tokens, + filteredSearchBarSearchParam: this.filteredSearchBar.searchParam, + }), }), - }); - - return FIELDS.filter( - field => this.tableSortableFields.includes(field.key) && field.sort, - ).flatMap(field => [buildOption(field, false), buildOption(field, true)]); + ); }, }, methods: { - isChecked(key, sortDesc) { - return this.sort?.sortBy === key && this.sort?.sortDesc === sortDesc; + isActive(key) { + return this.activeOption.key === key; + }, + handleSortDirectionChange() { + visitUrl( + buildSortHref({ + sortBy: this.activeOption.key, + sortDesc: !this.sort.sortDesc, + filteredSearchBarTokens: this.filteredSearchBar.tokens, + filteredSearchBarSearchParam: this.filteredSearchBar.searchParam, + }), + ); }, }, }; </script> <template> - <gl-form-group - :label="__('Sort by')" - class="gl-mb-0" - label-cols="auto" - label-class="gl-align-self-center gl-pb-0!" + <gl-sorting + class="gl-display-flex" + dropdown-class="gl-w-full" + data-testid="members-sort-dropdown" + :text="activeOptionLabel" + :is-ascending="isAscending" + :sort-direction-tool-tip="__('Sort direction')" + @sortDirectionChange="handleSortDirectionChange" > - <gl-dropdown - :text="sort.sortByLabel" - block - toggle-class="gl-mb-0" - data-testid="members-sort-dropdown" - right + <gl-sorting-item + v-for="option in filteredOptions" + :key="option.key" + :href="option.href" + :active="isActive(option.key)" > - <gl-dropdown-item - v-for="option in filteredOptions" - :key="option.param" - :href="option.url" - is-check-item - :is-checked="isChecked(option.key, option.sortDesc)" - > - {{ option.label }} - </gl-dropdown-item> - </gl-dropdown> - </gl-form-group> + {{ option.label }} + </gl-sorting-item> + </gl-sorting> </template> diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js index 874e934e5b0..21af825f795 100644 --- a/app/assets/javascripts/members/constants.js +++ b/app/assets/javascripts/members/constants.js @@ -1,20 +1,12 @@ -import { __, s__ } from '~/locale'; - -const ACCOUNT_SORT_ASC_LABEL = s__('Members|Account, ascending'); +import { __ } from '~/locale'; export const FIELDS = [ { key: 'account', label: __('Account'), sort: { - asc: { - param: 'name_asc', - label: ACCOUNT_SORT_ASC_LABEL, - }, - desc: { - param: 'name_desc', - label: s__('Members|Account, descending'), - }, + asc: 'name_asc', + desc: 'name_desc', }, }, { @@ -29,14 +21,8 @@ export const FIELDS = [ thClass: 'col-meta', tdClass: 'col-meta', sort: { - asc: { - param: 'last_joined', - label: s__('Members|Access granted, ascending'), - }, - desc: { - param: 'oldest_joined', - label: s__('Members|Access granted, descending'), - }, + asc: 'last_joined', + desc: 'oldest_joined', }, }, { @@ -63,14 +49,8 @@ export const FIELDS = [ thClass: 'col-max-role', tdClass: 'col-max-role', sort: { - asc: { - param: 'access_level_asc', - label: s__('Members|Max role, ascending'), - }, - desc: { - param: 'access_level_desc', - label: s__('Members|Max role, descending'), - }, + asc: 'access_level_asc', + desc: 'access_level_desc', }, }, { @@ -81,15 +61,10 @@ export const FIELDS = [ }, { key: 'lastSignIn', + label: __('Last sign-in'), sort: { - asc: { - param: 'recent_sign_in', - label: s__('Members|Last sign-in, ascending'), - }, - desc: { - param: 'oldest_sign_in', - label: s__('Members|Last sign-in, descending'), - }, + asc: 'recent_sign_in', + desc: 'oldest_sign_in', }, }, { @@ -101,9 +76,8 @@ export const FIELDS = [ ]; export const DEFAULT_SORT = { - sortBy: 'account', + sortByKey: 'account', sortDesc: false, - sortByLabel: ACCOUNT_SORT_ASC_LABEL, }; export const AVATAR_SIZE = 48; diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js index 5c58c4a9f6c..bf1fc2d7515 100644 --- a/app/assets/javascripts/members/utils.js +++ b/app/assets/javascripts/members/utils.js @@ -51,23 +51,20 @@ export const parseSortParam = sortableFields => { const sortParam = getParameterByName('sort'); const sortedField = FIELDS.filter(field => sortableFields.includes(field.key)).find( - field => field.sort?.asc?.param === sortParam || field.sort?.desc?.param === sortParam, + field => field.sort?.asc === sortParam || field.sort?.desc === sortParam, ); if (!sortedField) { return DEFAULT_SORT; } - const isDesc = sortedField?.sort?.desc?.param === sortParam; - return { - sortBy: sortedField.key, - sortDesc: isDesc, - sortByLabel: isDesc ? sortedField?.sort?.desc?.label : sortedField?.sort?.asc?.label, + sortByKey: sortedField.key, + sortDesc: sortedField?.sort?.desc === sortParam, }; }; -export const buildSortUrl = ({ +export const buildSortHref = ({ sortBy, sortDesc, filteredSearchBarTokens, @@ -79,7 +76,7 @@ export const buildSortUrl = ({ return ''; } - const sortParam = sortDesc ? sortDefinition.desc.param : sortDefinition.asc.param; + const sortParam = sortDesc ? sortDefinition.desc : sortDefinition.asc; const filterParams = filteredSearchBarTokens?.reduce((accumulator, token) => { diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue index e35817a4dbb..4e9b21a5c55 100644 --- a/app/assets/javascripts/pipelines/components/graph/action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue @@ -91,6 +91,6 @@ export default { @click.stop="onClickAction" > <gl-loading-icon v-if="isLoading" class="js-action-icon-loading" /> - <gl-icon v-else :name="actionIcon" class="gl-mr-0!" /> + <gl-icon v-else :name="actionIcon" class="gl-mr-0!" :aria-label="actionIcon" /> </gl-button> </template> diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue index d775a093f5f..07bd6019b80 100644 --- a/app/assets/javascripts/vue_shared/components/ci_icon.vue +++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue @@ -63,5 +63,7 @@ export default { }; </script> <template> - <span :class="cssClass"> <gl-icon :name="icon" :size="size" :class="cssClasses" /> </span> + <span :class="cssClass"> + <gl-icon :name="icon" :size="size" :class="cssClasses" :aria-label="status.icon" /> + </span> </template> diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb index 1d6f790af31..be3b329fb6f 100644 --- a/app/finders/alert_management/alerts_finder.rb +++ b/app/finders/alert_management/alerts_finder.rb @@ -18,6 +18,7 @@ module AlertManagement return AlertManagement::Alert.none unless authorized? collection = project.alert_management_alerts + collection = by_domain(collection) collection = by_status(collection) collection = by_iid(collection) collection = by_assignee(collection) @@ -30,6 +31,10 @@ module AlertManagement attr_reader :current_user, :project, :params + def by_domain(collection) + collection.with_operations_alerts + end + def by_iid(collection) return collection unless params[:iid] @@ -59,3 +64,5 @@ module AlertManagement end end end + +AlertManagement::AlertsFinder.prepend_if_ee('EE::AlertManagement::AlertsFinder') diff --git a/app/graphql/resolvers/alert_management/alert_resolver.rb b/app/graphql/resolvers/alert_management/alert_resolver.rb index c3219d9cdc3..b115bd80113 100644 --- a/app/graphql/resolvers/alert_management/alert_resolver.rb +++ b/app/graphql/resolvers/alert_management/alert_resolver.rb @@ -18,6 +18,11 @@ module Resolvers description: 'Sort alerts by this criteria', required: false + argument :domain, Types::AlertManagement::DomainFilterEnum, + description: 'Filter query for given domain', + required: true, + default_value: 'operations' + argument :search, GraphQL::STRING_TYPE, description: 'Search query for title, description, service, or monitoring_tool.', required: false diff --git a/app/graphql/types/alert_management/domain_filter_enum.rb b/app/graphql/types/alert_management/domain_filter_enum.rb new file mode 100644 index 00000000000..58dbc8bb2cf --- /dev/null +++ b/app/graphql/types/alert_management/domain_filter_enum.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module AlertManagement + class DomainFilterEnum < BaseEnum + graphql_name 'AlertManagementDomainFilter' + description 'Filters the alerts based on given domain' + + value 'operations', description: 'Alerts for operations domain ' + value 'threat_monitoring', description: 'Alerts for threat monitoring domain' + end + end +end diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb index fa9b9fc9e2d..7090d9f4ea1 100644 --- a/app/models/alert_management/alert.rb +++ b/app/models/alert_management/alert.rb @@ -127,6 +127,8 @@ module AlertManagement scope :open, -> { with_status(open_statuses) } scope :not_resolved, -> { without_status(:resolved) } scope :with_prometheus_alert, -> { includes(:prometheus_alert) } + scope :with_threat_monitoring_alerts, -> { where(domain: :threat_monitoring ) } + scope :with_operations_alerts, -> { where(domain: :operations) } scope :order_start_time, -> (sort_order) { order(started_at: sort_order) } scope :order_end_time, -> (sort_order) { order(ended_at: sort_order) } diff --git a/app/serializers/vulnerabilities/feedback_entity.rb b/app/serializers/vulnerabilities/feedback_entity.rb new file mode 100644 index 00000000000..19d99e2d26a --- /dev/null +++ b/app/serializers/vulnerabilities/feedback_entity.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +class Vulnerabilities::FeedbackEntity < Grape::Entity + include RequestAwareEntity + + expose :id + expose :created_at + expose :project_id + expose :author, using: UserEntity + expose :comment_details, if: -> (feedback, _) { feedback.has_comment? } do + expose :comment + expose :comment_timestamp + expose :comment_author, using: UserEntity + end + + expose :pipeline, if: -> (feedback, _) { feedback.pipeline.present? } do + expose :id do |feedback| + feedback.pipeline.id + end + + expose :path do |feedback| + project_pipeline_path(feedback.pipeline.project, feedback.pipeline) + end + end + + expose :issue_iid, if: -> (feedback, _) { feedback.issue.present? } do |feedback| + feedback.issue.iid + end + + expose :issue_url, if: -> (_, _) { can_read_issue? } do |feedback| + project_issue_url(feedback.project, feedback.issue) + end + + expose :merge_request_iid, if: -> (feedback, _) { feedback.merge_request.present? } do |feedback| + feedback.merge_request.iid + end + + expose :merge_request_path, if: -> (_, _) { can_read_merge_request? } do |feedback| + project_merge_request_path(feedback.project, feedback.merge_request) + end + + expose :destroy_vulnerability_feedback_dismissal_path, if: ->(_, _) { can_destroy_feedback? } + + expose :category + expose :feedback_type + expose :branch do |feedback| + feedback&.pipeline&.ref + end + expose :project_fingerprint + + alias_method :feedback, :object + + private + + def destroy_vulnerability_feedback_dismissal_path + project_vulnerability_feedback_path(feedback.project, feedback) + end + + def can_destroy_feedback? + can?(current_user, :destroy_vulnerability_feedback, feedback) + end + + def can_read_issue? + feedback.issue.present? && can?(current_user, :read_issue, feedback.issue) + end + + def can_read_merge_request? + feedback.merge_request.present? && can?(current_user, :read_merge_request, feedback.merge_request) + end + + def current_user + return request.current_user if request.respond_to?(:current_user) + end +end diff --git a/app/serializers/vulnerabilities/finding_diff_serializer.rb b/app/serializers/vulnerabilities/finding_diff_serializer.rb new file mode 100644 index 00000000000..19777873389 --- /dev/null +++ b/app/serializers/vulnerabilities/finding_diff_serializer.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Vulnerabilities::FindingDiffSerializer < BaseSerializer + include WithPagination + + entity Vulnerabilities::FindingReportsComparerEntity +end diff --git a/app/serializers/vulnerabilities/finding_entity.rb b/app/serializers/vulnerabilities/finding_entity.rb new file mode 100644 index 00000000000..40052896c26 --- /dev/null +++ b/app/serializers/vulnerabilities/finding_entity.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +class Vulnerabilities::FindingEntity < Grape::Entity + include RequestAwareEntity + include VulnerabilitiesHelper + + expose :id, :report_type, :name, :severity, :confidence + expose :scanner, using: Vulnerabilities::ScannerEntity + expose :identifiers, using: Vulnerabilities::IdentifierEntity + expose :project_fingerprint + expose :create_jira_issue_url do |occurrence| + create_jira_issue_url_for(occurrence) + end + expose :create_vulnerability_feedback_issue_path do |occurrence| + create_vulnerability_feedback_issue_path(occurrence.project) + end + expose :create_vulnerability_feedback_merge_request_path do |occurrence| + create_vulnerability_feedback_merge_request_path(occurrence.project) + end + expose :create_vulnerability_feedback_dismissal_path do |occurrence| + create_vulnerability_feedback_dismissal_path(occurrence.project) + end + + expose :project, using: ::ProjectEntity + expose :dismissal_feedback, using: Vulnerabilities::FeedbackEntity + expose :issue_feedback, using: Vulnerabilities::FeedbackEntity + expose :merge_request_feedback, using: Vulnerabilities::FeedbackEntity + + expose :metadata, merge: true, if: ->(occurrence, _) { occurrence.raw_metadata } do + expose :description + expose :links + expose :location + expose :remediations + expose :solution + expose(:evidence) { |model, _| model.evidence[:summary] } + expose(:request, using: Vulnerabilities::RequestEntity) { |model, _| model.evidence[:request] } + expose(:response, using: Vulnerabilities::ResponseEntity) { |model, _| model.evidence[:response] } + expose(:evidence_source) { |model, _| model.evidence[:source] } + expose(:supporting_messages) { |model, _| model.evidence[:supporting_messages]} + expose(:assets) { |model, _| model.assets } + end + + expose :state + expose :scan + + expose :blob_path do |occurrence| + occurrence.present.blob_path + end + + alias_method :occurrence, :object + + def current_user + return request.current_user if request.respond_to?(:current_user) + end +end + +Vulnerabilities::FindingEntity.include_if_ee('::EE::ProjectsHelper') diff --git a/app/serializers/vulnerabilities/finding_reports_comparer_entity.rb b/app/serializers/vulnerabilities/finding_reports_comparer_entity.rb new file mode 100644 index 00000000000..f8529ba56c3 --- /dev/null +++ b/app/serializers/vulnerabilities/finding_reports_comparer_entity.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class Vulnerabilities::FindingReportsComparerEntity < Grape::Entity + include RequestAwareEntity + + expose :base_report_created_at + expose :base_report_out_of_date + expose :head_report_created_at + expose :added, using: Vulnerabilities::FindingEntity + expose :fixed, using: Vulnerabilities::FindingEntity + expose :existing, using: Vulnerabilities::FindingEntity +end diff --git a/app/serializers/vulnerabilities/identifier_entity.rb b/app/serializers/vulnerabilities/identifier_entity.rb new file mode 100644 index 00000000000..79ae4623087 --- /dev/null +++ b/app/serializers/vulnerabilities/identifier_entity.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class Vulnerabilities::IdentifierEntity < Grape::Entity + expose :external_type + expose :external_id + expose :name + expose :url +end diff --git a/app/serializers/vulnerabilities/request_entity.rb b/app/serializers/vulnerabilities/request_entity.rb new file mode 100644 index 00000000000..f1da4525889 --- /dev/null +++ b/app/serializers/vulnerabilities/request_entity.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class Vulnerabilities::RequestEntity < Grape::Entity + expose :headers + expose :method + expose :url + expose :body +end diff --git a/app/serializers/vulnerabilities/response_entity.rb b/app/serializers/vulnerabilities/response_entity.rb new file mode 100644 index 00000000000..64ac006cb16 --- /dev/null +++ b/app/serializers/vulnerabilities/response_entity.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class Vulnerabilities::ResponseEntity < Grape::Entity + expose :headers + expose :reason_phrase + expose :status_code + expose :body +end diff --git a/app/serializers/vulnerabilities/scanner_entity.rb b/app/serializers/vulnerabilities/scanner_entity.rb new file mode 100644 index 00000000000..cdcbc01d867 --- /dev/null +++ b/app/serializers/vulnerabilities/scanner_entity.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Vulnerabilities::ScannerEntity < Grape::Entity + expose :external_id + expose :name + expose :vendor +end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 51fe60e25fc..90764d7374d 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -30,7 +30,7 @@ class RepositoryImportWorker # rubocop:disable Scalability/IdempotentWorker return if service.async? if result[:status] == :error - fail_import(result[:message]) if template_import? + fail_import(result[:message]) raise result[:message] end |