Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
017841e3c0
commit
89cb3fa774
35 changed files with 715 additions and 29 deletions
|
@ -102,6 +102,7 @@ include:
|
||||||
- local: .gitlab/ci/qa.gitlab-ci.yml
|
- local: .gitlab/ci/qa.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/reports.gitlab-ci.yml
|
- local: .gitlab/ci/reports.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/rails.gitlab-ci.yml
|
- local: .gitlab/ci/rails.gitlab-ci.yml
|
||||||
|
- local: .gitlab/ci/vendored-gems.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/review.gitlab-ci.yml
|
- local: .gitlab/ci/review.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/rules.gitlab-ci.yml
|
- local: .gitlab/ci/rules.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/setup.gitlab-ci.yml
|
- local: .gitlab/ci/setup.gitlab-ci.yml
|
||||||
|
|
|
@ -920,6 +920,16 @@
|
||||||
- <<: *if-merge-request
|
- <<: *if-merge-request
|
||||||
changes: [".gitlab/ci/rails.gitlab-ci.yml"]
|
changes: [".gitlab/ci/rails.gitlab-ci.yml"]
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# Vendored gems rules #
|
||||||
|
#######################
|
||||||
|
|
||||||
|
.vendor:rules:mail-smtp_pool:
|
||||||
|
rules:
|
||||||
|
- <<: *if-merge-request
|
||||||
|
changes: ["vendor/gems/mail-smtp_pool/**/*"]
|
||||||
|
- <<: *if-merge-request-title-run-all-rspec
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# Releases rules #
|
# Releases rules #
|
||||||
##################
|
##################
|
||||||
|
|
7
.gitlab/ci/vendored-gems.gitlab-ci.yml
Normal file
7
.gitlab/ci/vendored-gems.gitlab-ci.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
vendor mail-smtp_pool:
|
||||||
|
extends:
|
||||||
|
- .vendor:rules:mail-smtp_pool
|
||||||
|
needs: []
|
||||||
|
trigger:
|
||||||
|
include: vendor/gems/mail-smtp_pool/.gitlab-ci.yml
|
||||||
|
strategy: depend
|
1
Gemfile
1
Gemfile
|
@ -511,6 +511,7 @@ gem 'erubi', '~> 1.9.0'
|
||||||
# Monkey-patched in `config/initializers/mail_encoding_patch.rb`
|
# Monkey-patched in `config/initializers/mail_encoding_patch.rb`
|
||||||
# See https://gitlab.com/gitlab-org/gitlab/issues/197386
|
# See https://gitlab.com/gitlab-org/gitlab/issues/197386
|
||||||
gem 'mail', '= 2.7.1'
|
gem 'mail', '= 2.7.1'
|
||||||
|
gem 'mail-smtp_pool', '~> 0.1.0', path: 'vendor/gems/mail-smtp_pool', require: false
|
||||||
|
|
||||||
# File encryption
|
# File encryption
|
||||||
gem 'lockbox', '~> 0.6.2'
|
gem 'lockbox', '~> 0.6.2'
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
PATH
|
||||||
|
remote: vendor/gems/mail-smtp_pool
|
||||||
|
specs:
|
||||||
|
mail-smtp_pool (0.1.0)
|
||||||
|
connection_pool (~> 2.0)
|
||||||
|
mail (~> 2.7)
|
||||||
|
|
||||||
PATH
|
PATH
|
||||||
remote: vendor/shims/mimemagic
|
remote: vendor/shims/mimemagic
|
||||||
specs:
|
specs:
|
||||||
|
@ -1483,6 +1490,7 @@ DEPENDENCIES
|
||||||
loofah (~> 2.2)
|
loofah (~> 2.2)
|
||||||
lru_redux
|
lru_redux
|
||||||
mail (= 2.7.1)
|
mail (= 2.7.1)
|
||||||
|
mail-smtp_pool (~> 0.1.0)!
|
||||||
marginalia (~> 1.10.0)
|
marginalia (~> 1.10.0)
|
||||||
memory_profiler (~> 0.9)
|
memory_profiler (~> 0.9)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
GlFormCombobox,
|
GlFormCombobox,
|
||||||
GlFormGroup,
|
GlFormGroup,
|
||||||
GlFormSelect,
|
GlFormSelect,
|
||||||
|
GlFormInput,
|
||||||
GlFormTextarea,
|
GlFormTextarea,
|
||||||
GlIcon,
|
GlIcon,
|
||||||
GlLink,
|
GlLink,
|
||||||
|
@ -41,6 +42,7 @@ export default {
|
||||||
GlFormCombobox,
|
GlFormCombobox,
|
||||||
GlFormGroup,
|
GlFormGroup,
|
||||||
GlFormSelect,
|
GlFormSelect,
|
||||||
|
GlFormInput,
|
||||||
GlFormTextarea,
|
GlFormTextarea,
|
||||||
GlIcon,
|
GlIcon,
|
||||||
GlLink,
|
GlLink,
|
||||||
|
@ -128,6 +130,12 @@ export default {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
scopedVariablesEnabled() {
|
||||||
|
return !this.isGroup || this.glFeatures.scopedGroupVariables;
|
||||||
|
},
|
||||||
|
scopedVariablesAvailable() {
|
||||||
|
return !this.isGroup || this.glFeatures.groupScopedCiVariables;
|
||||||
|
},
|
||||||
variableValidationFeedback() {
|
variableValidationFeedback() {
|
||||||
return `${this.tokenValidationFeedback} ${this.maskedFeedback}`;
|
return `${this.tokenValidationFeedback} ${this.maskedFeedback}`;
|
||||||
},
|
},
|
||||||
|
@ -226,24 +234,27 @@ export default {
|
||||||
:label="__('Type')"
|
:label="__('Type')"
|
||||||
label-for="ci-variable-type"
|
label-for="ci-variable-type"
|
||||||
class="w-50 gl-mr-5"
|
class="w-50 gl-mr-5"
|
||||||
:class="{ 'w-100': isGroup }"
|
:class="{ 'w-100': !scopedVariablesEnabled }"
|
||||||
>
|
>
|
||||||
<gl-form-select id="ci-variable-type" v-model="variable_type" :options="typeOptions" />
|
<gl-form-select id="ci-variable-type" v-model="variable_type" :options="typeOptions" />
|
||||||
</gl-form-group>
|
</gl-form-group>
|
||||||
|
|
||||||
<gl-form-group
|
<gl-form-group
|
||||||
v-if="!isGroup"
|
v-if="scopedVariablesEnabled"
|
||||||
:label="__('Environment scope')"
|
:label="__('Environment scope')"
|
||||||
label-for="ci-variable-env"
|
label-for="ci-variable-env"
|
||||||
class="w-50"
|
class="w-50"
|
||||||
data-testid="environment-scope"
|
data-testid="environment-scope"
|
||||||
>
|
>
|
||||||
<ci-environments-dropdown
|
<ci-environments-dropdown
|
||||||
|
v-if="scopedVariablesAvailable"
|
||||||
class="w-100"
|
class="w-100"
|
||||||
:value="environment_scope"
|
:value="environment_scope"
|
||||||
@selectEnvironment="setEnvironmentScope"
|
@selectEnvironment="setEnvironmentScope"
|
||||||
@createClicked="addWildCardScope"
|
@createClicked="addWildCardScope"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<gl-form-input v-else v-model="environment_scope" class="w-100" readonly />
|
||||||
</gl-form-group>
|
</gl-form-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
|
import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
|
||||||
import { mapState, mapActions } from 'vuex';
|
import { mapState, mapActions } from 'vuex';
|
||||||
import { s__, __ } from '~/locale';
|
import { s__, __ } from '~/locale';
|
||||||
|
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||||
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
|
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
|
||||||
import CiVariablePopover from './ci_variable_popover.vue';
|
import CiVariablePopover from './ci_variable_popover.vue';
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ export default {
|
||||||
directives: {
|
directives: {
|
||||||
GlModalDirective,
|
GlModalDirective,
|
||||||
},
|
},
|
||||||
|
mixins: [glFeatureFlagsMixin()],
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['variables', 'valuesHidden', 'isGroup', 'isLoading', 'isDeleting']),
|
...mapState(['variables', 'valuesHidden', 'isGroup', 'isLoading', 'isDeleting']),
|
||||||
valuesButtonText() {
|
valuesButtonText() {
|
||||||
|
@ -68,7 +70,7 @@ export default {
|
||||||
return this.variables && this.variables.length > 0;
|
return this.variables && this.variables.length > 0;
|
||||||
},
|
},
|
||||||
fields() {
|
fields() {
|
||||||
if (this.isGroup) {
|
if (this.isGroup && !this.glFeatures.scopedGroupVariables) {
|
||||||
return this.$options.fields.filter((field) => field.key !== 'environment_scope');
|
return this.$options.fields.filter((field) => field.key !== 'environment_scope');
|
||||||
}
|
}
|
||||||
return this.$options.fields;
|
return this.$options.fields;
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import ContributorsGraphs from './components/contributors.vue';
|
import ContributorsGraphs from './components/contributors.vue';
|
||||||
import store from './stores';
|
import { createStore } from './stores';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const el = document.querySelector('.js-contributors-graph');
|
const el = document.querySelector('.js-contributors-graph');
|
||||||
|
|
||||||
if (!el) return null;
|
if (!el) return null;
|
||||||
|
|
||||||
|
const { projectGraphPath, projectBranch, defaultBranch } = el.dataset;
|
||||||
|
const store = createStore(defaultBranch);
|
||||||
|
|
||||||
return new Vue({
|
return new Vue({
|
||||||
el,
|
el,
|
||||||
store,
|
store,
|
||||||
|
@ -14,8 +17,8 @@ export default () => {
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
return createElement(ContributorsGraphs, {
|
return createElement(ContributorsGraphs, {
|
||||||
props: {
|
props: {
|
||||||
endpoint: el.dataset.projectGraphPath,
|
endpoint: projectGraphPath,
|
||||||
branch: el.dataset.projectBranch,
|
branch: projectBranch,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,12 +7,12 @@ import state from './state';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
export const createStore = () =>
|
export const createStore = (defaultBranch) =>
|
||||||
new Vuex.Store({
|
new Vuex.Store({
|
||||||
actions,
|
actions,
|
||||||
mutations,
|
mutations,
|
||||||
getters,
|
getters,
|
||||||
state: state(),
|
state: state(defaultBranch),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createStore();
|
export default createStore;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export default () => ({
|
export default (branch) => ({
|
||||||
loading: false,
|
loading: false,
|
||||||
chartData: null,
|
chartData: null,
|
||||||
branch: 'master',
|
branch,
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,9 +58,13 @@ export default {
|
||||||
<span v-if="request.hasWarnings">(!)</span>
|
<span v-if="request.hasWarnings">(!)</span>
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<span v-if="requestsWithWarnings.length">
|
<span v-if="requestsWithWarnings.length" class="gl-cursor-default">
|
||||||
<span id="performance-bar-request-selector-warning" v-html="glEmojiTag('warning')"></span>
|
<span id="performance-bar-request-selector-warning" v-html="glEmojiTag('warning')"></span>
|
||||||
<gl-popover target="performance-bar-request-selector-warning" :content="warningMessage" />
|
<gl-popover
|
||||||
|
placement="bottom"
|
||||||
|
target="performance-bar-request-selector-warning"
|
||||||
|
:content="warningMessage"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -35,8 +35,8 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<span v-if="hasWarnings">
|
<span v-if="hasWarnings" class="gl-cursor-default">
|
||||||
<span :id="htmlId" v-html="glEmojiTag('warning')"></span>
|
<span :id="htmlId" v-html="glEmojiTag('warning')"></span>
|
||||||
<gl-popover :target="htmlId" :content="warningMessage" />
|
<gl-popover placement="bottom" :target="htmlId" :content="warningMessage" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -10,6 +10,8 @@ module Groups
|
||||||
before_action :authorize_admin_group!
|
before_action :authorize_admin_group!
|
||||||
before_action :authorize_update_max_artifacts_size!, only: [:update]
|
before_action :authorize_update_max_artifacts_size!, only: [:update]
|
||||||
before_action :define_variables, only: [:show]
|
before_action :define_variables, only: [:show]
|
||||||
|
before_action :push_feature_flags, only: [:show]
|
||||||
|
before_action :push_licensed_features, only: [:show]
|
||||||
|
|
||||||
feature_category :continuous_integration
|
feature_category :continuous_integration
|
||||||
|
|
||||||
|
@ -91,6 +93,16 @@ module Groups
|
||||||
def update_group_params
|
def update_group_params
|
||||||
params.require(:group).permit(:max_artifacts_size)
|
params.require(:group).permit(:max_artifacts_size)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def push_feature_flags
|
||||||
|
push_frontend_feature_flag(:scoped_group_variables, group)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Overridden in EE
|
||||||
|
def push_licensed_features
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Groups::Settings::CiCdController.prepend_if_ee('EE::Groups::Settings::CiCdController')
|
||||||
|
|
|
@ -56,3 +56,5 @@ module Groups
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Groups::VariablesController.prepend_if_ee('EE::Groups::VariablesController')
|
||||||
|
|
|
@ -86,7 +86,7 @@ class Group < Namespace
|
||||||
validate :visibility_level_allowed_by_sub_groups
|
validate :visibility_level_allowed_by_sub_groups
|
||||||
validate :visibility_level_allowed_by_parent
|
validate :visibility_level_allowed_by_parent
|
||||||
validate :two_factor_authentication_allowed
|
validate :two_factor_authentication_allowed
|
||||||
validates :variables, nested_attributes_duplicates: true
|
validates :variables, nested_attributes_duplicates: { scope: :environment_scope }
|
||||||
|
|
||||||
validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
|
|
||||||
module Ci
|
module Ci
|
||||||
class GroupVariableEntity < Ci::BasicVariableEntity
|
class GroupVariableEntity < Ci::BasicVariableEntity
|
||||||
|
expose :environment_scope
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,4 +5,4 @@
|
||||||
= render 'shared/ref_switcher', destination: 'graphs'
|
= render 'shared/ref_switcher', destination: 'graphs'
|
||||||
= link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn gl-button btn-default'
|
= link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn gl-button btn-default'
|
||||||
|
|
||||||
.js-contributors-graph{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json), 'data-project-branch': current_ref }
|
.js-contributors-graph{ class: container_class, data: { project_graph_path: project_graph_path(@project, current_ref, format: :json), project_branch: current_ref, default_branch: @project.default_branch } }
|
||||||
|
|
5
changelogs/unreleased/230717-add-mail-smtp-pool-gem.yml
Normal file
5
changelogs/unreleased/230717-add-mail-smtp-pool-gem.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add support for SMTP connection pooling when sending emails
|
||||||
|
merge_request: 57805
|
||||||
|
author:
|
||||||
|
type: added
|
5
changelogs/unreleased/pb-popover-improvement.yml
Normal file
5
changelogs/unreleased/pb-popover-improvement.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Update popover placement and cursor on warning icon in PB
|
||||||
|
merge_request: 58552
|
||||||
|
author: Yogi (@yo)
|
||||||
|
type: changed
|
|
@ -22,3 +22,28 @@ if Rails.env.production?
|
||||||
openssl_verify_mode: 'peer' # See ActionMailer documentation for other possible options
|
openssl_verify_mode: 'peer' # See ActionMailer documentation for other possible options
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# To use an SMTP connection pool, uncomment the following section:
|
||||||
|
#
|
||||||
|
# require 'mail/smtp_pool'
|
||||||
|
#
|
||||||
|
# ActionMailer::Base.add_delivery_method :smtp_pool, Mail::SMTPPool
|
||||||
|
#
|
||||||
|
# if Rails.env.production?
|
||||||
|
# Rails.application.config.action_mailer.delivery_method = :smtp_pool
|
||||||
|
#
|
||||||
|
# ActionMailer::Base.delivery_method = :smtp_pool
|
||||||
|
# ActionMailer::Base.smtp_pool_settings = {
|
||||||
|
# pool: Mail::SMTPPool.create_pool(
|
||||||
|
# pool_size: Gitlab::Runtime.max_threads,
|
||||||
|
# address: "email.server.com",
|
||||||
|
# port: 465,
|
||||||
|
# user_name: "smtp",
|
||||||
|
# password: "123456",
|
||||||
|
# domain: "gitlab.company.com",
|
||||||
|
# authentication: :login,
|
||||||
|
# enable_starttls_auto: true,
|
||||||
|
# openssl_verify_mode: 'peer' # See ActionMailer documentation for other possible options
|
||||||
|
# )
|
||||||
|
# }
|
||||||
|
# end
|
||||||
|
|
|
@ -20,7 +20,7 @@ Return all of the raw SQL queries used to compute usage ping.
|
||||||
Example request:
|
Example request:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/usage_data/queries
|
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/usage_data/queries"
|
||||||
```
|
```
|
||||||
|
|
||||||
Sample response
|
Sample response
|
||||||
|
@ -66,3 +66,51 @@ Sample response
|
||||||
"ci_runners": "SELECT COUNT(\"ci_runners\".\"id\") FROM \"ci_runners\"",
|
"ci_runners": "SELECT COUNT(\"ci_runners\".\"id\") FROM \"ci_runners\"",
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## UsageDataNonSqlMetrics API
|
||||||
|
|
||||||
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57050) in GitLab 13.11.
|
||||||
|
> - [Deployed behind a feature flag](../user/feature_flags.md), disabled by default.
|
||||||
|
|
||||||
|
Return all non-SQL metrics data used in the usage ping.
|
||||||
|
|
||||||
|
Example request:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/usage_data/non_sql_metrics"
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"recorded_at": "2021-03-26T07:04:03.724Z",
|
||||||
|
"uuid": null,
|
||||||
|
"hostname": "localhost",
|
||||||
|
"version": "13.11.0-pre",
|
||||||
|
"installation_type": "gitlab-development-kit",
|
||||||
|
"active_user_count": -3,
|
||||||
|
"edition": "EE",
|
||||||
|
"license_md5": "bb8cd0d8a6d9569ff3f70b8927a1f949",
|
||||||
|
"license_id": null,
|
||||||
|
"historical_max_users": 0,
|
||||||
|
"licensee": {
|
||||||
|
"Name": "John Doe1"
|
||||||
|
},
|
||||||
|
"license_user_count": null,
|
||||||
|
"license_starts_at": "1970-01-01",
|
||||||
|
"license_expires_at": "2022-02-26",
|
||||||
|
"license_plan": "starter",
|
||||||
|
"license_add_ons": {
|
||||||
|
"GitLab_FileLocks": 1,
|
||||||
|
"GitLab_Auditor_User": 1
|
||||||
|
},
|
||||||
|
"license_trial": null,
|
||||||
|
"license_subscription_id": "0000",
|
||||||
|
"license": {},
|
||||||
|
"settings": {
|
||||||
|
"ldap_encrypted_secrets_enabled": false,
|
||||||
|
"operating_system": "mac_os_x-11.2.2"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { GlButton } from '@gitlab/ui';
|
import { GlButton, GlFormInput } from '@gitlab/ui';
|
||||||
import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
|
import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
|
import CiEnvironmentsDropdown from '~/ci_variable_list/components/ci_environments_dropdown.vue';
|
||||||
import CiVariableModal from '~/ci_variable_list/components/ci_variable_modal.vue';
|
import CiVariableModal from '~/ci_variable_list/components/ci_variable_modal.vue';
|
||||||
import { AWS_ACCESS_KEY_ID } from '~/ci_variable_list/constants';
|
import { AWS_ACCESS_KEY_ID } from '~/ci_variable_list/constants';
|
||||||
import createStore from '~/ci_variable_list/store';
|
import createStore from '~/ci_variable_list/store';
|
||||||
|
@ -15,7 +16,7 @@ describe('Ci variable modal', () => {
|
||||||
let store;
|
let store;
|
||||||
|
|
||||||
const createComponent = (method, options = {}) => {
|
const createComponent = (method, options = {}) => {
|
||||||
store = createStore();
|
store = createStore({ isGroup: options.isGroup });
|
||||||
wrapper = method(CiVariableModal, {
|
wrapper = method(CiVariableModal, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -27,6 +28,7 @@ describe('Ci variable modal', () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findCiEnvironmentsDropdown = () => wrapper.find(CiEnvironmentsDropdown);
|
||||||
const findModal = () => wrapper.find(ModalStub);
|
const findModal = () => wrapper.find(ModalStub);
|
||||||
const findAddorUpdateButton = () =>
|
const findAddorUpdateButton = () =>
|
||||||
findModal()
|
findModal()
|
||||||
|
@ -149,6 +151,61 @@ describe('Ci variable modal', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Environment scope', () => {
|
||||||
|
describe('group level variables', () => {
|
||||||
|
it('renders the environment dropdown', () => {
|
||||||
|
createComponent(shallowMount, {
|
||||||
|
isGroup: true,
|
||||||
|
provide: {
|
||||||
|
glFeatures: {
|
||||||
|
scopedGroupVariables: true,
|
||||||
|
groupScopedCiVariables: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(findCiEnvironmentsDropdown().exists()).toBe(true);
|
||||||
|
expect(findCiEnvironmentsDropdown().isVisible()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('feature flag is disabled', () => {
|
||||||
|
it('hides the dropdown', () => {
|
||||||
|
createComponent(shallowMount, {
|
||||||
|
isGroup: true,
|
||||||
|
provide: {
|
||||||
|
glFeatures: {
|
||||||
|
scopedGroupVariables: false,
|
||||||
|
groupScopedCiVariables: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(findCiEnvironmentsDropdown().exists()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('licensed feature is not available', () => {
|
||||||
|
it('disables the dropdown', () => {
|
||||||
|
createComponent(mount, {
|
||||||
|
isGroup: true,
|
||||||
|
provide: {
|
||||||
|
glFeatures: {
|
||||||
|
scopedGroupVariables: true,
|
||||||
|
groupScopedCiVariables: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const environmentScopeInput = wrapper
|
||||||
|
.find('[data-testid="environment-scope"]')
|
||||||
|
.find(GlFormInput);
|
||||||
|
expect(findCiEnvironmentsDropdown().exists()).toBe(false);
|
||||||
|
expect(environmentScopeInput.attributes('readonly')).toBe('readonly');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Validations', () => {
|
describe('Validations', () => {
|
||||||
const maskError = 'This variable can not be masked.';
|
const maskError = 'This variable can not be masked.';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { GlTable } from '@gitlab/ui';
|
|
||||||
import { createLocalVue, mount } from '@vue/test-utils';
|
import { createLocalVue, mount } from '@vue/test-utils';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import CiVariableTable from '~/ci_variable_list/components/ci_variable_table.vue';
|
import CiVariableTable from '~/ci_variable_list/components/ci_variable_table.vue';
|
||||||
|
@ -12,21 +11,24 @@ describe('Ci variable table', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
let store;
|
let store;
|
||||||
|
|
||||||
const createComponent = () => {
|
const createComponent = (isGroup = false, scopedGroupVariables = false) => {
|
||||||
store = createStore();
|
store = createStore({ isGroup });
|
||||||
store.state.isGroup = true;
|
|
||||||
jest.spyOn(store, 'dispatch').mockImplementation();
|
jest.spyOn(store, 'dispatch').mockImplementation();
|
||||||
wrapper = mount(CiVariableTable, {
|
wrapper = mount(CiVariableTable, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
localVue,
|
localVue,
|
||||||
store,
|
store,
|
||||||
|
provide: {
|
||||||
|
glFeatures: {
|
||||||
|
scopedGroupVariables,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const findRevealButton = () => wrapper.find({ ref: 'secret-value-reveal-button' });
|
const findRevealButton = () => wrapper.find({ ref: 'secret-value-reveal-button' });
|
||||||
const findEditButton = () => wrapper.find({ ref: 'edit-ci-variable' });
|
const findEditButton = () => wrapper.find({ ref: 'edit-ci-variable' });
|
||||||
const findEmptyVariablesPlaceholder = () => wrapper.find({ ref: 'empty-variables' });
|
const findEmptyVariablesPlaceholder = () => wrapper.find({ ref: 'empty-variables' });
|
||||||
const findTable = () => wrapper.find(GlTable);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createComponent();
|
createComponent();
|
||||||
|
@ -40,12 +42,14 @@ describe('Ci variable table', () => {
|
||||||
expect(store.dispatch).toHaveBeenCalledWith('fetchVariables');
|
expect(store.dispatch).toHaveBeenCalledWith('fetchVariables');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fields prop does not contain environment_scope if group', () => {
|
it('fields do not contain environment_scope if group level and feature is disabled', () => {
|
||||||
expect(findTable().props('fields')).not.toEqual(
|
createComponent(true, false);
|
||||||
|
|
||||||
|
expect(wrapper.vm.fields).not.toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
key: 'environment_scope',
|
key: 'environment_scope',
|
||||||
label: 'Environment Scope',
|
label: 'Environments',
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,7 @@ RSpec.describe Ci::GroupVariableEntity do
|
||||||
subject { entity.as_json }
|
subject { entity.as_json }
|
||||||
|
|
||||||
it 'contains required fields' do
|
it 'contains required fields' do
|
||||||
expect(subject).to include(:id, :key, :value, :protected, :variable_type)
|
expect(subject).to include(:id, :key, :value, :protected, :variable_type, :environment_scope)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
3
vendor/gems/mail-smtp_pool/.gitignore
vendored
Normal file
3
vendor/gems/mail-smtp_pool/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Gemfile.lock
|
||||||
|
*.gem
|
||||||
|
.bundle
|
29
vendor/gems/mail-smtp_pool/.gitlab-ci.yml
vendored
Normal file
29
vendor/gems/mail-smtp_pool/.gitlab-ci.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
workflow:
|
||||||
|
rules:
|
||||||
|
- if: $CI_MERGE_REQUEST_ID
|
||||||
|
|
||||||
|
.rspec:
|
||||||
|
cache:
|
||||||
|
key: mail-smtp_pool-ruby
|
||||||
|
paths:
|
||||||
|
- vendor/gems/mail-smtp_pool/vendor/ruby
|
||||||
|
before_script:
|
||||||
|
- cd vendor/gems/mail-smtp_pool
|
||||||
|
- ruby -v # Print out ruby version for debugging
|
||||||
|
- gem install bundler --no-document # Bundler is not installed with the image
|
||||||
|
- bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
|
||||||
|
- bundle install -j $(nproc)
|
||||||
|
script:
|
||||||
|
- bundle exec rspec
|
||||||
|
|
||||||
|
rspec-2.6:
|
||||||
|
image: "ruby:2.6"
|
||||||
|
extends: .rspec
|
||||||
|
|
||||||
|
rspec-2.7:
|
||||||
|
image: "ruby:2.7"
|
||||||
|
extends: .rspec
|
||||||
|
|
||||||
|
rspec-3.0:
|
||||||
|
image: "ruby:3.0"
|
||||||
|
extends: .rspec
|
5
vendor/gems/mail-smtp_pool/Gemfile
vendored
Normal file
5
vendor/gems/mail-smtp_pool/Gemfile
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gemspec
|
21
vendor/gems/mail-smtp_pool/LICENSE
vendored
Normal file
21
vendor/gems/mail-smtp_pool/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2021 GitLab B.V.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
57
vendor/gems/mail-smtp_pool/README.md
vendored
Normal file
57
vendor/gems/mail-smtp_pool/README.md
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Mail::SMTPPool
|
||||||
|
|
||||||
|
This gem is an extension to `Mail` that allows delivery of emails using an SMTP connection pool
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add this line to your application's Gemfile:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem 'mail-smtp_pool'
|
||||||
|
```
|
||||||
|
|
||||||
|
And then execute:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
bundle
|
||||||
|
```
|
||||||
|
|
||||||
|
Or install it yourself as:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
gem install mail-smtp_pool
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage with ActionMailer
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/environments/development.rb
|
||||||
|
|
||||||
|
Rails.application.configure do
|
||||||
|
...
|
||||||
|
|
||||||
|
ActionMailer::Base.add_delivery_method :smtp_pool, Mail::SMTPPool
|
||||||
|
|
||||||
|
config.action_mailer.perform_deliveries = true
|
||||||
|
config.action_mailer.smtp_pool_settings = {
|
||||||
|
pool: Mail::SMTPPool.create_pool(
|
||||||
|
pool_size: 5,
|
||||||
|
pool_timeout: 5,
|
||||||
|
address: 'smtp.gmail.com',
|
||||||
|
port: 587,
|
||||||
|
domain: 'example.com',
|
||||||
|
user_name: '<username>',
|
||||||
|
password: '<password>',
|
||||||
|
authentication: 'plain',
|
||||||
|
enable_starttls_auto: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration options:
|
||||||
|
|
||||||
|
* `pool_size` - The maximum number of SMTP connections in the pool. Connections are created lazily as needed.
|
||||||
|
* `pool_timeout` - The number of seconds to wait for a connection in the pool to be available. A `Timeout::Error` exception is raised when this is exceeded.
|
||||||
|
|
||||||
|
This also accepts all options supported by `Mail::SMTP`. See https://www.rubydoc.info/gems/mail/2.6.1/Mail/SMTP for more information.
|
34
vendor/gems/mail-smtp_pool/lib/mail/smtp_pool.rb
vendored
Normal file
34
vendor/gems/mail-smtp_pool/lib/mail/smtp_pool.rb
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'connection_pool'
|
||||||
|
require 'mail/smtp_pool/connection'
|
||||||
|
|
||||||
|
module Mail
|
||||||
|
class SMTPPool
|
||||||
|
POOL_DEFAULTS = {
|
||||||
|
pool_size: 5,
|
||||||
|
pool_timeout: 5
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def create_pool(settings = {})
|
||||||
|
pool_settings = POOL_DEFAULTS.merge(settings)
|
||||||
|
smtp_settings = settings.reject { |k, v| POOL_DEFAULTS.keys.include?(k) }
|
||||||
|
|
||||||
|
ConnectionPool.new(size: pool_settings[:pool_size], timeout: pool_settings[:pool_timeout]) do
|
||||||
|
Mail::SMTPPool::Connection.new(smtp_settings)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(settings)
|
||||||
|
raise ArgumentError, 'pool is required. You can create one using Mail::SMTPPool.create_pool.' if settings[:pool].nil?
|
||||||
|
|
||||||
|
@pool = settings[:pool]
|
||||||
|
end
|
||||||
|
|
||||||
|
def deliver!(mail)
|
||||||
|
@pool.with { |conn| conn.deliver!(mail) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
60
vendor/gems/mail-smtp_pool/lib/mail/smtp_pool/connection.rb
vendored
Normal file
60
vendor/gems/mail-smtp_pool/lib/mail/smtp_pool/connection.rb
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A connection object that can be used to deliver mail.
|
||||||
|
#
|
||||||
|
# This is meant to be used in a pool so the main difference between this
|
||||||
|
# and Mail::SMTP is that this expects deliver! to be called multiple times.
|
||||||
|
#
|
||||||
|
# SMTP connection reset and error handling is handled by this class and
|
||||||
|
# the SMTP connection is not closed after a delivery.
|
||||||
|
|
||||||
|
require 'mail'
|
||||||
|
|
||||||
|
module Mail
|
||||||
|
class SMTPPool
|
||||||
|
class Connection < Mail::SMTP
|
||||||
|
def initialize(values)
|
||||||
|
super
|
||||||
|
|
||||||
|
@smtp_session = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def deliver!(mail)
|
||||||
|
response = Mail::SMTPConnection.new(connection: smtp_session, return_response: true).deliver!(mail)
|
||||||
|
|
||||||
|
settings[:return_response] ? response : self
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish
|
||||||
|
finish_smtp_session if @smtp_session && @smtp_session.started?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def smtp_session
|
||||||
|
return start_smtp_session if @smtp_session.nil? || !@smtp_session.started?
|
||||||
|
return @smtp_session if reset_smtp_session
|
||||||
|
|
||||||
|
finish_smtp_session
|
||||||
|
start_smtp_session
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_smtp_session
|
||||||
|
@smtp_session = build_smtp_session.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication])
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_smtp_session
|
||||||
|
!@smtp_session.instance_variable_get(:@error_occurred) && @smtp_session.rset.success?
|
||||||
|
rescue Net::SMTPError, IOError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish_smtp_session
|
||||||
|
@smtp_session.finish
|
||||||
|
rescue Net::SMTPError, IOError
|
||||||
|
ensure
|
||||||
|
@smtp_session = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
26
vendor/gems/mail-smtp_pool/mail-smtp_pool.gemspec
vendored
Normal file
26
vendor/gems/mail-smtp_pool/mail-smtp_pool.gemspec
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
lib = File.expand_path('lib', __dir__)
|
||||||
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
|
|
||||||
|
Gem::Specification.new do |spec|
|
||||||
|
spec.name = 'mail-smtp_pool'
|
||||||
|
spec.version = '0.1.0'
|
||||||
|
spec.authors = ['Heinrich Lee Yu']
|
||||||
|
spec.email = ['heinrich@gitlab.com']
|
||||||
|
|
||||||
|
spec.summary = 'Mail extension for sending using an SMTP connection pool'
|
||||||
|
spec.homepage = 'https://gitlab.com/gitlab-org/gitlab/-/tree/master/vendor/gems/mail-smtp_pool'
|
||||||
|
spec.metadata = { 'source_code_uri' => 'https://gitlab.com/gitlab-org/gitlab/-/tree/master/vendor/gems/mail-smtp_pool' }
|
||||||
|
spec.license = 'MIT'
|
||||||
|
|
||||||
|
spec.files = Dir['lib/**/*.rb']
|
||||||
|
spec.require_paths = ['lib']
|
||||||
|
|
||||||
|
# Please maintain alphabetical order for dependencies
|
||||||
|
spec.add_runtime_dependency 'connection_pool', '~> 2.0'
|
||||||
|
spec.add_runtime_dependency 'mail', '~> 2.7'
|
||||||
|
|
||||||
|
# Please maintain alphabetical order for dev dependencies
|
||||||
|
spec.add_development_dependency 'rspec', '~> 3.10.0'
|
||||||
|
end
|
93
vendor/gems/mail-smtp_pool/spec/lib/mail/smtp_pool/connection_spec.rb
vendored
Normal file
93
vendor/gems/mail-smtp_pool/spec/lib/mail/smtp_pool/connection_spec.rb
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mail::SMTPPool::Connection do
|
||||||
|
let(:connection) { described_class.new({}) }
|
||||||
|
let(:mail) do
|
||||||
|
Mail.new do
|
||||||
|
from 'mikel@test.lindsaar.net'
|
||||||
|
to 'you@test.lindsaar.net'
|
||||||
|
subject 'This is a test email'
|
||||||
|
body 'Test body'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
MockSMTP.clear_deliveries
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#deliver!' do
|
||||||
|
it 'delivers mail using the same SMTP connection' do
|
||||||
|
mock_smtp = MockSMTP.new
|
||||||
|
|
||||||
|
expect(Net::SMTP).to receive(:new).once.and_return(mock_smtp)
|
||||||
|
expect(mock_smtp).to receive(:sendmail).twice.and_call_original
|
||||||
|
expect(mock_smtp).to receive(:rset).once.and_call_original
|
||||||
|
|
||||||
|
connection.deliver!(mail)
|
||||||
|
connection.deliver!(mail)
|
||||||
|
|
||||||
|
expect(MockSMTP.deliveries.size).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when RSET fails' do
|
||||||
|
let(:mock_smtp) { MockSMTP.new }
|
||||||
|
let(:mock_smtp_2) { MockSMTP.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
expect(Net::SMTP).to receive(:new).twice.and_return(mock_smtp, mock_smtp_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an IOError' do
|
||||||
|
before do
|
||||||
|
expect(mock_smtp).to receive(:rset).once.and_raise(IOError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new SMTP connection' do
|
||||||
|
expect(mock_smtp).to receive(:sendmail).once.and_call_original
|
||||||
|
expect(mock_smtp).to receive(:finish).once.and_call_original
|
||||||
|
expect(mock_smtp_2).to receive(:sendmail).once.and_call_original
|
||||||
|
|
||||||
|
connection.deliver!(mail)
|
||||||
|
connection.deliver!(mail)
|
||||||
|
|
||||||
|
expect(MockSMTP.deliveries.size).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an SMTP error' do
|
||||||
|
before do
|
||||||
|
expect(mock_smtp).to receive(:rset).once.and_raise(Net::SMTPServerBusy)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new SMTP connection' do
|
||||||
|
expect(mock_smtp).to receive(:sendmail).once.and_call_original
|
||||||
|
expect(mock_smtp).to receive(:finish).once.and_call_original
|
||||||
|
expect(mock_smtp_2).to receive(:sendmail).once.and_call_original
|
||||||
|
|
||||||
|
connection.deliver!(mail)
|
||||||
|
connection.deliver!(mail)
|
||||||
|
|
||||||
|
expect(MockSMTP.deliveries.size).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and closing the old connection fails' do
|
||||||
|
before do
|
||||||
|
expect(mock_smtp).to receive(:finish).once.and_raise(IOError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new SMTP connection' do
|
||||||
|
expect(mock_smtp).to receive(:sendmail).once.and_call_original
|
||||||
|
expect(mock_smtp_2).to receive(:sendmail).once.and_call_original
|
||||||
|
|
||||||
|
connection.deliver!(mail)
|
||||||
|
connection.deliver!(mail)
|
||||||
|
|
||||||
|
expect(MockSMTP.deliveries.size).to eq(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
68
vendor/gems/mail-smtp_pool/spec/lib/mail/smtp_pool_spec.rb
vendored
Normal file
68
vendor/gems/mail-smtp_pool/spec/lib/mail/smtp_pool_spec.rb
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mail::SMTPPool do
|
||||||
|
describe '.create_pool' do
|
||||||
|
it 'sets the default pool settings' do
|
||||||
|
expect(ConnectionPool).to receive(:new).with(size: 5, timeout: 5).once
|
||||||
|
|
||||||
|
described_class.create_pool
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows overriding pool size and timeout' do
|
||||||
|
expect(ConnectionPool).to receive(:new).with(size: 3, timeout: 2).once
|
||||||
|
|
||||||
|
described_class.create_pool(pool_size: 3, pool_timeout: 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an SMTP connection with the correct settings' do
|
||||||
|
settings = { address: 'smtp.example.com', port: '465' }
|
||||||
|
|
||||||
|
smtp_pool = described_class.create_pool(settings)
|
||||||
|
|
||||||
|
expect(Mail::SMTPPool::Connection).to receive(:new).with(settings).once.and_call_original
|
||||||
|
|
||||||
|
smtp_pool.checkout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#initialize' do
|
||||||
|
it 'raises an error if a pool is not specified' do
|
||||||
|
expect { described_class.new({}) }.to raise_error(
|
||||||
|
ArgumentError, 'pool is required. You can create one using Mail::SMTPPool.create_pool.'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#deliver!' do
|
||||||
|
let(:mail) do
|
||||||
|
Mail.new do
|
||||||
|
from 'mikel@test.lindsaar.net'
|
||||||
|
to 'you@test.lindsaar.net'
|
||||||
|
subject 'This is a test email'
|
||||||
|
body 'Test body'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
MockSMTP.clear_deliveries
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'delivers mail using a connection from the pool' do
|
||||||
|
connection_pool = double(ConnectionPool)
|
||||||
|
connection = double(Mail::SMTPPool::Connection)
|
||||||
|
|
||||||
|
expect(connection_pool).to receive(:with).and_yield(connection)
|
||||||
|
expect(connection).to receive(:deliver!).with(mail)
|
||||||
|
|
||||||
|
described_class.new(pool: connection_pool).deliver!(mail)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'delivers mail' do
|
||||||
|
described_class.new(pool: described_class.create_pool).deliver!(mail)
|
||||||
|
|
||||||
|
expect(MockSMTP.deliveries.size).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
84
vendor/gems/mail-smtp_pool/spec/spec_helper.rb
vendored
Normal file
84
vendor/gems/mail-smtp_pool/spec/spec_helper.rb
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'mail/smtp_pool'
|
||||||
|
|
||||||
|
# Original mockup from ActionMailer
|
||||||
|
# Based on https://github.com/mikel/mail/blob/22a7afc23f253319965bf9228a0a430eec94e06d/spec/spec_helper.rb#L74-L138
|
||||||
|
class MockSMTP
|
||||||
|
def self.deliveries
|
||||||
|
@@deliveries
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.security
|
||||||
|
@@security
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@@deliveries = []
|
||||||
|
@@security = nil
|
||||||
|
@started = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def sendmail(mail, from, to)
|
||||||
|
@@deliveries << [mail, from, to]
|
||||||
|
'OK'
|
||||||
|
end
|
||||||
|
|
||||||
|
def rset
|
||||||
|
Net::SMTP::Response.parse('250 OK')
|
||||||
|
end
|
||||||
|
|
||||||
|
def start(*args)
|
||||||
|
@started = true
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
result = yield(self)
|
||||||
|
@started = false
|
||||||
|
|
||||||
|
return result
|
||||||
|
else
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def started?
|
||||||
|
@started
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish
|
||||||
|
@started = false
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.clear_deliveries
|
||||||
|
@@deliveries = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.clear_security
|
||||||
|
@@security = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable_tls(context)
|
||||||
|
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @@security && @@security != :enable_tls
|
||||||
|
@@security = :enable_tls
|
||||||
|
context
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable_starttls(context = nil)
|
||||||
|
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @@security == :enable_tls
|
||||||
|
@@security = :enable_starttls
|
||||||
|
context
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable_starttls_auto(context)
|
||||||
|
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @@security == :enable_tls
|
||||||
|
@@security = :enable_starttls_auto
|
||||||
|
context
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Net::SMTP
|
||||||
|
def self.new(*args)
|
||||||
|
MockSMTP.new
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue