diff --git a/.gitlab/ci/review-apps/dast-api.gitlab-ci.yml b/.gitlab/ci/review-apps/dast-api.gitlab-ci.yml new file mode 100644 index 00000000000..e2f32f120af --- /dev/null +++ b/.gitlab/ci/review-apps/dast-api.gitlab-ci.yml @@ -0,0 +1,14 @@ +include: + - template: DAST-API.gitlab-ci.yml + +dast_api: + variables: + DAST_API_PROFILE: Passive + DAST_API_GRAPHQL: /api/graphql + DAST_API_TARGET_URL: ${CI_ENVIRONMENT_URL} + DAST_API_OVERRIDES_ENV: "{\"headers\":{\"Authorization\":\"Bearer $REVIEW_APPS_ROOT_TOKEN\"}}" + needs: ["review-deploy"] + # Uncomment resource_group if DAST_API_PROFILE is changed to an active scan + # resource_group: dast_api_scan + rules: + - !reference [".reports:rules:schedule-dast", rules] diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml index 77e0bbff6a7..4c0a3579c92 100644 --- a/.gitlab/ci/review-apps/main.gitlab-ci.yml +++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml @@ -14,6 +14,7 @@ include: - local: .gitlab/ci/review-apps/rules.gitlab-ci.yml - local: .gitlab/ci/review-apps/qa.gitlab-ci.yml - local: .gitlab/ci/review-apps/dast.gitlab-ci.yml + - local: .gitlab/ci/review-apps/dast-api.gitlab-ci.yml .base-before_script: &base-before_script - source ./scripts/utils.sh diff --git a/Gemfile b/Gemfile index 25d21017bff..8a8cf9e94b1 100644 --- a/Gemfile +++ b/Gemfile @@ -357,7 +357,7 @@ gem 'warning', '~> 1.3.0' group :development do gem 'lefthook', '~> 1.1.1', require: false gem 'rubocop' - gem 'solargraph', '~> 0.46.0', require: false + gem 'solargraph', '~> 0.47.2', require: false gem 'letter_opener_web', '~> 2.0.0' gem 'lookbook', '~> 1.0', '>= 1.0.8' diff --git a/Gemfile.checksum b/Gemfile.checksum index c22dc2c8804..1216b526045 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -458,7 +458,7 @@ {"name":"redis-namespace","version":"1.9.0","platform":"ruby","checksum":"0923961f38cf15b86cb57d92507e0a3b32480729eb5033249f5de8b12e0d8612"}, {"name":"redis-rack","version":"2.1.4","platform":"ruby","checksum":"0872eecb303e483c3863d6bd0d47323d230640d41c1a4ac4a2c7596ec0b1774c"}, {"name":"redis-store","version":"1.9.1","platform":"ruby","checksum":"7b4c7438d46f7b7ce8f67fc0eda3a04fc67d32d28cf606cc98a5df4d2b77071d"}, -{"name":"regexp_parser","version":"2.5.0","platform":"ruby","checksum":"a076d2d35ab8d11feab5fecf8aa09ec6df68c2429810748cba079f7b021ecde5"}, +{"name":"regexp_parser","version":"2.6.0","platform":"ruby","checksum":"f163ba463a45ca2f2730e0902f2475bb0eefcd536dfc2f900a86d1e5a7d7a556"}, {"name":"regexp_property_values","version":"1.0.0","platform":"java","checksum":"5e26782b01241616855c4ee7bb8a62fce9387e484f2d3eaf04f2a0633708222e"}, {"name":"regexp_property_values","version":"1.0.0","platform":"ruby","checksum":"162499dc0bba1e66d334273a059f207a61981cc8cc69d2ca743594e7886d080f"}, {"name":"representable","version":"3.0.4","platform":"ruby","checksum":"07d43917dea4712ecebd19c1909e769deed863ad444d23ceb6461519e2cba962"}, @@ -544,7 +544,7 @@ {"name":"slack-messenger","version":"2.3.4","platform":"ruby","checksum":"49c611d2be5b0f9c250a3a957b9cc09b9c07b81dacb9843642d87b6fa35609c1"}, {"name":"snaky_hash","version":"2.0.0","platform":"ruby","checksum":"fe8b2e39e8ff69320f7812af73ea06401579e29ff1734a7009567391600687de"}, {"name":"snowplow-tracker","version":"0.6.1","platform":"ruby","checksum":"9cec52fd060619f4974b3dc1f7d9a2776c5e31b668a6ead53145b9780e312314"}, -{"name":"solargraph","version":"0.46.0","platform":"ruby","checksum":"1da9fd8c364501f18b0454e54506e7098bc38dae719219713fe5f246dfc91465"}, +{"name":"solargraph","version":"0.47.2","platform":"ruby","checksum":"87ca4b799b9155c2c31c15954c483e952fdacd800f52d6709b901dd447bcac6a"}, {"name":"sorted_set","version":"1.0.3","platform":"java","checksum":"996283f2e5c6e838825bcdcee31d6306515ae5f24bcb0ee4ce09dfff32919b8c"}, {"name":"sorted_set","version":"1.0.3","platform":"ruby","checksum":"4f2b8bee6e8c59cbd296228c0f1f81679357177a8b6859dcc2a99e86cce6372f"}, {"name":"spamcheck","version":"1.0.0","platform":"ruby","checksum":"dfeea085184091353e17d729d2f3d714b07cba36aaf64c32dfc35ce9b466fc9c"}, @@ -576,7 +576,7 @@ {"name":"text","version":"1.3.1","platform":"ruby","checksum":"2fbbbc82c1ce79c4195b13018a87cbb00d762bda39241bb3cdc32792759dd3f4"}, {"name":"thor","version":"1.2.1","platform":"ruby","checksum":"b1752153dc9c6b8d3fcaa665e9e1a00a3e73f28da5e238b81c404502e539d446"}, {"name":"thrift","version":"0.16.0","platform":"ruby","checksum":"d023286ea89e30444c9f1c28dd76107f87d8aaf85fe1742da1d8cd3b5417dcce"}, -{"name":"tilt","version":"2.0.10","platform":"ruby","checksum":"9b664f0e9ae2b500cfa00f9c65c34abc6ff1799cf0034a8c0a0412d520fac866"}, +{"name":"tilt","version":"2.0.11","platform":"ruby","checksum":"7b180fc472cbdeb186c85d31c0f2d1e61a2c0d77e1d9fd0ca28482a9d972d6a0"}, {"name":"timecop","version":"0.9.1","platform":"ruby","checksum":"374b543f0961dbd487e96d09ac812d4fdfeb603ec705bbff241ba060d0a9f534"}, {"name":"timeliness","version":"0.3.10","platform":"ruby","checksum":"c357233ce19dc53148e8b29dfddde134689f18f52b32928e9dfe12ebcf4a773f"}, {"name":"timfel-krb5-auth","version":"0.8.3","platform":"ruby","checksum":"ab388c9d747fa3cd95baf2cc1c03253e372d8c680adcc543670f4f099854bb80"}, diff --git a/Gemfile.lock b/Gemfile.lock index 8b023661438..2a42fa30826 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1144,7 +1144,7 @@ GEM redis-store (>= 1.2, < 2) redis-store (1.9.1) redis (>= 4, < 5) - regexp_parser (2.5.0) + regexp_parser (2.6.0) regexp_property_values (1.0.0) representable (3.0.4) declarative (< 0.1.0) @@ -1333,7 +1333,7 @@ GEM version_gem (~> 1.1) snowplow-tracker (0.6.1) contracts (~> 0.7, <= 0.11) - solargraph (0.46.0) + solargraph (0.47.2) backport (~> 1.2) benchmark bundler (>= 1.17.2) @@ -1402,7 +1402,7 @@ GEM text (1.3.1) thor (1.2.1) thrift (0.16.0) - tilt (2.0.10) + tilt (2.0.11) timecop (0.9.1) timeliness (0.3.10) timfel-krb5-auth (0.8.3) @@ -1783,7 +1783,7 @@ DEPENDENCIES simplecov-lcov (~> 0.8.0) slack-messenger (~> 2.3.4) snowplow-tracker (~> 0.6.1) - solargraph (~> 0.46.0) + solargraph (~> 0.47.2) spamcheck (~> 1.0.0) spring (~> 2.1.0) spring-commands-rspec (~> 1.0.4) diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index 956d62c29f3..93beb014099 100644 --- a/app/assets/javascripts/boards/components/board_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -397,7 +397,6 @@ export default { :img-size="avatarSize" class="js-no-trigger user-avatar-link" tooltip-placement="bottom" - :enforce-gl-avatar="true" > {{ __('Assignee') }} diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index 6caf5aa8e94..d74cb2d8175 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -97,6 +97,7 @@ export default { eventHub.$on(`${this.action}showLeaveGroupModal`, this.showLeaveGroupModal); eventHub.$on(`${this.action}updatePagination`, this.updatePagination); eventHub.$on(`${this.action}updateGroups`, this.updateGroups); + eventHub.$on(`${this.action}fetchFilteredAndSortedGroups`, this.fetchFilteredAndSortedGroups); }, mounted() { this.fetchAllGroups(); @@ -111,6 +112,7 @@ export default { eventHub.$off(`${this.action}showLeaveGroupModal`, this.showLeaveGroupModal); eventHub.$off(`${this.action}updatePagination`, this.updatePagination); eventHub.$off(`${this.action}updateGroups`, this.updateGroups); + eventHub.$off(`${this.action}fetchFilteredAndSortedGroups`, this.fetchFilteredAndSortedGroups); }, methods: { hideModal() { @@ -153,6 +155,18 @@ export default { this.updateGroups(res, Boolean(this.filterGroupsBy)); }); }, + fetchFilteredAndSortedGroups({ filterGroupsBy, sortBy }) { + this.isLoading = true; + + return this.fetchGroups({ + filterGroupsBy, + sortBy, + updatePagination: true, + }).then((res) => { + this.isLoading = false; + this.updateGroups(res, Boolean(filterGroupsBy)); + }); + }, fetchPage({ page, filterGroupsBy, sortBy, archived }) { this.isLoading = true; diff --git a/app/assets/javascripts/groups/components/overview_tabs.vue b/app/assets/javascripts/groups/components/overview_tabs.vue index 325e42af0f8..84e992b6365 100644 --- a/app/assets/javascripts/groups/components/overview_tabs.vue +++ b/app/assets/javascripts/groups/components/overview_tabs.vue @@ -1,6 +1,6 @@ @@ -99,5 +171,36 @@ export default { :render-empty-state="renderEmptyState" /> + diff --git a/app/assets/javascripts/groups/constants.js b/app/assets/javascripts/groups/constants.js index 223c2975c11..33bfcade336 100644 --- a/app/assets/javascripts/groups/constants.js +++ b/app/assets/javascripts/groups/constants.js @@ -62,3 +62,26 @@ export const VISIBILITY_TYPE_ICON = { [VISIBILITY_LEVEL_INTERNAL_STRING]: 'shield', [VISIBILITY_LEVEL_PRIVATE_STRING]: 'lock', }; + +export const OVERVIEW_TABS_SORTING_ITEMS = [ + { + label: __('Name'), + asc: 'name_asc', + desc: 'name_desc', + }, + { + label: __('Created'), + asc: 'created_asc', + desc: 'created_desc', + }, + { + label: __('Updated'), + asc: 'latest_activity_asc', + desc: 'latest_activity_desc', + }, + { + label: __('Stars'), + asc: 'stars_asc', + desc: 'stars_desc', + }, +]; diff --git a/app/assets/javascripts/groups/init_overview_tabs.js b/app/assets/javascripts/groups/init_overview_tabs.js index 4fa3682c729..664d07ca13d 100644 --- a/app/assets/javascripts/groups/init_overview_tabs.js +++ b/app/assets/javascripts/groups/init_overview_tabs.js @@ -51,6 +51,7 @@ export const initGroupOverviewTabs = () => { subgroupsAndProjectsEndpoint, sharedProjectsEndpoint, archivedProjectsEndpoint, + initialSort, } = el.dataset; return new Vue({ @@ -70,6 +71,7 @@ export const initGroupOverviewTabs = () => { [ACTIVE_TAB_SHARED]: sharedProjectsEndpoint, [ACTIVE_TAB_ARCHIVED]: archivedProjectsEndpoint, }, + initialSort, }, render(createElement) { return createElement(OverviewTabs); diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue index 22fe3fe440e..f2f3d7ebeab 100644 --- a/app/assets/javascripts/repository/components/last_commit.vue +++ b/app/assets/javascripts/repository/components/last_commit.vue @@ -122,14 +122,12 @@ export default { :link-href="commit.author.webPath" :img-src="commit.author.avatarUrl" :img-size="32" - :img-css-classes="'gl-mr-0!' /* NOTE: this is needed only while we migrate user-avatar-image to GlAvatar (7731 epics) */" class="gl-my-2 gl-mr-4" />
*/ +import { GlTooltip, GlAvatar } from '@gitlab/ui'; +import { isObject } from 'lodash'; import defaultAvatarUrl from 'images/no_avatar.png'; import { __ } from '~/locale'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import UserAvatarImageNew from './user_avatar_image_new.vue'; -import UserAvatarImageOld from './user_avatar_image_old.vue'; +import { placeholderImage } from '~/lazy_loader'; export default { name: 'UserAvatarImage', components: { - UserAvatarImageNew, - UserAvatarImageOld, + GlTooltip, + GlAvatar, }, - mixins: [glFeatureFlagMixin()], props: { lazy: { type: Boolean, @@ -51,8 +51,7 @@ export default { }, size: { type: [Number, Object], - required: false, - default: 20, + required: true, }, tooltipText: { type: String, @@ -64,22 +63,52 @@ export default { required: false, default: 'top', }, - enforceGlAvatar: { - type: Boolean, - required: false, + }, + computed: { + // API response sends null when gravatar is disabled and + // we provide an empty string when we use it inside user avatar link. + // In both cases we should render the defaultAvatarUrl + sanitizedSource() { + let baseSrc = this.imgSrc === '' || this.imgSrc === null ? defaultAvatarUrl : this.imgSrc; + // Only adds the width to the URL if its not a base64 data image + if (!(baseSrc.indexOf('data:') === 0) && !baseSrc.includes('?')) + baseSrc += `?width=${this.maximumSize}`; + return baseSrc; + }, + maximumSize() { + if (isObject(this.size)) { + return Math.max(...Object.values(this.size)); + } + + return this.size; + }, + resultantSrcAttribute() { + return this.lazy ? placeholderImage : this.sanitizedSource; }, }, }; diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue deleted file mode 100644 index 6bd66981860..00000000000 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue +++ /dev/null @@ -1,117 +0,0 @@ - - - diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_old.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_old.vue deleted file mode 100644 index 6e8c200d5c3..00000000000 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_old.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue index f80abed4d69..1a81da3eb0d 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue @@ -9,7 +9,7 @@ :link-href="userProfileUrl" :img-src="userAvatarSrc" :img-alt="tooltipText" - :img-size="20" + :img-size="32" :tooltip-text="tooltipText" :tooltip-placement="top" :username="username" @@ -17,17 +17,18 @@ */ -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import UserAvatarLinkNew from './user_avatar_link_new.vue'; -import UserAvatarLinkOld from './user_avatar_link_old.vue'; +import { GlAvatarLink, GlTooltipDirective } from '@gitlab/ui'; +import UserAvatarImage from './user_avatar_image.vue'; export default { - name: 'UserAvatarLink', + name: 'UserAvatarLinkNew', components: { - UserAvatarLinkNew, - UserAvatarLinkOld, + UserAvatarImage, + GlAvatarLink, + }, + directives: { + GlTooltip: GlTooltipDirective, }, - mixins: [glFeatureFlagMixin()], props: { lazy: { type: Boolean, @@ -56,8 +57,7 @@ export default { }, imgSize: { type: [Number, Object], - required: false, - default: 20, + required: true, }, tooltipText: { type: String, @@ -74,29 +74,43 @@ export default { required: false, default: '', }, - enforceGlAvatar: { - type: Boolean, - required: false, + }, + computed: { + shouldShowUsername() { + return this.username.length > 0; + }, + avatarTooltipText() { + return this.shouldShowUsername ? '' : this.tooltipText; }, }, }; diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_new.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_new.vue deleted file mode 100644 index 83551c689c4..00000000000 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_new.vue +++ /dev/null @@ -1,122 +0,0 @@ - - - diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_old.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_old.vue deleted file mode 100644 index c2e46e61e1b..00000000000 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link_old.vue +++ /dev/null @@ -1,117 +0,0 @@ - - - diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue index 9da298ad705..231f5ff3d1f 100644 --- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue +++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue @@ -1,6 +1,5 @@