summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-28 09:08:25 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-28 09:08:25 +0000
commit5eab6dcdd923ca375b86d6993f20a3e37dbd7a51 (patch)
treecefd095312ba2dbaad845b699f42ed73df728e46
parentd3eb1e90a58fc97c9c1548e8ac9631c233e723ea (diff)
downloadgitlab-ce-5eab6dcdd923ca375b86d6993f20a3e37dbd7a51.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue53
-rw-r--r--app/models/ci/runner.rb2
-rw-r--r--app/models/ci/runner_machine.rb4
-rw-r--r--app/models/concerns/redis_cacheable.rb8
-rw-r--r--app/models/user.rb2
-rw-r--r--doc/api/graphql/getting_started.md1
-rw-r--r--doc/api/rest/index.md4
-rw-r--r--doc/development/service_ping/metrics_lifecycle.md4
-rw-r--r--doc/integration/external-issue-tracker.md2
-rw-r--r--doc/integration/jira/configure.md2
-rw-r--r--doc/integration/jira/index.md2
-rw-r--r--doc/integration/jira/issues.md2
-rw-r--r--doc/integration/partner_marketplace.md2
-rw-r--r--locale/gitlab.pot18
-rw-r--r--spec/frontend/issues/show/components/fields/description_spec.js23
-rw-r--r--spec/frontend/pages/shared/wikis/components/wiki_form_spec.js7
-rw-r--r--spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js23
-rw-r--r--spec/frontend/work_items/components/work_item_description_spec.js7
-rw-r--r--spec/lib/unnested_in_filters/rewriter_spec.rb26
-rw-r--r--spec/models/ci/runner_machine_spec.rb12
-rw-r--r--spec/models/ci/runner_spec.rb12
-rw-r--r--spec/models/concerns/redis_cacheable_spec.rb52
25 files changed, 185 insertions, 93 deletions
diff --git a/Gemfile b/Gemfile
index 9f7f7601085..13c119420ea 100644
--- a/Gemfile
+++ b/Gemfile
@@ -373,7 +373,7 @@ gem 'prometheus-client-mmap', '~> 0.17', require: 'prometheus/client'
gem 'warning', '~> 1.3.0'
group :development do
- gem 'lefthook', '~> 1.2.9', require: false
+ gem 'lefthook', '~> 1.3.2', require: false
gem 'rubocop'
gem 'solargraph', '~> 0.47.2', require: false
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 4ccdddc27d4..7c4dfd8bd84 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -314,7 +314,7 @@
{"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"},
{"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"},
{"name":"launchy","version":"2.5.0","platform":"ruby","checksum":"954243c4255920982ce682f89a42e76372dba94770bf09c23a523e204bdebef5"},
-{"name":"lefthook","version":"1.2.9","platform":"ruby","checksum":"1fd4a768e08fc624e756597fc628b3c7991267325974a7a5cc169595b425701d"},
+{"name":"lefthook","version":"1.3.2","platform":"ruby","checksum":"38607be9d670af5bfbbcb2159459f4403bc8e1b10885a923b9e512b3b72b3dec"},
{"name":"letter_opener","version":"1.7.0","platform":"ruby","checksum":"095bc0d58e006e5b43ea7d219e64ecf2de8d1f7d9dafc432040a845cf59b4725"},
{"name":"letter_opener_web","version":"2.0.0","platform":"ruby","checksum":"33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f"},
{"name":"libyajl2","version":"1.2.0","platform":"ruby","checksum":"1117cd1e48db013b626e36269bbf1cef210538ca6d2e62d3fa3db9ded005b258"},
diff --git a/Gemfile.lock b/Gemfile.lock
index dced424b042..6067d9009bc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -845,7 +845,7 @@ GEM
kramdown (~> 2.0)
launchy (2.5.0)
addressable (~> 2.7)
- lefthook (1.2.9)
+ lefthook (1.3.2)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (2.0.0)
@@ -1738,7 +1738,7 @@ DEPENDENCIES
knapsack (~> 1.21.1)
kramdown (~> 2.3.1)
kubeclient (~> 4.9.3)!
- lefthook (~> 1.2.9)
+ lefthook (~> 1.3.2)
letter_opener_web (~> 2.0.0)
license_finder (~> 7.0)
licensee (~> 9.15)
@@ -1893,4 +1893,4 @@ DEPENDENCIES
yajl-ruby (~> 1.4.3)
BUNDLED WITH
- 2.4.6
+ 2.4.7
diff --git a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
index 2a82986e90b..a350198d692 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
@@ -22,15 +22,6 @@ export default {
type: String,
required: true,
},
- markdownDocsPath: {
- type: String,
- required: true,
- },
- quickActionsDocsPath: {
- type: String,
- required: false,
- default: '',
- },
uploadsPath: {
type: String,
required: false,
@@ -41,21 +32,6 @@ export default {
required: false,
default: true,
},
- enablePreview: {
- type: Boolean,
- required: false,
- default: true,
- },
- autocompleteDataSources: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- enableAutocomplete: {
- type: Boolean,
- required: false,
- default: true,
- },
formFieldProps: {
type: Object,
required: true,
@@ -76,14 +52,10 @@ export default {
required: false,
default: false,
},
- drawioEnabled: {
- type: Boolean,
- required: false,
- default: false,
- },
},
data() {
return {
+ markdown: this.value || '',
editingMode: EDITING_MODE_MARKDOWN_FIELD,
autofocused: false,
};
@@ -97,14 +69,21 @@ export default {
return this.autofocus && !this.autofocused ? 'end' : false;
},
},
+ watch: {
+ value(val) {
+ this.markdown = val;
+ },
+ },
mounted() {
this.autofocusTextarea();
},
methods: {
updateMarkdownFromContentEditor({ markdown }) {
+ this.markdown = markdown;
this.$emit('input', markdown);
},
updateMarkdownFromMarkdownField({ target }) {
+ this.markdown = target.value;
this.$emit('input', target.value);
},
renderMarkdown(markdown) {
@@ -143,16 +122,12 @@ export default {
/>
<markdown-field
v-if="!isContentEditorActive"
+ v-bind="$attrs"
+ data-testid="markdown-field"
:markdown-preview-path="renderMarkdownPath"
can-attach-file
- :enable-autocomplete="enableAutocomplete"
- :textarea-value="value"
- :markdown-docs-path="markdownDocsPath"
- :quick-actions-docs-path="quickActionsDocsPath"
- :autocomplete-data-sources="autocompleteDataSources"
+ :textarea-value="markdown"
:uploads-path="uploadsPath"
- :enable-preview="enablePreview"
- :drawio-enabled="drawioEnabled"
show-content-editor-switcher
class="bordered-box"
@enableContentEditor="onEditingModeChange('contentEditor')"
@@ -161,7 +136,7 @@ export default {
<textarea
v-bind="formFieldProps"
ref="textarea"
- :value="value"
+ :value="markdown"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
:data-supports-quick-actions="supportsQuickActions"
@@ -176,7 +151,7 @@ export default {
<content-editor
:render-markdown="renderMarkdown"
:uploads-path="uploadsPath"
- :markdown="value"
+ :markdown="markdown"
:autofocus="contentEditorAutofocused"
:use-bottom-toolbar="useBottomToolbar"
@initialized="setEditorAsAutofocused"
@@ -186,7 +161,7 @@ export default {
/>
<input
v-bind="formFieldProps"
- :value="value"
+ :value="markdown"
data-qa-selector="markdown_editor_form_field"
type="hidden"
/>
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 4ff18adb69d..d7643956554 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -453,7 +453,7 @@ module Ci
new_version = values[:version]
schedule_runner_version_update(new_version) if new_version && values[:version] != version
- cache_attributes(values)
+ merge_cache_attributes(values)
# We save data without validation, it will always change due to `contacted_at`
update_columns(values) if persist_cached_data?
diff --git a/app/models/ci/runner_machine.rb b/app/models/ci/runner_machine.rb
index 8f504ab2468..78f2ad48c82 100644
--- a/app/models/ci/runner_machine.rb
+++ b/app/models/ci/runner_machine.rb
@@ -10,7 +10,7 @@ module Ci
ignore_column :machine_xid, remove_with: '15.11', remove_after: '2022-03-22'
# The `UPDATE_CONTACT_COLUMN_EVERY` defines how often the Runner Machine DB entry can be updated
- UPDATE_CONTACT_COLUMN_EVERY = 40.minutes..55.minutes
+ UPDATE_CONTACT_COLUMN_EVERY = (40.minutes)..(55.minutes)
belongs_to :runner
@@ -60,7 +60,7 @@ module Ci
new_version = values[:version]
schedule_runner_version_update(new_version) if new_version && values[:version] != version
- cache_attributes(values)
+ merge_cache_attributes(values)
# We save data without validation, it will always change due to `contacted_at`
update_columns(values) if persist_cached_data?
diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb
index f1d29ad5a90..460cb529715 100644
--- a/app/models/concerns/redis_cacheable.rb
+++ b/app/models/concerns/redis_cacheable.rb
@@ -33,6 +33,14 @@ module RedisCacheable
clear_memoization(:cached_attributes)
end
+ def merge_cache_attributes(values)
+ existing_attributes = Hash(cached_attributes)
+ merged_attributes = existing_attributes.merge(values.symbolize_keys)
+ return if merged_attributes == existing_attributes
+
+ cache_attributes(merged_attributes)
+ end
+
private
def cache_attribute_key
diff --git a/app/models/user.rb b/app/models/user.rb
index 8ff69d3f67f..421d2500f02 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -102,8 +102,6 @@ class User < ApplicationRecord
MINIMUM_DAYS_CREATED = 7
- ignore_columns %i[linkedin twitter skype website_url location organization], remove_with: '15.10', remove_after: '2023-02-22'
-
# Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour
# rubocop: disable CodeReuse/ServiceClass
diff --git a/doc/api/graphql/getting_started.md b/doc/api/graphql/getting_started.md
index 70e12df21f0..df1b1cdca7c 100644
--- a/doc/api/graphql/getting_started.md
+++ b/doc/api/graphql/getting_started.md
@@ -79,6 +79,7 @@ If you are running GitLab 12.0, enable the `graphql`
GraphQL queries can be run in a [Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session). For example, to search projects:
```ruby
+current_user = User.find_by_id(1)
query = <<~EOQ
query securityGetProjects($search: String!) {
projects(search: $search) {
diff --git a/doc/api/rest/index.md b/doc/api/rest/index.md
index 57d13f2a54f..0f1f74b9bd9 100644
--- a/doc/api/rest/index.md
+++ b/doc/api/rest/index.md
@@ -311,9 +311,9 @@ The following table shows the possible return codes for API requests.
| Return values | Description |
|--------------------------|-------------|
| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, and the resource itself is returned as JSON. |
+| `201 Created` | The `POST` request was successful, and the resource is returned as JSON. |
| `202 Accepted` | The `GET`, `PUT` or `DELETE` request was successful, and the resource is scheduled for processing. |
| `204 No Content` | The server has successfully fulfilled the request, and there is no additional content to send in the response payload body. |
-| `201 Created` | The `POST` request was successful, and the resource is returned as JSON. |
| `304 Not Modified` | The resource hasn't been modified since the last request. |
| `400 Bad Request` | A required attribute of the API request is missing. For example, the title of an issue is not given. |
| `401 Unauthorized` | The user isn't authenticated. A valid [user token](#authentication) is necessary. |
@@ -321,7 +321,7 @@ The following table shows the possible return codes for API requests.
| `404 Not Found` | A resource couldn't be accessed. For example, an ID for a resource couldn't be found. |
| `405 Method Not Allowed` | The request isn't supported. |
| `409 Conflict` | A conflicting resource already exists. For example, creating a project with a name that already exists. |
-| `412` | The request was denied. This can happen if the `If-Unmodified-Since` header is provided when trying to delete a resource, which was modified in between. |
+| `412 Precondition Failed`| The request was denied. This can happen if the `If-Unmodified-Since` header is provided when trying to delete a resource, which was modified in between. |
| `422 Unprocessable` | The entity couldn't be processed. |
| `429 Too Many Requests` | The user exceeded the [application rate limits](../../administration/instance_limits.md#rate-limits). |
| `500 Server Error` | While handling the request, something went wrong on the server. |
diff --git a/doc/development/service_ping/metrics_lifecycle.md b/doc/development/service_ping/metrics_lifecycle.md
index 8a8ceae1f4c..1c9cc5b15b7 100644
--- a/doc/development/service_ping/metrics_lifecycle.md
+++ b/doc/development/service_ping/metrics_lifecycle.md
@@ -82,10 +82,6 @@ To remove a metric:
1. Create an issue for removing the metric if none exists yet. The issue needs to outline why the metric should be deleted. You can use this issue to document the removal process.
-1. Check the following YAML files and verify the metric is not used in an aggregate:
- - [`config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/aggregates/)
- - [`ee/config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/aggregates/)
-
1. Verify the metric is not used to calculate the conversational index. The
conversational index is a measure that reports back to self-managed instances
to inform administrators of the progress of DevOps adoption for the instance.
diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md
index a3c206176b9..355be006fe8 100644
--- a/doc/integration/external-issue-tracker.md
+++ b/doc/integration/external-issue-tracker.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# External issue tracker **(FREE)**
+# External issue trackers **(FREE)**
GitLab has an [issue tracker](../user/project/issues/index.md), but you can
configure an external issue tracker per GitLab project.
diff --git a/doc/integration/jira/configure.md b/doc/integration/jira/configure.md
index 03d742703a1..dc00deec7a6 100644
--- a/doc/integration/jira/configure.md
+++ b/doc/integration/jira/configure.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Configure the Jira integration **(FREE)**
+# Configure Jira **(FREE)**
You can set up the [Jira integration](index.md#jira-integration)
by configuring your project settings in GitLab.
diff --git a/doc/integration/jira/index.md b/doc/integration/jira/index.md
index 53c6dfe9c07..5e83a69997e 100644
--- a/doc/integration/jira/index.md
+++ b/doc/integration/jira/index.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Jira integrations **(FREE)**
+# Jira **(FREE)**
If your organization uses [Jira](https://www.atlassian.com/software/jira) issues,
you can [migrate your issues from Jira](../../user/project/import/jira.md) and work
diff --git a/doc/integration/jira/issues.md b/doc/integration/jira/issues.md
index 4e76cc4e606..d110ea4b7a1 100644
--- a/doc/integration/jira/issues.md
+++ b/doc/integration/jira/issues.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Jira integration issue management **(FREE)**
+# Jira issue management **(FREE)**
To integrate issue management with Jira, [configure Jira](index.md#jira-integration)
and [enable the integration](configure.md) in GitLab.
diff --git a/doc/integration/partner_marketplace.md b/doc/integration/partner_marketplace.md
index 987ae7e21b3..010ce78c9fd 100644
--- a/doc/integration/partner_marketplace.md
+++ b/doc/integration/partner_marketplace.md
@@ -4,7 +4,7 @@ group: Commerce Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Marketplace partner integration
+# Marketplace partners
GitLab supports automation for selected distribution marketplaces to process sales of GitLab products to authorized
channel partners. Marketplace partners can use the GitLab Marketplace APIs to integrate their systems with GitLab to
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index cdcb2296900..c35193d0dc9 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5237,6 +5237,11 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve a pending member"
+msgid_plural "Approve %d pending members"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Approve merge request"
msgstr ""
@@ -5249,6 +5254,16 @@ msgstr ""
msgid "Approved MRs"
msgstr ""
+msgid "Approved members will use an additional seat in your subscription, which may override your user cap."
+msgid_plural "Approved members will use an additional %d seats in your subscription, which may override your user cap."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Approved members will use an additional seat in your subscription."
+msgid_plural "Approved members will use an additional %d seats in your subscription."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Approved the current merge request."
msgstr ""
@@ -5324,9 +5339,6 @@ msgstr ""
msgid "Are you sure you want to approve %{user}?"
msgstr ""
-msgid "Are you sure you want to approve all users?"
-msgstr ""
-
msgid "Are you sure you want to attempt to merge?"
msgstr ""
diff --git a/spec/frontend/issues/show/components/fields/description_spec.js b/spec/frontend/issues/show/components/fields/description_spec.js
index 273ddfdd5d4..d795a5e2c1a 100644
--- a/spec/frontend/issues/show/components/fields/description_spec.js
+++ b/spec/frontend/issues/show/components/fields/description_spec.js
@@ -80,17 +80,18 @@ describe('Description field component', () => {
});
it('uses the MarkdownEditor component to edit markdown', () => {
- expect(findMarkdownEditor().props()).toEqual(
- expect.objectContaining({
- value: 'test',
- renderMarkdownPath: '/',
- markdownDocsPath: '/',
- quickActionsDocsPath: expect.any(String),
- autofocus: true,
- supportsQuickActions: true,
- enableAutocomplete: true,
- }),
- );
+ expect(findMarkdownEditor().props()).toMatchObject({
+ value: 'test',
+ renderMarkdownPath: '/',
+ autofocus: true,
+ supportsQuickActions: true,
+ });
+
+ expect(findMarkdownEditor().vm.$attrs).toMatchObject({
+ 'enable-autocomplete': true,
+ 'markdown-docs-path': '/',
+ 'quick-actions-docs-path': expect.any(String),
+ });
});
it('triggers update with meta+enter', () => {
diff --git a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
index ffcfd1d9f78..53c87a62940 100644
--- a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
+++ b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
@@ -116,7 +116,6 @@ describe('WikiForm', () => {
expect.objectContaining({
value: pageInfoPersisted.content,
renderMarkdownPath: pageInfoPersisted.markdownPreviewPath,
- markdownDocsPath: pageInfoPersisted.markdownHelpPath,
uploadsPath: pageInfoPersisted.uploadsPath,
autofocus: pageInfoPersisted.persisted,
}),
@@ -126,6 +125,10 @@ describe('WikiForm', () => {
id: 'wiki_content',
name: 'wiki[content]',
});
+
+ expect(markdownEditor.vm.$attrs['markdown-docs-path']).toEqual(
+ pageInfoPersisted.markdownHelpPath,
+ );
});
it.each`
@@ -172,7 +175,7 @@ describe('WikiForm', () => {
nextTick();
- expect(findMarkdownEditor().props('enablePreview')).toBe(enabled);
+ expect(findMarkdownEditor().vm.$attrs['enable-preview']).toBe(enabled);
});
it.each`
diff --git a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
index 26b536984ff..fa183cc64ef 100644
--- a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js
@@ -83,8 +83,28 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
});
});
+ it('passes down any additional props to markdown field component', () => {
+ const propsData = {
+ line: { text: 'hello world', richText: 'hello world' },
+ lines: [{ text: 'hello world', richText: 'hello world' }],
+ canSuggest: true,
+ };
+
+ buildWrapper({
+ propsData: { ...propsData, myCustomProp: 'myCustomValue', 'data-testid': 'custom id' },
+ });
+
+ expect(findMarkdownField().props()).toMatchObject(propsData);
+ expect(findMarkdownField().vm.$attrs).toMatchObject({
+ myCustomProp: 'myCustomValue',
+
+ // data-testid isn't copied over
+ 'data-testid': 'markdown-field',
+ });
+ });
+
it('renders markdown field textarea', () => {
- buildWrapper();
+ buildWrapper({ propsData: { supportsQuickActions: true } });
expect(findTextarea().attributes()).toEqual(
expect.objectContaining({
@@ -92,6 +112,7 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
name: formFieldName,
placeholder: formFieldPlaceholder,
'aria-label': formFieldAriaLabel,
+ 'data-supports-quick-actions': 'true',
}),
);
diff --git a/spec/frontend/work_items/components/work_item_description_spec.js b/spec/frontend/work_items/components/work_item_description_spec.js
index a12ec23c15a..ecb6404f261 100644
--- a/spec/frontend/work_items/components/work_item_description_spec.js
+++ b/spec/frontend/work_items/components/work_item_description_spec.js
@@ -117,10 +117,13 @@ describe('WorkItemDescription', () => {
await createComponent({ isEditing: true });
expect(findMarkdownEditor().props()).toMatchObject({
- autocompleteDataSources: autocompleteDataSources(fullPath, iid),
supportsQuickActions: true,
renderMarkdownPath: markdownPreviewPath(fullPath, iid),
- quickActionsDocsPath: wrapper.vm.$options.quickActionsDocsPath,
+ });
+
+ expect(findMarkdownEditor().vm.$attrs).toMatchObject({
+ 'autocomplete-data-sources': autocompleteDataSources(fullPath, iid),
+ 'quick-actions-docs-path': wrapper.vm.$options.quickActionsDocsPath,
});
});
});
diff --git a/spec/lib/unnested_in_filters/rewriter_spec.rb b/spec/lib/unnested_in_filters/rewriter_spec.rb
index bba27276037..fe34fba579b 100644
--- a/spec/lib/unnested_in_filters/rewriter_spec.rb
+++ b/spec/lib/unnested_in_filters/rewriter_spec.rb
@@ -69,21 +69,15 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:recorded_queries) { ActiveRecord::QueryRecorder.new { rewriter.rewrite.load } }
let(:relation) { User.where(state: :active, user_type: %i(support_bot alert_bot)).limit(2) }
- let(:users_default_select_fields) do
- User.default_select_columns
- .map { |field| "\"users\".\"#{field.name}\"" }
- .join(',')
- end
-
let(:expected_query) do
<<~SQL
SELECT
- #{users_default_select_fields}
+ "users".*
FROM
unnest('{1,2}'::smallint[]) AS "user_types"("user_type"),
LATERAL (
SELECT
- #{users_default_select_fields}
+ "users".*
FROM
"users"
WHERE
@@ -107,13 +101,13 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
SELECT
- #{users_default_select_fields}
+ "users".*
FROM
unnest(ARRAY(SELECT "users"."state" FROM "users")::character varying[]) AS "states"("state"),
unnest('{1,2}'::smallint[]) AS "user_types"("user_type"),
LATERAL (
SELECT
- #{users_default_select_fields}
+ "users".*
FROM
"users"
WHERE
@@ -135,12 +129,12 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
SELECT
- #{users_default_select_fields}
+ "users".*
FROM
unnest('{active,blocked,banned}'::charactervarying[]) AS "states"("state"),
LATERAL (
SELECT
- #{users_default_select_fields}
+ "users".*
FROM
"users"
WHERE
@@ -187,6 +181,8 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
+ SELECT
+ "users".*
FROM
"users"
WHERE
@@ -221,7 +217,7 @@ RSpec.describe UnnestedInFilters::Rewriter do
end
it 'changes the query' do
- expect(issued_query.gsub(/\s/, '')).to include(expected_query.gsub(/\s/, ''))
+ expect(issued_query.gsub(/\s/, '')).to start_with(expected_query.gsub(/\s/, ''))
end
end
@@ -230,6 +226,8 @@ RSpec.describe UnnestedInFilters::Rewriter do
let(:expected_query) do
<<~SQL
+ SELECT
+ "users".*
FROM
"users"
WHERE
@@ -259,7 +257,7 @@ RSpec.describe UnnestedInFilters::Rewriter do
end
it 'does not rewrite the in statement for the joined table' do
- expect(issued_query.gsub(/\s/, '')).to include(expected_query.gsub(/\s/, ''))
+ expect(issued_query.gsub(/\s/, '')).to start_with(expected_query.gsub(/\s/, ''))
end
end
diff --git a/spec/models/ci/runner_machine_spec.rb b/spec/models/ci/runner_machine_spec.rb
index f687f7f73d6..6162077a055 100644
--- a/spec/models/ci/runner_machine_spec.rb
+++ b/spec/models/ci/runner_machine_spec.rb
@@ -107,6 +107,18 @@ RSpec.describe Ci::RunnerMachine, feature_category: :runner_fleet, type: :model
heartbeat
end
+
+ context 'with new version having been cached' do
+ let(:version) { '15.0.1' }
+
+ before do
+ runner_machine.cache_attributes(version: version)
+ end
+
+ it 'does not lose cached version value' do
+ expect { heartbeat }.not_to change { runner_machine.version }.from(version)
+ end
+ end
end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 750b75684ec..a3d1dcc79ee 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -1122,6 +1122,18 @@ RSpec.describe Ci::Runner, type: :model, feature_category: :runner do
heartbeat
end
+
+ context 'with new version having been cached' do
+ let(:version) { '15.0.1' }
+
+ before do
+ runner.cache_attributes(version: version)
+ end
+
+ it 'does not lose cached version value' do
+ expect { heartbeat }.not_to change { runner.version }.from(version)
+ end
+ end
end
end
diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb
index c270f23defb..bf277b094ea 100644
--- a/spec/models/concerns/redis_cacheable_spec.rb
+++ b/spec/models/concerns/redis_cacheable_spec.rb
@@ -50,6 +50,58 @@ RSpec.describe RedisCacheable do
subject
end
+
+ context 'with existing cached attributes' do
+ before do
+ instance.cache_attributes({ existing_attr: 'value' })
+ end
+
+ it 'sets the cache attributes' do
+ Gitlab::Redis::Cache.with do |redis|
+ expect(redis).to receive(:set).with(cache_key, payload.to_json, anything).and_call_original
+ end
+
+ expect { subject }.to change { instance.cached_attribute(:existing_attr) }.from('value').to(nil)
+ end
+ end
+ end
+
+ describe '#merge_cache_attributes' do
+ subject { instance.merge_cache_attributes(payload) }
+
+ let(:existing_attributes) { { existing_attr: 'value', name: 'value' } }
+
+ before do
+ instance.cache_attributes(existing_attributes)
+ end
+
+ context 'with different attribute values' do
+ let(:payload) { { name: 'new_value' } }
+
+ it 'merges the cache attributes with existing values' do
+ Gitlab::Redis::Cache.with do |redis|
+ expect(redis).to receive(:set).with(cache_key, existing_attributes.merge(payload).to_json, anything)
+ .and_call_original
+ end
+
+ subject
+
+ expect(instance.cached_attribute(:existing_attr)).to eq 'value'
+ expect(instance.cached_attribute(:name)).to eq 'new_value'
+ end
+ end
+
+ context 'with no new or changed attribute values' do
+ let(:payload) { { name: 'value' } }
+
+ it 'does not try to set Redis key' do
+ Gitlab::Redis::Cache.with do |redis|
+ expect(redis).not_to receive(:set)
+ end
+
+ subject
+ end
+ end
end
describe '#cached_attr_reader', :clean_gitlab_redis_cache do