diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-16 09:08:48 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-16 09:08:48 +0300 |
commit | e94d662e5aabd4918de373b37c4f084325c7c1f8 (patch) | |
tree | 0b2cfdcc08258329318dcbf4412266893f7616d6 | |
parent | c311ce331fcfe1c1f1619534eb746245a1deb326 (diff) |
Add latest changes from gitlab-org/gitlab@master
30 files changed, 451 insertions, 89 deletions
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index fff89832bf0..4fa78ecd5a4 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -25,7 +25,8 @@ export default class FilteredSearchBoards extends FilteredSearchManager { } updateObject(path) { - this.store.path = path.substr(1); + const groupByParam = new URLSearchParams(window.location.search).get('group_by'); + this.store.path = `${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`; if (gon.features.boardsWithSwimlanes || gon.features.graphqlBoardLists) { boardsStore.updateFiltersUrl(); diff --git a/app/assets/javascripts/jira_connect.js b/app/assets/javascripts/jira_connect.js index 895cdc4562c..0864a3024ac 100644 --- a/app/assets/javascripts/jira_connect.js +++ b/app/assets/javascripts/jira_connect.js @@ -18,6 +18,13 @@ function onLoaded() { alert(res.responseJSON.error); }; + AP.getLocation(function(location) { + $('.js-jira-connect-sign-in').each(function() { + var updatedLink = `${$(this).attr('href')}?return_to=${location}`; + $(this).attr('href', updatedLink); + }); + }); + $('#add-subscription-form').on('submit', function(e) { var actionUrl = $(this).attr('action'); e.preventDefault(); diff --git a/app/assets/javascripts/static_site_editor/graphql/index.js b/app/assets/javascripts/static_site_editor/graphql/index.js index fbb3d7fbfcc..cc68bc57bb0 100644 --- a/app/assets/javascripts/static_site_editor/graphql/index.js +++ b/app/assets/javascripts/static_site_editor/graphql/index.js @@ -21,6 +21,7 @@ const createApolloProvider = appData => { }, { typeDefs, + assumeImmutableResults: true, }, ); diff --git a/app/assets/javascripts/static_site_editor/graphql/resolvers/has_submitted_changes.js b/app/assets/javascripts/static_site_editor/graphql/resolvers/has_submitted_changes.js index ce55db7f3e5..ea49b21eb0d 100644 --- a/app/assets/javascripts/static_site_editor/graphql/resolvers/has_submitted_changes.js +++ b/app/assets/javascripts/static_site_editor/graphql/resolvers/has_submitted_changes.js @@ -1,16 +1,24 @@ +import { produce } from 'immer'; import query from '../queries/app_data.query.graphql'; const hasSubmittedChangesResolver = (_, { input: { hasSubmittedChanges } }, { cache }) => { - const { appData } = cache.readQuery({ query }); - cache.writeQuery({ - query, - data: { + const oldData = cache.readQuery({ query }); + + const data = produce(oldData, draftState => { + // punctually modifying draftState as per immer docs upsets our linters + return { + ...draftState, appData: { __typename: 'AppData', - ...appData, + ...draftState.appData, hasSubmittedChanges, }, - }, + }; + }); + + cache.writeQuery({ + query, + data, }); }; diff --git a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js index 694cf762e51..4137ede49c6 100644 --- a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js +++ b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js @@ -1,3 +1,4 @@ +import { produce } from 'immer'; import submitContentChanges from '../../services/submit_content_changes'; import savedContentMetaQuery from '../queries/saved_content_meta.query.graphql'; @@ -14,14 +15,18 @@ const submitContentChangesResolver = ( images, mergeRequestMeta, }).then(savedContentMeta => { - cache.writeQuery({ - query: savedContentMetaQuery, - data: { + const data = produce(savedContentMeta, draftState => { + return { savedContentMeta: { __typename: 'SavedContentMeta', - ...savedContentMeta, + ...draftState, }, - }, + }; + }); + + cache.writeQuery({ + query: savedContentMetaQuery, + data, }); }); }; diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 59e83608d9d..a8c830f5a42 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -186,6 +186,7 @@ table { .checkbox { padding-left: $gl-spacing-scale-4; padding-right: 0; + width: 1px; + td, + th { @@ -205,12 +206,20 @@ table { width: 10%; } + .description { + max-width: 0; + } + .identifier { width: 16%; } .scanner { - width: 15%; + width: 10%; + } + + .activity { + width: 5%; } } } diff --git a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss index 93cb9be4a8f..d1f7c2e9865 100644 --- a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss +++ b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss @@ -226,20 +226,20 @@ @return calc(#{$original-padding + $original-border} - var(--ide-btn-hover-border-width, #{$original-border})); } - .btn:not(.btn-link):not([disabled]):hover { + .btn:not(.gl-button):not(.btn-link):not([disabled]):hover { border-width: var(--ide-btn-hover-border-width, 1px); padding: calc-btn-hover-padding(6px) calc-btn-hover-padding(10px); } - .btn:not([disabled]).btn-sm:hover { + .btn:not(.gl-button):not([disabled]).btn-sm:hover { padding: calc-btn-hover-padding(4px) calc-btn-hover-padding(10px); } - .btn:not([disabled]).btn-block:hover { + .btn:not(.gl-button):not([disabled]).btn-block:hover { padding: calc-btn-hover-padding(6px) 0; } - .btn-default, + .btn-default:not(.gl-button), .dropdown, .dropdown-menu-toggle { background-color: var(--ide-input-background, $white) !important; @@ -258,7 +258,7 @@ } // In IDE, the only inverted buttons are `.btn-remove` - .btn-inverted.btn-remove { + .btn-inverted.btn-remove:not(.gl-button) { color: var(--ide-input-color, $red-500) !important; background-color: var(--ide-input-background, $white) !important; border-color: var(--ide-btn-default-border, $red-500); @@ -277,17 +277,21 @@ } } - .btn-default { + // todo: remove this block after all default buttons have been migrated to gl-button + .btn-default:not(.gl-button) { + background-color: var(--ide-btn-default-background, $white) !important; + border-color: var(--ide-btn-default-border, $border-color); + &:hover, &:focus { border-color: var(--ide-btn-default-hover-border, $border-white-normal) !important; - background-color: var(--ide-input-background, $white-normal) !important; + background-color: var(--ide-btn-default-background, $white-normal) !important; } &:active, .active { border-color: var(--ide-btn-default-hover-border, $border-white-normal) !important; - background-color: var(--ide-input-background, $white-dark) !important; + background-color: var(--ide-btn-default-background, $white-dark) !important; } } @@ -321,8 +325,9 @@ border-color: var(--ide-dropdown-hover-background, $gray-100) !important; } - .btn-primary, - .btn-info { + // todo: remove this block after all primary/info buttons have been migrated to gl-button + .btn-primary:not(.gl-button), + .btn-info:not(.gl-button) { background-color: var(--ide-btn-primary-background, $blue-500); border-color: var(--ide-btn-primary-border, $blue-600) !important; @@ -339,7 +344,8 @@ } } - .btn-success { + // todo: remove this block after all success buttons have been migrated to gl-button + .btn-success:not(.gl-button) { background-color: var(--ide-btn-success-background, $green-500); border-color: var(--ide-btn-success-border, $green-600) !important; @@ -356,12 +362,70 @@ } } - .btn[disabled] { + // todo: remove this block after all disabled buttons have been migrated to gl-button + .btn[disabled]:not(.gl-button) { background-color: var(--ide-btn-default-background, $gray-light) !important; border: 1px solid var(--ide-btn-disabled-border, $gray-100) !important; color: var(--ide-btn-disabled-color, $gl-text-color-disabled) !important; } + @function ide-btn-var($btn-type, $var-type, $value) { + @return var(--ide-btn-#{$btn-type}-#{$var-type}, $value); + } + + @mixin ide-gl-button($btn-type, $bg-normal, $bg-hover, $bg-active, $border-normal, $border-hover, $border-focus, $border-active, $border-width-hover: 2px, $box-shadow-hover: $t-gray-a-08, $box-shadow-focus: 0 0 0 4px rgba($blue-500, 0.25)) { + background-color: ide-btn-var($btn-type, background, $bg-normal); + box-shadow: inset 0 0 0 1px ide-btn-var($btn-type, border, $border-normal); + + &:hover, + &:focus { + background-color: ide-btn-var($btn-type, background, $bg-hover); + } + + &:hover { + box-shadow: inset 0 0 0 ide-btn-var($btn-type, hover-border-width, $border-width-hover) ide-btn-var($btn-type, hover-border, $border-hover), + 0 2px 2px 0 $box-shadow-hover; + } + + &:focus { + box-shadow: inset 0 0 0 ide-btn-var($btn-type, hover-border-width, $border-width-hover) ide-btn-var($btn-type, hover-border, $border-focus), + ide-btn-var($btn-type, focus-box-shadow, $box-shadow-focus); + } + + &:active:focus { + background-color: ide-btn-var($btn-type, background, $bg-active); + box-shadow: inset 0 0 0 ide-btn-var($btn-type, hover-border-width, $border-width-hover) ide-btn-var($btn-type, hover-border, $border-active), + ide-btn-var($btn-type, focus-box-shadow, $box-shadow-focus); + } + } + + .btn-default.gl-button.gl-button { + color: var(--ide-input-color, $gl-text-color); + + @include ide-gl-button(default, $white, $gray-50, $gray-100, $gray-200, $gray-300, $gray-300, $gray-400); + } + + .btn-success.gl-button.gl-button { + @include ide-gl-button(success, $green-500, $green-600, $green-800, $green-600, $green-800, $green-800, $green-900); + } + + .btn-info.gl-button.gl-button, + .btn-primary.gl-button.gl-button { + @include ide-gl-button(primary, $blue-500, $blue-600, $blue-800, $blue-600, $blue-800, $blue-800, $blue-900); + } + + .btn-danger.btn-danger-secondary.gl-button.gl-button { + color: var(--ide-input-color, $red-500); + + @include ide-gl-button(danger-secondary, $white, $red-50, $red-100, $red-500, $red-600, $red-600, $red-700); + } + + .btn[disabled].gl-button.gl-button { + color: var(--ide-btn-disabled-color, $gl-text-color-disabled); + + @include ide-gl-button(disabled, $gray-10, $gray-10, $gray-10, $gray-100, $gray-100, $gray-100, $gray-100, 1px, transparent, transparent); + } + .md table:not(.code) tbody { background-color: var(--ide-border-color, $white); } diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss b/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss index 37e6be9849b..41f9a8e6db7 100644 --- a/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss +++ b/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss @@ -20,20 +20,28 @@ --ide-btn-default-background: transparent; --ide-btn-default-border: #bfbfbf; --ide-btn-default-hover-border: #d8d8d8; + --ide-btn-default-hover-border-width: 2px; + --ide-btn-default-focus-box-shadow: 0 0 0 1px #bfbfbf; --ide-btn-primary-background: #1068bf; --ide-btn-primary-border: #428fdc; --ide-btn-primary-hover-border: #63a6e9; + --ide-btn-primary-hover-border-width: 2px; + --ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9; --ide-btn-success-background: #217645; --ide-btn-success-border: #108548; --ide-btn-success-hover-border: #2da160; + --ide-btn-success-hover-border-width: 2px; + --ide-btn-success-focus-box-shadow: 0 0 0 1px #2da160; + --ide-btn-disabled-background: transparent; --ide-btn-disabled-border: rgba(223, 223, 223, 0.24); + --ide-btn-disabled-hover-border: rgba(223, 223, 223, 0.24); + --ide-btn-disabled-hover-border-width: 1px; + --ide-btn-disabled-focus-box-shadow: 0 0 0 0 transparent; --ide-btn-disabled-color: rgba(145, 145, 145, 0.48); - --ide-btn-hover-border-width: 2px; - --ide-dropdown-background: #404040; --ide-dropdown-hover-background: #525252; diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss b/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss index 0ef0834d8db..ccb6f7a333b 100644 --- a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss +++ b/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss @@ -18,22 +18,30 @@ --ide-input-color: #fff; --ide-btn-default-background: transparent; - --ide-btn-default-border: var(--ide-input-border); + --ide-btn-default-border: #d8d8d8; --ide-btn-default-hover-border: #d8d8d8; + --ide-btn-default-hover-border-width: 2px; + --ide-btn-default-focus-box-shadow: 0 0 0 1px #d8d8d8; --ide-btn-primary-background: #1068bf; --ide-btn-primary-border: #428fdc; --ide-btn-primary-hover-border: #63a6e9; + --ide-btn-primary-hover-border-width: 2px; + --ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9; --ide-btn-success-background: #217645; --ide-btn-success-border: #108548; --ide-btn-success-hover-border: #2da160; + --ide-btn-success-hover-border-width: 2px; + --ide-btn-success-focus-box-shadow: 0 0 0 1px #2da160; + --ide-btn-disabled-background: transparent; --ide-btn-disabled-border: rgba(223, 223, 223, 0.24); + --ide-btn-disabled-hover-border: rgba(223, 223, 223, 0.24); + --ide-btn-disabled-hover-border-width: 1px; + --ide-btn-disabled-focus-box-shadow: transparent; --ide-btn-disabled-color: rgba(145, 145, 145, 0.48); - --ide-btn-hover-border-width: 2px; - --ide-dropdown-background: #004c61; --ide-dropdown-hover-background: #00617a; diff --git a/app/assets/stylesheets/page_bundles/jira_connect.scss b/app/assets/stylesheets/page_bundles/jira_connect.scss index 83d16f29d49..b8cdd120e04 100644 --- a/app/assets/stylesheets/page_bundles/jira_connect.scss +++ b/app/assets/stylesheets/page_bundles/jira_connect.scss @@ -13,6 +13,7 @@ $atlaskit-border-color: #dfe1e6; padding-top: $gl-padding-4; .ak-button { + align-items: center; height: auto; margin-left: $btn-margin-5; } @@ -20,6 +21,74 @@ $atlaskit-border-color: #dfe1e6; } } +$header-height: 40px; + +.jira-connect-header { + border-bottom: 1px solid $gray-100; + display: flex; + align-items: center; + justify-content: center; + min-height: $header-height; + padding-left: 16px; + padding-right: 16px; + position: fixed; + top: 0; + left: 0; + right: 0; +} + +.jira-connect-user { + float: right; + position: relative; + top: -30px; +} + +.jira-connect-app { + margin-top: $header-height; + max-width: 600px; + padding-top: 48px; + padding-left: 16px; + padding-right: 16px; + margin-left: auto; + margin-right: auto; + text-align: center; +} + +.gl-mt-5 { + margin-top: 16px; +} + +.heading-with-border { + border-bottom: 1px solid $gray-100; + display: inline-block; + padding-bottom: 16px; +} + +svg { + fill: currentColor; + + &.s16 { + height: 16px; + width: 16px; + } +} + +.ak-field-group label { + text-align: left; +} + +.ak-button__appearance-primary { + &:hover { + color: $white; + text-decoration: none; + } + + svg { + align-self: center; + margin-left: 4px; + } +} + .subscriptions { tbody { tr { @@ -31,3 +100,11 @@ $atlaskit-border-color: #dfe1e6; } } } + +.empty-subscriptions { + color: $gray-900; +} + +.browser-limitations-notice { + margin-top: 32px; +} diff --git a/app/assets/stylesheets/page_bundles/jira_connect_users.scss b/app/assets/stylesheets/page_bundles/jira_connect_users.scss new file mode 100644 index 00000000000..6725bf8f1a1 --- /dev/null +++ b/app/assets/stylesheets/page_bundles/jira_connect_users.scss @@ -0,0 +1,13 @@ +@import 'mixins_and_variables_and_functions'; + +.jira-connect-users-container { + margin-left: auto; + margin-right: auto; + width: px-to-rem(350px); +} + +.devise-layout-html body .navless-container { + @include media-breakpoint-down(xs) { + padding-top: 65px; + } +} diff --git a/app/controllers/jira_connect/events_controller.rb b/app/controllers/jira_connect/events_controller.rb index 8f79c82d847..d833491b8f7 100644 --- a/app/controllers/jira_connect/events_controller.rb +++ b/app/controllers/jira_connect/events_controller.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true class JiraConnect::EventsController < JiraConnect::ApplicationController + # See https://developer.atlassian.com/cloud/jira/software/app-descriptor/#lifecycle + skip_before_action :verify_atlassian_jwt!, only: :installed before_action :verify_qsh_claim!, only: :uninstalled def installed + return head :ok if atlassian_jwt_valid? + installation = JiraConnectInstallation.new(install_params) if installation.save diff --git a/app/controllers/jira_connect/users_controller.rb b/app/controllers/jira_connect/users_controller.rb new file mode 100644 index 00000000000..571d9f87779 --- /dev/null +++ b/app/controllers/jira_connect/users_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class JiraConnect::UsersController < ApplicationController + feature_category :integrations + + layout 'devise_experimental_onboarding_issues' + + def show + @jira_app_link = params.delete(:return_to) + end +end diff --git a/app/helpers/external_link_helper.rb b/app/helpers/external_link_helper.rb index 9dbad1f5032..bf47087543f 100644 --- a/app/helpers/external_link_helper.rb +++ b/app/helpers/external_link_helper.rb @@ -3,7 +3,7 @@ module ExternalLinkHelper def external_link(body, url, options = {}) link_to url, { target: '_blank', rel: 'noopener noreferrer' }.merge(options) do - "#{body} #{icon('external-link')}".html_safe + "#{body} #{sprite_icon('external-link')}".html_safe end end end diff --git a/app/views/jira_connect/subscriptions/index.html.haml b/app/views/jira_connect/subscriptions/index.html.haml index 349b3328c12..655c413f2a6 100644 --- a/app/views/jira_connect/subscriptions/index.html.haml +++ b/app/views/jira_connect/subscriptions/index.html.haml @@ -1,37 +1,61 @@ -%h1 - GitLab for Jira Configuration - -%form#add-subscription-form.subscription-form{ action: jira_connect_subscriptions_path } - .ak-field-group - %label - Namespace - - .ak-field-group.field-group-input - %input#namespace-input.ak-field-text{ type: 'text', required: true } - %button.ak-button.ak-button__appearance-primary{ type: 'submit' } - Link namespace to Jira - -%table.subscriptions - %thead - %tr - %th Namespace - %th Added - %th - %tbody - - @subscriptions.each do |subscription| - %tr - %td= subscription.namespace.full_path - %td= subscription.created_at - %td= link_to 'Remove', jira_connect_subscription_path(subscription), class: 'remove-subscription' - -%p - %strong Browser limitations: - Adding a namespace currently works only in browsers that allow cross site cookies. Please make sure to use - %a{ href: 'https://www.mozilla.org/en-US/firefox/', target: '_blank', rel: 'noopener noreferrer' } Firefox - or - %a{ href: 'https://www.google.com/chrome/index.html', target: '_blank', rel: 'noopener noreferrer' } Google Chrome - or enable cross-site cookies in your browser when adding a namespace. - %a{ href: 'https://gitlab.com/gitlab-org/gitlab/-/issues/263509', target: '_blank', rel: 'noopener noreferrer' } Learn more +%header.jira-connect-header + = brand_header_logo + +.jira-connect-user + - if current_user + - user_link = link_to(current_user.to_reference, user_path(current_user), target: '_blank', rel: 'noopener noreferrer') + = _('Signed in to GitLab as %{user_link}').html_safe % { user_link: user_link } + - elsif @subscriptions.present? + = link_to _('Sign in to GitLab'), jira_connect_users_path, target: '_blank', rel: 'noopener noreferrer', class: 'js-jira-connect-sign-in' + +.jira-connect-app + %h1 + GitLab for Jira Configuration + + - if current_user.blank? && @subscriptions.empty? + %h2.heading-with-border Sign in to GitLab.com to get started. + + .gl-mt-5 + = external_link _('Sign in to GitLab'), jira_connect_users_path, class: 'ak-button ak-button__appearance-primary js-jira-connect-sign-in' + + .gl-mt-5 + %p Note: this integration only works with accounts on GitLab.com (SaaS). + - else + %form#add-subscription-form.subscription-form{ action: jira_connect_subscriptions_path } + .ak-field-group + %label + GitLab namespace + + .ak-field-group.field-group-input + %input#namespace-input.ak-field-text{ type: 'text', required: true, placeholder: 'e.g. "MyCompany" or "MyCompany/GroupName"' } + %button.ak-button.ak-button__appearance-primary{ type: 'submit' } + Link namespace to Jira + + - if @subscriptions.present? + %table.subscriptions + %thead + %tr + %th Namespace + %th Added + %th + %tbody + - @subscriptions.each do |subscription| + %tr + %td= subscription.namespace.full_path + %td= subscription.created_at + %td= link_to 'Remove', jira_connect_subscription_path(subscription), class: 'remove-subscription' + - else + %h4.empty-subscriptions + No linked namespaces + + %p.browser-limitations-notice + %strong Browser limitations: + Adding a namespace currently works only in browsers that allow cross site cookies. Please make sure to use + %a{ href: 'https://www.mozilla.org/en-US/firefox/', target: '_blank', rel: 'noopener noreferrer' } Firefox + or + %a{ href: 'https://www.google.com/chrome/index.html', target: '_blank', rel: 'noopener noreferrer' } Google Chrome + or enable cross-site cookies in your browser when adding a namespace. + %a{ href: 'https://gitlab.com/gitlab-org/gitlab/-/issues/263509', target: '_blank', rel: 'noopener noreferrer' } Learn more = page_specific_javascript_tag('jira_connect.js') - add_page_specific_style 'page_bundles/jira_connect' diff --git a/app/views/jira_connect/users/show.html.haml b/app/views/jira_connect/users/show.html.haml new file mode 100644 index 00000000000..2ff92ab0dc8 --- /dev/null +++ b/app/views/jira_connect/users/show.html.haml @@ -0,0 +1,12 @@ +.jira-connect-users-container.gl-text-center + - user_link = link_to(current_user.to_reference, user_path(current_user), target: '_blank', rel: 'noopener noreferrer') + %h2= _('You are signed into GitLab as %{user_link}').html_safe % { user_link: user_link } + + %p= s_('Integrations|You can now close this window and return to the GitLab for Jira application.') + + - if @jira_app_link + %p= external_link s_('Integrations|Return to GitLab for Jira'), @jira_app_link, class: 'btn btn-success' + + %p= link_to _('Sign out'), destroy_user_session_path, method: :post + +- add_page_specific_style 'page_bundles/jira_connect_users' diff --git a/changelogs/unreleased/229114-jira-connect-install-webhook-for-updates.yml b/changelogs/unreleased/229114-jira-connect-install-webhook-for-updates.yml new file mode 100644 index 00000000000..d86468981bc --- /dev/null +++ b/changelogs/unreleased/229114-jira-connect-install-webhook-for-updates.yml @@ -0,0 +1,5 @@ +--- +title: Fix Jira Connect App update webhooks +merge_request: 45151 +author: +type: fixed diff --git a/changelogs/unreleased/235888-gitlab-for-jira-app-should-display-who-is-logged-in.yml b/changelogs/unreleased/235888-gitlab-for-jira-app-should-display-who-is-logged-in.yml new file mode 100644 index 00000000000..bdcd4905d71 --- /dev/null +++ b/changelogs/unreleased/235888-gitlab-for-jira-app-should-display-who-is-logged-in.yml @@ -0,0 +1,5 @@ +--- +title: Add user sign in indicator to Jira connect app +merge_request: 42628 +author: +type: changed diff --git a/config/application.rb b/config/application.rb index bb961877f4c..7bb70c13580 100644 --- a/config/application.rb +++ b/config/application.rb @@ -182,6 +182,7 @@ module Gitlab config.assets.precompile << "page_bundles/ide.css" config.assets.precompile << "page_bundles/issues_list.css" config.assets.precompile << "page_bundles/jira_connect.css" + config.assets.precompile << "page_bundles/jira_connect_users.css" config.assets.precompile << "page_bundles/merge_conflicts.css" config.assets.precompile << "page_bundles/merge_requests.css" config.assets.precompile << "page_bundles/milestone.css" diff --git a/config/feature_flags/development/global_default_branch_name.yml b/config/feature_flags/development/global_default_branch_name.yml index 57b324a6da2..2954ed74062 100644 --- a/config/feature_flags/development/global_default_branch_name.yml +++ b/config/feature_flags/development/global_default_branch_name.yml @@ -1,7 +1,7 @@ --- name: global_default_branch_name -introduced_by_url: -rollout_issue_url: -group: +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35269 +rollout_issue_url: +group: group::source code type: development default_enabled: true diff --git a/config/feature_flags/development/mr_commit_neighbor_nav.yml b/config/feature_flags/development/mr_commit_neighbor_nav.yml index 5fcacbdd3fe..7b540e14464 100644 --- a/config/feature_flags/development/mr_commit_neighbor_nav.yml +++ b/config/feature_flags/development/mr_commit_neighbor_nav.yml @@ -1,7 +1,7 @@ --- name: mr_commit_neighbor_nav -introduced_by_url: -rollout_issue_url: -group: +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28596 +rollout_issue_url: +group: group::source code type: development default_enabled: true diff --git a/config/routes/jira_connect.rb b/config/routes/jira_connect.rb index a3b786b60f0..72b3f04f5e5 100644 --- a/config/routes/jira_connect.rb +++ b/config/routes/jira_connect.rb @@ -5,6 +5,7 @@ namespace :jira_connect do root to: proc { [404, {}, ['']] }, as: 'base' get 'app_descriptor' => 'app_descriptor#show' + get :users, to: 'users#show' namespace :events do post 'installed' diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index 184909e0099..3d4c033e676 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -385,8 +385,8 @@ end GitLab's GraphQL API is versionless, which means we maintain backwards compatibility with older versions of the API with every change. Rather than removing a field or [enum value](#enums), we need to _deprecate_ it instead. -In future, GitLab -[may remove deprecated parts of the schema](https://gitlab.com/gitlab-org/gitlab/-/issues/32292). +The deprecated parts of the schema can then be removed in a future release +in accordance with [GitLab's deprecation process](../api/graphql/index.md#deprecation-process). Fields and enum values are deprecated using the `deprecated` property. The value of the property is a `Hash` of: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0be158add6f..36da7340c60 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -14149,6 +14149,9 @@ msgstr "" msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults." msgstr "" +msgid "Integrations|Return to GitLab for Jira" +msgstr "" + msgid "Integrations|Save settings?" msgstr "" @@ -14173,6 +14176,9 @@ msgstr "" msgid "Integrations|When a Jira issue is mentioned in a commit or merge request a remote link and comment (if enabled) will be created." msgstr "" +msgid "Integrations|You can now close this window and return to the GitLab for Jira application." +msgstr "" + msgid "Interested parties can even contribute by pushing commits if they want to." msgstr "" @@ -24241,6 +24247,9 @@ msgstr "" msgid "Sign in to \"%{group_name}\"" msgstr "" +msgid "Sign in to GitLab" +msgstr "" + msgid "Sign in using smart card" msgstr "" @@ -24286,6 +24295,9 @@ msgstr "" msgid "Signed in" msgstr "" +msgid "Signed in to GitLab as %{user_link}" +msgstr "" + msgid "Signed in with %{authentication} authentication" msgstr "" @@ -29859,6 +29871,9 @@ msgstr "" msgid "You are receiving this message because you are a GitLab administrator for %{url}." msgstr "" +msgid "You are signed into GitLab as %{user_link}" +msgstr "" + msgid "You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico." msgstr "" diff --git a/package.json b/package.json index ffe38235d7e..1511c095d82 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", "@gitlab/svgs": "1.171.0", - "@gitlab/ui": "21.30.1", + "@gitlab/ui": "21.32.1", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-3", "@rails/ujs": "^6.0.3-2", diff --git a/spec/controllers/jira_connect/events_controller_spec.rb b/spec/controllers/jira_connect/events_controller_spec.rb index d1a2dd6e7af..8a07f69e480 100644 --- a/spec/controllers/jira_connect/events_controller_spec.rb +++ b/spec/controllers/jira_connect/events_controller_spec.rb @@ -4,14 +4,20 @@ require 'spec_helper' RSpec.describe JiraConnect::EventsController do describe '#installed' do - subject do - post :installed, params: { - clientKey: '1234', - sharedSecret: 'secret', + let(:client_key) { '1234' } + let(:shared_secret) { 'secret' } + let(:params) do + { + clientKey: client_key, + sharedSecret: shared_secret, baseUrl: 'https://test.atlassian.net' } end + subject do + post :installed, params: params + end + it 'saves the jira installation data' do expect { subject }.to change { JiraConnectInstallation.count }.by(1) end @@ -19,15 +25,15 @@ RSpec.describe JiraConnect::EventsController do it 'saves the correct values' do subject - installation = JiraConnectInstallation.find_by_client_key('1234') + installation = JiraConnectInstallation.find_by_client_key(client_key) - expect(installation.shared_secret).to eq('secret') + expect(installation.shared_secret).to eq(shared_secret) expect(installation.base_url).to eq('https://test.atlassian.net') end context 'client key already exists' do it 'returns 422' do - create(:jira_connect_installation, client_key: '1234') + create(:jira_connect_installation, client_key: client_key) subject @@ -35,6 +41,23 @@ RSpec.describe JiraConnect::EventsController do end end + context 'when it is a version update and shared_secret is not sent' do + let(:params) do + { + clientKey: client_key, + baseUrl: 'https://test.atlassian.net' + } + end + + it 'validates the JWT token in authorization header and returns 200 without creating a new installation' do + create(:jira_connect_installation, client_key: client_key, shared_secret: shared_secret) + request.headers["Authorization"] = "Bearer #{Atlassian::Jwt.encode({ iss: client_key }, shared_secret)}" + + expect { subject }.not_to change { JiraConnectInstallation.count } + expect(response).to have_gitlab_http_status(:ok) + end + end + describe '#uninstalled' do let!(:installation) { create(:jira_connect_installation) } let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/events/uninstalled', 'POST', 'https://gitlab.test') } diff --git a/spec/frontend/static_site_editor/graphql/resolvers/has_submitted_changes_spec.js b/spec/frontend/static_site_editor/graphql/resolvers/has_submitted_changes_spec.js new file mode 100644 index 00000000000..0670b240a3f --- /dev/null +++ b/spec/frontend/static_site_editor/graphql/resolvers/has_submitted_changes_spec.js @@ -0,0 +1,27 @@ +import appDataQuery from '~/static_site_editor/graphql/queries/app_data.query.graphql'; +import hasSubmittedChanges from '~/static_site_editor/graphql/resolvers/has_submitted_changes'; + +describe('static_site_editor/graphql/resolvers/has_submitted_changes', () => { + it('updates the cache with the data passed in input', () => { + const cachedData = { appData: { original: 'foo' } }; + const newValue = { input: { hasSubmittedChanges: true } }; + + const cache = { + readQuery: jest.fn().mockReturnValue(cachedData), + writeQuery: jest.fn(), + }; + hasSubmittedChanges(null, newValue, { cache }); + + expect(cache.readQuery).toHaveBeenCalledWith({ query: appDataQuery }); + expect(cache.writeQuery).toHaveBeenCalledWith({ + query: appDataQuery, + data: { + appData: { + __typename: 'AppData', + original: 'foo', + hasSubmittedChanges: true, + }, + }, + }); + }); +}); diff --git a/spec/helpers/external_link_helper_spec.rb b/spec/helpers/external_link_helper_spec.rb index b1a1884d887..f5bb0568824 100644 --- a/spec/helpers/external_link_helper_spec.rb +++ b/spec/helpers/external_link_helper_spec.rb @@ -6,12 +6,15 @@ RSpec.describe ExternalLinkHelper do include IconsHelper it 'returns external link with icon' do - expect(external_link('https://gitlab.com', 'https://gitlab.com').to_s) - .to eq('<a target="_blank" rel="noopener noreferrer" href="https://gitlab.com">https://gitlab.com <i aria-hidden="true" data-hidden="true" class="fa fa-external-link"></i></a>') + link = external_link('https://gitlab.com', 'https://gitlab.com').to_s + expect(link).to start_with('<a target="_blank" rel="noopener noreferrer" href="https://gitlab.com">https://gitlab.com') + expect(link).to include('data-testid="external-link-icon"') end it 'allows options when creating external link with icon' do - expect(external_link('https://gitlab.com', 'https://gitlab.com', { "data-foo": "bar", class: "externalLink" }).to_s) - .to eq('<a target="_blank" rel="noopener noreferrer" data-foo="bar" class="externalLink" href="https://gitlab.com">https://gitlab.com <i aria-hidden="true" data-hidden="true" class="fa fa-external-link"></i></a>') + link = external_link('https://gitlab.com', 'https://gitlab.com', { "data-foo": "bar", class: "externalLink" }).to_s + + expect(link).to start_with('<a target="_blank" rel="noopener noreferrer" data-foo="bar" class="externalLink" href="https://gitlab.com">https://gitlab.com') + expect(link).to include('data-testid="external-link-icon"') end end diff --git a/spec/views/jira_connect/subscriptions/index.html.haml_spec.rb b/spec/views/jira_connect/subscriptions/index.html.haml_spec.rb new file mode 100644 index 00000000000..dcc36c93327 --- /dev/null +++ b/spec/views/jira_connect/subscriptions/index.html.haml_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'jira_connect/subscriptions/index.html.haml' do + let(:user) { build_stubbed(:user) } + + before do + allow(view).to receive(:current_user).and_return(user) + assign(:subscriptions, []) + end + + context 'when the user is signed in' do + it 'shows link to user profile' do + render + + expect(rendered).to have_link(user.to_reference) + end + end + + context 'when the user is not signed in' do + let(:user) { nil } + + it 'shows "Sign in" link' do + render + + expect(rendered).to have_link('Sign in to GitLab') + end + end +end diff --git a/yarn.lock b/yarn.lock index ccdabf8e53b..8c4777f2797 100644 --- a/yarn.lock +++ b/yarn.lock @@ -866,10 +866,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.171.0.tgz#abc3092bf804f0898301626130e0f3231834924a" integrity sha512-TPfdqIxQDda+0CQHhb9XdF50lmqDmADu6yT8R4oZi6BoUtWLdiHbyFt+RnVU6t7EmjIKicNAii7Ga+f2ljCfUA== -"@gitlab/ui@21.30.1": - version "21.30.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.30.1.tgz#561bd10c5264e48d16be4c5706827f40ec31286f" - integrity sha512-JdLA8x7fOk1qgsqBeNhlmg1VTJIrl+ECWea8whoyqWnI95GPuiM5ZSnehZHA6FoNwB9n/u1GnVSLCHcqvHZW0w== +"@gitlab/ui@21.32.1": + version "21.32.1" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.32.1.tgz#8ed29de1093afc36ea31d3cf93b047bcfe0c6543" + integrity sha512-ZZi/rJfDTXMytX1vNV2FU+l0fG6J7V2+1M7CWIkxdPIPVJ1LopkkfwnbBP29zrwBFBmguXcPoRfTjGa2oSNdbg== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" |