diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-24 15:09:00 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-24 15:09:00 +0300 |
commit | ae78b85a25cb0c19c3d6a2e4e6c7ca91ed50787d (patch) | |
tree | c53ad0fcdab26725814f1dc5267f6a04ebe4cf73 /app | |
parent | 38149afcf95e7669a7a99828c579d185b70c04dc (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
15 files changed, 135 insertions, 101 deletions
diff --git a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js deleted file mode 100644 index 5f2a17da630..00000000000 --- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable no-else-return */ -/* global CommentsStore */ -/* global ResolveService */ - -import Vue from 'vue'; -import { __ } from '~/locale'; - -const ResolveDiscussionBtn = Vue.extend({ - props: { - discussionId: { - type: String, - required: true, - }, - mergeRequestId: { - type: Number, - required: true, - }, - canResolve: { - type: Boolean, - required: true, - }, - }, - data() { - return { - discussion: {}, - }; - }, - computed: { - showButton() { - if (this.discussion) { - return this.discussion.isResolvable(); - } else { - return false; - } - }, - isDiscussionResolved() { - if (this.discussion) { - return this.discussion.isResolved(); - } else { - return false; - } - }, - buttonText() { - if (this.isDiscussionResolved) { - return __('Unresolve discussion'); - } else { - return __('Resolve discussion'); - } - }, - loading() { - if (this.discussion) { - return this.discussion.loading; - } else { - return false; - } - }, - }, - created() { - CommentsStore.createDiscussion(this.discussionId, this.canResolve); - - this.discussion = CommentsStore.state[this.discussionId]; - }, - methods: { - resolve() { - ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId); - }, - }, -}); - -Vue.component('resolve-discussion-btn', ResolveDiscussionBtn); diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js index 7dcf3594471..92862d4c933 100644 --- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js +++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js @@ -11,7 +11,6 @@ import './components/comment_resolve_btn'; import './components/jump_to_discussion'; import './components/resolve_btn'; import './components/resolve_count'; -import './components/resolve_discussion_btn'; import './components/diff_note_avatars'; import './components/new_issue_for_discussion'; @@ -20,7 +19,7 @@ export default () => { document.querySelector('.merge-request') || document.querySelector('.commit-box'); const { projectPath } = projectPathHolder.dataset; const COMPONENT_SELECTOR = - 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn'; + 'resolve-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn'; window.gl = window.gl || {}; window.gl.diffNoteApps = {}; diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js index 5450abf4cbd..692b41da965 100644 --- a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js +++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js @@ -9,7 +9,7 @@ import DropdownUtils from './dropdown_utils'; import { mergeUrlParams } from '../lib/utils/url_utility'; export default class AvailableDropdownMappings { - constructor( + constructor({ container, runnerTagsEndpoint, labelsEndpoint, @@ -18,7 +18,7 @@ export default class AvailableDropdownMappings { groupsOnly, includeAncestorGroups, includeDescendantGroups, - ) { + }) { this.container = container; this.runnerTagsEndpoint = runnerTagsEndpoint; this.labelsEndpoint = labelsEndpoint; diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index 566fb295588..03f65612b60 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -13,6 +13,7 @@ export default class FilteredSearchDropdownManager { labelsEndpoint = '', milestonesEndpoint = '', releasesEndpoint = '', + epicsEndpoint = '', tokenizer, page, isGroup, @@ -27,6 +28,7 @@ export default class FilteredSearchDropdownManager { this.labelsEndpoint = removeTrailingSlash(labelsEndpoint); this.milestonesEndpoint = removeTrailingSlash(milestonesEndpoint); this.releasesEndpoint = removeTrailingSlash(releasesEndpoint); + this.epicsEndpoint = removeTrailingSlash(epicsEndpoint); this.tokenizer = tokenizer; this.filteredSearchTokenKeys = filteredSearchTokenKeys || FilteredSearchTokenKeys; this.filteredSearchInput = this.container.querySelector('.filtered-search'); @@ -54,16 +56,8 @@ export default class FilteredSearchDropdownManager { setupMapping() { const supportedTokens = this.filteredSearchTokenKeys.getKeys(); - const availableMappings = new AvailableDropdownMappings( - this.container, - this.runnerTagsEndpoint, - this.labelsEndpoint, - this.milestonesEndpoint, - this.releasesEndpoint, - this.groupsOnly, - this.includeAncestorGroups, - this.includeDescendantGroups, - ); + + const availableMappings = new AvailableDropdownMappings({ ...this }); this.mapping = availableMappings.getAllowedMappings(supportedTokens); } diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 0b4f9457c54..e9a714605c7 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -45,6 +45,11 @@ export default class FilteredSearchManager { this.filteredSearchTokenKeys.enableMultipleAssignees(); } + const { epicsEndpoint } = this.filteredSearchInput.dataset; + if (!epicsEndpoint && this.filteredSearchTokenKeys.removeEpicToken) { + this.filteredSearchTokenKeys.removeEpicToken(); + } + this.recentSearchesStore = new RecentSearchesStore({ isLocalStorageAvailable: RecentSearchesService.isAvailable(), allowedKeys: this.filteredSearchTokenKeys.getKeys(), @@ -88,12 +93,20 @@ export default class FilteredSearchManager { if (this.filteredSearchInput) { this.tokenizer = FilteredSearchTokenizer; + const { + runnerTagsEndpoint = '', + labelsEndpoint = '', + milestonesEndpoint = '', + releasesEndpoint = '', + epicsEndpoint = '', + } = this.filteredSearchInput.dataset; + this.dropdownManager = new FilteredSearchDropdownManager({ - runnerTagsEndpoint: - this.filteredSearchInput.getAttribute('data-runner-tags-endpoint') || '', - labelsEndpoint: this.filteredSearchInput.getAttribute('data-labels-endpoint') || '', - milestonesEndpoint: this.filteredSearchInput.getAttribute('data-milestones-endpoint') || '', - releasesEndpoint: this.filteredSearchInput.getAttribute('data-releases-endpoint') || '', + runnerTagsEndpoint, + labelsEndpoint, + milestonesEndpoint, + releasesEndpoint, + epicsEndpoint, tokenizer: this.tokenizer, page: this.page, isGroup: this.isGroup, diff --git a/app/assets/javascripts/filtered_search/visual_token_value.js b/app/assets/javascripts/filtered_search/visual_token_value.js index 9f3cf881af4..b7ac655b619 100644 --- a/app/assets/javascripts/filtered_search/visual_token_value.js +++ b/app/assets/javascripts/filtered_search/visual_token_value.js @@ -28,6 +28,8 @@ export default class VisualTokenValue { this.updateUserTokenAppearance(tokenValueContainer, tokenValueElement); } else if (tokenType === 'my-reaction') { this.updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement); + } else if (tokenType === 'epic') { + this.updateEpicLabel(tokenValueContainer, tokenValueElement); } } @@ -83,6 +85,39 @@ export default class VisualTokenValue { .catch(() => new Flash(__('An error occurred while fetching label colors.'))); } + updateEpicLabel(tokenValueContainer) { + const tokenValue = this.tokenValue.replace(/^&/, ''); + const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search'); + const { epicsEndpoint } = filteredSearchInput.dataset; + const epicsEndpointWithParams = FilteredSearchVisualTokens.getEndpointWithQueryParams( + `${epicsEndpoint}.json`, + filteredSearchInput.dataset.endpointQueryParams, + ); + + return AjaxCache.retrieve(epicsEndpointWithParams) + .then(epics => { + const matchingEpic = (epics || []).find(epic => epic.id === Number(tokenValue)); + + if (!matchingEpic) { + return; + } + + VisualTokenValue.replaceEpicTitle(tokenValueContainer, matchingEpic.title, matchingEpic.id); + }) + .catch(() => new Flash(__('An error occurred while adding formatted title for epic'))); + } + + static replaceEpicTitle(tokenValueContainer, epicTitle, epicId) { + const tokenContainer = tokenValueContainer; + + const valueContainer = tokenContainer.querySelector('.value'); + + if (valueContainer) { + tokenContainer.dataset.originalValue = valueContainer.innerText; + valueContainer.innerText = `"${epicTitle}"::&${epicId}`; + } + } + static setTokenStyle(tokenValueContainer, backgroundColor, textColor) { const token = tokenValueContainer; diff --git a/app/assets/javascripts/self_monitor/components/self_monitor_form.vue b/app/assets/javascripts/self_monitor/components/self_monitor_form.vue index 6b19a72317c..1e203d332db 100644 --- a/app/assets/javascripts/self_monitor/components/self_monitor_form.vue +++ b/app/assets/javascripts/self_monitor/components/self_monitor_form.vue @@ -129,7 +129,7 @@ export default { </div> <div class="settings-content"> <form name="self-monitoring-form"> - <p v-html="selfMonitoringFormText"></p> + <p ref="selfMonitoringFormText" v-html="selfMonitoringFormText"></p> <gl-form-group :label="$options.formLabels.createProject" label-for="self-monitor-toggle"> <gl-toggle v-model="selfMonitorEnabled" diff --git a/app/assets/javascripts/self_monitor/store/state.js b/app/assets/javascripts/self_monitor/store/state.js index a0ce88ff58c..582ce5576f1 100644 --- a/app/assets/javascripts/self_monitor/store/state.js +++ b/app/assets/javascripts/self_monitor/store/state.js @@ -9,7 +9,7 @@ export default (initialState = {}) => ({ deleteProjectStatusEndpoint: initialState.statusDeleteSelfMonitoringProjectPath || '', selfMonitorProjectPath: initialState.selfMonitoringProjectFullPath || '', showAlert: false, - projectPath: '', + projectPath: initialState.selfMonitoringProjectFullPath || '', loading: false, alertContent: {}, }); diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 005e5efbdaf..53a642d68ce 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -410,6 +410,15 @@ } } + > button.dropdown-epic-button { + flex-direction: column; + + .reference { + color: $gl-gray-400; + margin-top: $gl-padding-4; + } + } + &.droplab-item-selected i { visibility: visible; } diff --git a/app/finders/serverless_domain_finder.rb b/app/finders/serverless_domain_finder.rb index 3a8a55022bb..661cd0ca363 100644 --- a/app/finders/serverless_domain_finder.rb +++ b/app/finders/serverless_domain_finder.rb @@ -11,7 +11,7 @@ class ServerlessDomainFinder return unless serverless? @serverless_domain_cluster = ::Serverless::DomainCluster.for_uuid(serverless_domain_cluster_uuid) - return unless serverless_domain_cluster + return unless serverless_domain_cluster&.knative&.external_ip @environment = ::Environment.for_id_and_slug(match[:environment_id].to_i(16), match[:environment_slug]) return unless environment diff --git a/app/models/serverless/lookup_path.rb b/app/models/serverless/lookup_path.rb new file mode 100644 index 00000000000..c09b3718651 --- /dev/null +++ b/app/models/serverless/lookup_path.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Serverless + class LookupPath + attr_reader :serverless_domain + + delegate :serverless_domain_cluster, to: :serverless_domain + delegate :knative, to: :serverless_domain_cluster + delegate :certificate, to: :serverless_domain_cluster + delegate :key, to: :serverless_domain_cluster + + def initialize(serverless_domain) + @serverless_domain = serverless_domain + end + + def source + { + type: 'serverless', + service: serverless_domain.knative_uri.host, + cluster: { + hostname: knative.hostname, + address: knative.external_ip, + port: 443, + cert: certificate, + key: key + } + } + end + end +end diff --git a/app/models/serverless/virtual_domain.rb b/app/models/serverless/virtual_domain.rb new file mode 100644 index 00000000000..d6a23a4c0ce --- /dev/null +++ b/app/models/serverless/virtual_domain.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Serverless + class VirtualDomain + attr_reader :serverless_domain + + delegate :serverless_domain_cluster, to: :serverless_domain + delegate :pages_domain, to: :serverless_domain_cluster + delegate :certificate, to: :pages_domain + delegate :key, to: :pages_domain + + def initialize(serverless_domain) + @serverless_domain = serverless_domain + end + + def lookup_paths + [ + ::Serverless::LookupPath.new(serverless_domain) + ] + end + end +end diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index 57c0cdd0602..fb59797a8df 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -8,6 +8,8 @@ module Ci JOB_QUEUE_DURATION_SECONDS_BUCKETS = [1, 3, 10, 30, 60, 300, 900, 1800, 3600].freeze JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET = 5.freeze + METRICS_SHARD_TAG_PREFIX = 'metrics_shard::'.freeze + DEFAULT_METRICS_SHARD = 'default'.freeze Result = Struct.new(:build, :valid?) @@ -193,7 +195,13 @@ module Ci def register_success(job) labels = { shared_runner: runner.instance_type?, - jobs_running_for_project: jobs_running_for_project(job) } + jobs_running_for_project: jobs_running_for_project(job), + shard: DEFAULT_METRICS_SHARD } + + if runner.instance_type? + shard = runner.tag_list.sort.find { |name| name.starts_with?(METRICS_SHARD_TAG_PREFIX) } + labels[:shard] = shard.gsub(METRICS_SHARD_TAG_PREFIX, '') if shard + end job_queue_duration_seconds.observe(labels, Time.now - job.queued_at) unless job.queued_at.nil? attempt_counter.increment diff --git a/app/views/discussions/_resolve_all.html.haml b/app/views/discussions/_resolve_all.html.haml deleted file mode 100644 index 689a22acd27..00000000000 --- a/app/views/discussions/_resolve_all.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%resolve-discussion-btn{ ":discussion-id" => "'#{discussion.id}'", - ":merge-request-id" => discussion.noteable.iid, - ":can-resolve" => discussion.can_resolve?(current_user), - "inline-template" => true } - .btn-group{ role: "group", "v-if" => "showButton" } - %button.btn.btn-default{ type: "button", "@click" => "resolve", ":disabled" => "loading", "v-cloak" => "true" } - = icon("spinner spin", "v-show" => "loading") - {{ buttonText }} diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index a27ceaff782..d9ca0b8869f 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -159,6 +159,8 @@ = render_if_exists 'shared/issuable/filter_weight', type: type + = render_if_exists 'shared/issuable/filter_epic', type: type + %button.clear-search.hidden{ type: 'button' } = icon('times') .filter-dropdown-container.d-flex.flex-column.flex-md-row |