Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d3eb1e90a5
commit
5eab6dcdd9
2
Gemfile
2
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue