Commit b4cdff15 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 538fff82
<script>
import { mapActions, mapState } from 'vuex';
import { createNamespacedHelpers, mapState, mapActions } from 'vuex';
import RegionDropdown from './region_dropdown.vue';
import RoleNameDropdown from './role_name_dropdown.vue';
import SecurityGroupDropdown from './security_group_dropdown.vue';
import SubnetDropdown from './subnet_dropdown.vue';
import VPCDropdown from './vpc_dropdown.vue';
import VpcDropdown from './vpc_dropdown.vue';
const { mapState: mapRegionsState, mapActions: mapRegionsActions } = createNamespacedHelpers(
'regions',
);
export default {
components: {
......@@ -13,31 +16,39 @@ export default {
RoleNameDropdown,
SecurityGroupDropdown,
SubnetDropdown,
VPCDropdown,
VpcDropdown,
},
computed: {
...mapState(['isLoadingRegions', 'loadingRegionsError', 'regions', 'selectedRegion']),
...mapState(['selectedRegion']),
...mapRegionsState({
regions: 'items',
isLoadingRegions: 'isLoadingItems',
loadingRegionsError: 'loadingItemsError',
}),
},
mounted() {
this.fetchRegions();
},
methods: {
...mapActions(['fetchRegions', 'setRegion']),
...mapActions(['setRegion']),
...mapRegionsActions({
fetchRegions: 'fetchItems',
}),
},
};
</script>
<template>
<form name="eks-cluster-configuration-form">
<div class="form-group">
<label class="label-bold" name="role" for="eks-role">
{{ s__('ClusterIntegration|Role name') }}
</label>
<label class="label-bold" name="role" for="eks-role">{{
s__('ClusterIntegration|Role name')
}}</label>
<role-name-dropdown />
</div>
<div class="form-group">
<label class="label-bold" name="role" for="eks-role">
{{ s__('ClusterIntegration|Region') }}
</label>
<label class="label-bold" name="role" for="eks-role">{{
s__('ClusterIntegration|Region')
}}</label>
<region-dropdown
:value="selectedRegion"
:regions="regions"
......
import * as awsServices from '../services/aws_services_facade';
import * as types from './mutation_types';
export const requestRegions = ({ commit }) => commit(types.REQUEST_REGIONS);
export const receiveRegionsSuccess = ({ commit }, payload) => {
commit(types.RECEIVE_REGIONS_SUCCESS, payload);
};
export const receiveRegionsError = ({ commit }, payload) => {
commit(types.RECEIVE_REGIONS_ERROR, payload);
};
export const fetchRegions = ({ dispatch }) => {
dispatch('requestRegions');
return awsServices
.fetchRegions()
.then(regions => dispatch('receiveRegionsSuccess', { regions }))
.catch(error => dispatch('receiveRegionsError', { error }));
};
export const setRegion = ({ commit }, payload) => {
commit(types.SET_REGION, payload);
};
......
import * as types from './mutation_types';
export default fetchItems => ({
requestItems: ({ commit }) => commit(types.REQUEST_ITEMS),
receiveItemsSuccess: ({ commit }, payload) => commit(types.RECEIVE_ITEMS_SUCCESS, payload),
receiveItemsError: ({ commit }, payload) => commit(types.RECEIVE_ITEMS_ERROR, payload),
fetchItems: ({ dispatch }) => {
dispatch('requestItems');
return fetchItems()
.then(items => dispatch('receiveItemsSuccess', { items }))
.catch(error => dispatch('receiveItemsError', { error }));
},
});
import * as getters from './getters';
import actions from './actions';
import mutations from './mutations';
import state from './state';
const createStore = fetchFn => ({
actions: actions(fetchFn),
getters,
mutations,
state: state(),
});
export default createStore;
export const REQUEST_ITEMS = 'REQUEST_ITEMS';
export const RECEIVE_ITEMS_SUCCESS = 'REQUEST_ITEMS_SUCCESS';
export const RECEIVE_ITEMS_ERROR = 'RECEIVE_ITEMS_ERROR';
import * as types from './mutation_types';
export default {
[types.REQUEST_ITEMS](state) {
state.isLoadingItems = true;
state.loadingItemsError = null;
},
[types.RECEIVE_ITEMS_SUCCESS](state, { items }) {
state.isLoadingItems = false;
state.items = items;
},
[types.RECEIVE_ITEMS_ERROR](state, { error }) {
state.isLoadingItems = false;
state.loadingItemsError = error;
},
};
export default () => ({
isLoadingItems: false,
items: [],
loadingItemsError: null,
});
......@@ -4,12 +4,22 @@ import * as getters from './getters';
import mutations from './mutations';
import state from './state';
import clusterDropdownStore from './cluster_dropdown';
import * as awsServices from '../services/aws_services_facade';
const createStore = () =>
new Vuex.Store({
actions,
getters,
mutations,
state: state(),
modules: {
regions: {
namespaced: true,
...clusterDropdownStore(awsServices.fetchRegions),
},
},
});
export default createStore;
export const REQUEST_REGIONS = 'REQUEST_REGIONS';
export const RECEIVE_REGIONS_SUCCESS = 'REQUEST_REGIONS_SUCCESS';
export const RECEIVE_REGIONS_ERROR = 'RECEIVE_REGIONS_ERROR';
// eslint-disable-next-line import/prefer-default-export
export const SET_REGION = 'SET_REGION';
import * as types from './mutation_types';
export default {
[types.REQUEST_REGIONS](state) {
state.isLoadingRegions = true;
state.loadingRegionsError = null;
},
[types.RECEIVE_REGIONS_SUCCESS](state, { regions }) {
state.isLoadingRegions = false;
state.regions = regions;
},
[types.RECEIVE_REGIONS_ERROR](state, { error }) {
state.isLoadingRegions = false;
state.loadingRegionsError = error;
},
[types.SET_REGION](state, { region }) {
state.selectedRegion = region;
},
......
......@@ -2,27 +2,9 @@ export default () => ({
isValidatingCredentials: false,
validCredentials: false,
isLoadingRegions: false,
isLoadingRoles: false,
isLoadingVPCs: false,
isLoadingSubnets: false,
isLoadingSecurityGroups: false,
regions: [],
roles: [],
vpcs: [],
subnets: [],
securityGroups: [],
loadingRegionsError: null,
loadingRolesError: null,
loadingVPCsError: null,
loadingSubnetsError: null,
loadingSecurityGroupsError: null,
selectedRegion: '',
selectedRole: '',
selectedVPC: '',
selectedVpc: '',
selectedSubnet: '',
selectedSecurityGroup: '',
});
......@@ -44,9 +44,15 @@ module AtomicInternalId
scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
usage = self.class.table_name.to_sym
if value.present?
if value.present? && (@iid_needs_tracking || Feature.enabled?(:iid_always_track, default_enabled: true))
# The value was set externally, e.g. by the user
# We update the InternalId record to keep track of the greatest value.
InternalId.track_greatest(self, scope_attrs, usage, value, init)
else
@iid_needs_tracking = false
elsif !value.present?
# We don't have a value yet and use a InternalId record to generate
# the next value.
value = InternalId.generate_next(self, scope_attrs, usage, init)
write_attribute(column, value)
end
......@@ -54,6 +60,13 @@ module AtomicInternalId
value
end
define_method("#{column}=") do |value|
super(value).tap do |v|
# Indicate the iid was set from externally
@iid_needs_tracking = true
end
end
define_method("reset_#{scope}_#{column}") do
if value = read_attribute(column)
scope_value = association(scope).reader
......
......@@ -24,7 +24,7 @@ The following table describes the version types and their release cadence:
| Version type | Description | Cadence |
|:-------------|:------------|:--------|
| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 12.0 on June 22, 2019. Subsequent major releases will be scheduled for May 22 each year, by default. |
| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 13.0 on May 22, 2020. Subsequent major releases will be scheduled for May 22 each year, by default. |
| Minor | For when new backward-compatible functionality is introduced to the public API, a minor feature is introduced, or when a set of smaller features is rolled out. | Monthly on the 22nd. |
| Patch | For backward-compatible bug fixes that fix incorrect behavior. See [Patch releases](#patch-releases). | As needed. |
......
......@@ -32,7 +32,7 @@ module Banzai
video = doc.document.create_element(
'video',
src: element['src'],
width: '400',
width: '100%',
controls: true,
'data-setup' => '{}',
'data-title' => element['title'] || element['alt'])
......
......@@ -4,28 +4,38 @@ import Vue from 'vue';
import EksClusterConfigurationForm from '~/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue';
import RegionDropdown from '~/create_cluster/eks_cluster/components/region_dropdown.vue';
import clusterDropdownStoreState from '~/create_cluster/eks_cluster/store/cluster_dropdown/state';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('EksClusterConfigurationForm', () => {
let store;
let actions;
let state;
let regionsState;
let regionsActions;
let vm;
beforeEach(() => {
actions = {
fetchRegions: jest.fn(),
setRegion: jest.fn(),
setVpc: jest.fn(),
};
regionsActions = {
fetchItems: jest.fn(),
};
state = {
regions: [{ name: 'region 1' }],
isLoadingRegions: false,
loadingRegionsError: { message: '' },
regionsState = {
...clusterDropdownStoreState(),
};
store = new Vuex.Store({
state,
actions,
modules: {
regions: {
namespaced: true,
state: regionsState,
actions: regionsActions,
},
},
});
});
......@@ -44,31 +54,35 @@ describe('EksClusterConfigurationForm', () => {
describe('when mounted', () => {
it('fetches available regions', () => {
expect(actions.fetchRegions).toHaveBeenCalled();
expect(regionsActions.fetchItems).toHaveBeenCalled();
});
});
it('sets isLoadingRegions to RegionDropdown loading property', () => {
state.isLoadingRegions = true;
regionsState.isLoadingItems = true;
return Vue.nextTick().then(() => {
expect(findRegionDropdown().props('loading')).toEqual(state.isLoadingRegions);
expect(findRegionDropdown().props('loading')).toEqual(regionsState.isLoadingItems);
});
});
it('sets regions to RegionDropdown regions property', () => {
expect(findRegionDropdown().props('regions')).toEqual(state.regions);
expect(findRegionDropdown().props('regions')).toEqual(regionsState.items);
});
it('sets loadingRegionsError to RegionDropdown error property', () => {
expect(findRegionDropdown().props('error')).toEqual(state.loadingRegionsError);
expect(findRegionDropdown().props('error')).toEqual(regionsState.loadingItemsError);
});
it('dispatches setRegion action when region is selected', () => {
const region = { region: 'us-west-2' };
describe('when region is selected', () => {
const region = { name: 'us-west-2' };
findRegionDropdown().vm.$emit('input', region);
beforeEach(() => {
findRegionDropdown().vm.$emit('input', region);
});
expect(actions.setRegion).toHaveBeenCalledWith(expect.anything(), { region }, undefined);
it('dispatches setRegion action', () => {
expect(actions.setRegion).toHaveBeenCalledWith(expect.anything(), { region }, undefined);
});
});
});
import testAction from 'helpers/vuex_action_helper';
import * as awsServicesFacade from '~/create_cluster/eks_cluster/services/aws_services_facade';
import createState from '~/create_cluster/eks_cluster/store/state';
import * as types from '~/create_cluster/eks_cluster/store/mutation_types';
import * as actions from '~/create_cluster/eks_cluster/store/actions';
describe('EKS Cluster Store Actions', () => {
const regions = [{ name: 'region 1' }];
describe('fetchRegions', () => {
describe('on success', () => {
beforeEach(() => {
jest.spyOn(awsServicesFacade, 'fetchRegions').mockResolvedValueOnce(regions);
});
it('dispatches success with received regions', () =>
testAction(
actions.fetchRegions,
null,
createState(),
[],
[
{ type: 'requestRegions' },
{
type: 'receiveRegionsSuccess',
payload: { regions },
},
],
));
});
describe('on failure', () => {
const error = new Error('Could not fetch regions');
beforeEach(() => {
jest.spyOn(awsServicesFacade, 'fetchRegions').mockRejectedValueOnce(error);
});
it('dispatches success with received regions', () =>
testAction(
actions.fetchRegions,
null,
createState(),
[],
[
{ type: 'requestRegions' },
{
type: 'receiveRegionsError',
payload: { error },
},
],
));
});
});
describe('requestRegions', () => {
it(`commits ${types.REQUEST_REGIONS} mutation`, () =>
testAction(actions.requestRegions, null, createState(), [{ type: types.REQUEST_REGIONS }]));
});
describe('receiveRegionsSuccess', () => {
it(`commits ${types.RECEIVE_REGIONS_SUCCESS} mutation`, () =>
testAction(actions.receiveRegionsSuccess, { regions }, createState(), [
{
type: types.RECEIVE_REGIONS_SUCCESS,
payload: {
regions,
},
},
]));
});
describe('receiveRegionsError', () => {
it(`commits ${types.RECEIVE_REGIONS_ERROR} mutation`, () => {
const error = new Error('Error fetching regions');
testAction(actions.receiveRegionsError, { error }, createState(), [
{
type: types.RECEIVE_REGIONS_ERROR,
payload: {
error,
},
},
]);
});
});
describe('setRegion', () => {
it(`commits ${types.SET_REGION} mutation`, () => {
const region = { name: 'west-1' };
......
import testAction from 'helpers/vuex_action_helper';
import createState from '~/create_cluster/eks_cluster/store/cluster_dropdown/state';
import * as types from '~/create_cluster/eks_cluster/store/cluster_dropdown/mutation_types';
import actionsFactory from '~/create_cluster/eks_cluster/store/cluster_dropdown/actions';
describe('Cluster dropdown Store Actions', () => {
const items = [{ name: 'item 1' }];
let fetchFn;
let actions;
beforeEach(() => {
fetchFn = jest.fn();
actions = actionsFactory(fetchFn);
});
describe('fetchItems', () => {
describe('on success', () => {
beforeEach(() => {
fetchFn.mockResolvedValueOnce(items);
actions = actionsFactory(fetchFn);
});
it('dispatches success with received items', () =>
testAction(
actions.fetchItems,
null,
createState(),
[],
[
{ type: 'requestItems' },
{
type: 'receiveItemsSuccess',
payload: { items },
},
],
));
});
describe('on failure', () => {
const error = new Error('Could not fetch items');
beforeEach(() => {
fetchFn.mockRejectedValueOnce(error);
});
it('dispatches success with received items', () =>
testAction(
actions.fetchItems,
null,
createState(),
[],
[
{ type: 'requestItems' },
{
type: 'receiveItemsError',
payload: { error },
},
],
));
});
});
describe('requestItems', () => {
it(`commits ${types.REQUEST_ITEMS} mutation`, () =>
testAction(actions.requestItems, null, createState(), [{ type: types.REQUEST_ITEMS }]));
});
describe('receiveItemsSuccess', () => {
it(`commits ${types.RECEIVE_ITEMS_SUCCESS} mutation`, () =>
testAction(actions.receiveItemsSuccess, { items }, createState(), [
{
type: types.RECEIVE_ITEMS_SUCCESS,
payload: {
items,
},
},
]));
});
describe('receiveItemsError', () => {
it(`commits ${types.RECEIVE_ITEMS_ERROR} mutation`, () => {
const error = new Error('Error fetching items');
testAction(actions.receiveItemsError, { error }, createState(), [
{
type: types.RECEIVE_ITEMS_ERROR,
payload: {
error,
},
},
]);
});
});
});
import {
REQUEST_ITEMS,
RECEIVE_ITEMS_SUCCESS,
RECEIVE_ITEMS_ERROR,
} from '~/create_cluster/eks_cluster/store/cluster_dropdown/mutation_types';
import createState from '~/create_cluster/eks_cluster/store/cluster_dropdown/state';
import mutations from '~/create_cluster/eks_cluster/store/cluster_dropdown/mutations';
describe('Cluster dropdown store mutations', () => {
let state;
let emptyPayload;
let items;
let error;
beforeEach(() => {
emptyPayload = {};
items = [{ name: 'item 1' }];
error = new Error('could not load error');
state = createState();
});
it.each`
mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
${REQUEST_ITEMS} | ${'isLoadingItems'} | ${emptyPayload} | ${true} | ${true}
${REQUEST_ITEMS} | ${'loadingItemsError'} | ${emptyPayload} | ${null} | ${null}
${RECEIVE_ITEMS_SUCCESS} | ${'isLoadingItems'} | ${{ items }} | ${false} | ${false}
${RECEIVE_ITEMS_SUCCESS} | ${'items'} | ${{ items }} | ${items} | ${'items payload'}
${RECEIVE_ITEMS_ERROR} | ${'isLoadingItems'} | ${{ error }} | ${false} | ${false}
${RECEIVE_ITEMS_ERROR} | ${'error'} | ${{ error }} | ${error} | ${'received error object'}
`(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => {
const { mutation, mutatedProperty, payload, expectedValue } = data;
mutations[mutation](state, payload);
expect(state[mutatedProperty]).toBe(expectedValue);
});
});
import {
REQUEST_REGIONS,
RECEIVE_REGIONS_ERROR,
RECEIVE_REGIONS_SUCCESS,
SET_REGION,
} from '~/create_cluster/eks_cluster/store/mutation_types';
import { SET_REGION } from '~/create_cluster/eks_cluster/store/mutation_types';
import createState from '~/create_cluster/eks_cluster/store/state';
import mutations from '~/create_cluster/eks_cluster/store/mutations';
describe('Create EKS cluster store mutations', () => {
let state;
let emptyPayload;
let regions;
let region;
let error;
beforeEach(() => {
emptyPayload = {};
region = { name: 'regions-1' };
regions = [region];
error = new Error('could not load error');
state = createState();
});
it.each`
mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
${REQUEST_REGIONS} | ${'isLoadingRegions'} | ${emptyPayload} | ${true} | ${true}
${REQUEST_REGIONS} | ${'loadingRegionsError'} | ${emptyPayload} | ${null} | ${null}
${RECEIVE_REGIONS_SUCCESS} | ${'isLoadingRegions'} | ${{ regions }} | ${false} | ${false}
${RECEIVE_REGIONS_SUCCESS} | ${'regions'} | ${{ regions }} | ${regions} | ${'regions payload'}
${RECEIVE_REGIONS_ERROR} | ${'isLoadingRegions'} | ${{ error }} | ${false} | ${false}
${RECEIVE_REGIONS_ERROR} | ${'error'} | ${{ error }} | ${error} | ${'received error object'}
${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'}
mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'}
`(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => {
const { mutation, mutatedProperty, payload, expectedValue } = data;
......
# frozen_string_literal: true
require 'spec_helper'
describe AtomicInternalId do
let(:milestone) { build(:milestone) }
let(:iid) { double('iid', to_i: 42) }
let(:external_iid) { 100 }
let(:scope_attrs) { { project: milestone.project } }
let(:usage) { :milestones }
describe '#ensure_project_iid!' do
subject { milestone.ensure_project_iid! }
it 'generates a new value if non is present' do
expect(InternalId).to receive(:generate_next).with(milestone, scope_attrs, usage, anything).and_return(iid)
expect { subject }.to change { milestone.iid }.from(nil).to(iid.to_i)
end
it 'tracks the present value if not generated by InternalId.generate_next' do
milestone.iid = external_iid
expect(InternalId).to receive(:track_greatest).once.with(milestone, scope_attrs, usage, external_iid, anything)
expect(InternalId).not_to receive(:generate_next)
subject
end
it 'generates a new value if first set with iid= but later set to nil' do
expect(InternalId).to receive(:generate_next).with(milestone, scope_attrs, usage, anything).and_return(iid)
milestone.iid = external_iid
milestone.iid = nil
expect { subject }.to change { milestone.iid }.from(nil).to(iid.to_i)
end
context 'with iid_always_track disabled' do
before do
stub_feature_flags(iid_always_track: false)
end
it 'does not track the present value if generated by InternalId.generate_next' do
milestone.ensure_project_iid!
expect(InternalId).not_to receive(:track_greatest)
subject
end
end
end
end
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment