From 09efacdc77fcdbb954dac3fcb2be282dac0e7c45 Mon Sep 17 00:00:00 2001 From: Paul Slaughter Date: Tue, 15 Jan 2019 12:12:14 -0600 Subject: [PATCH 1/5] Extract modules from approvals store **Why?** - For the MR create/edit input, we'll need some common functionality and to override these action names. **Note:** - This also removes the `setSettings` action. Instead, we can simply init the settings when we create the store. --- .../javascripts/approvals/stores/index.js | 23 +++++++------- .../approvals/stores/modules/base/getters.js | 3 ++ .../approvals/stores/modules/base/index.js | 9 ++++++ .../{ => modules/base}/mutation_types.js | 1 - .../stores/{ => modules/base}/mutations.js | 3 -- .../approvals/stores/modules/base/state.js | 4 +++ .../{ => modules/project_settings}/actions.js | 30 ++++++++----------- .../stores/modules/project_settings/index.js | 7 +++++ .../javascripts/approvals/stores/state.js | 13 +++++--- .../stores/modules/base/getters_spec.js | 17 +++++++++++ .../{ => modules/base}/mutations_spec.js | 16 ++-------- .../project_settings}/actions_spec.js | 25 ++-------------- 12 files changed, 78 insertions(+), 73 deletions(-) create mode 100644 ee/app/assets/javascripts/approvals/stores/modules/base/getters.js create mode 100644 ee/app/assets/javascripts/approvals/stores/modules/base/index.js rename ee/app/assets/javascripts/approvals/stores/{ => modules/base}/mutation_types.js (64%) rename ee/app/assets/javascripts/approvals/stores/{ => modules/base}/mutations.js (71%) create mode 100644 ee/app/assets/javascripts/approvals/stores/modules/base/state.js rename ee/app/assets/javascripts/approvals/stores/{ => modules/project_settings}/actions.js (73%) create mode 100644 ee/app/assets/javascripts/approvals/stores/modules/project_settings/index.js create mode 100644 ee/spec/javascripts/approvals/stores/modules/base/getters_spec.js rename ee/spec/javascripts/approvals/stores/{ => modules/base}/mutations_spec.js (58%) rename ee/spec/javascripts/approvals/stores/{ => modules/project_settings}/actions_spec.js (91%) diff --git a/ee/app/assets/javascripts/approvals/stores/index.js b/ee/app/assets/javascripts/approvals/stores/index.js index 8d34f826efc3f7..b17514eab74678 100644 --- a/ee/app/assets/javascripts/approvals/stores/index.js +++ b/ee/app/assets/javascripts/approvals/stores/index.js @@ -1,16 +1,15 @@ import Vuex from 'vuex'; import modalModule from '~/vuex_shared/modules/modal'; import state from './state'; -import mutations from './mutations'; -import * as actions from './actions'; -export default () => - new Vuex.Store({ - state: state(), - mutations, - actions, - modules: { - createModal: modalModule(), - deleteModal: modalModule(), - }, - }); +export const createStoreOptions = (approvalsModule, settings) => ({ + state: state(settings), + modules: { + ...(approvalsModule ? { approvals: approvalsModule } : {}), + createModal: modalModule(), + deleteModal: modalModule(), + }, +}); + +export default (approvalsModule, settings = {}) => + new Vuex.Store(createStoreOptions(approvalsModule, settings)); diff --git a/ee/app/assets/javascripts/approvals/stores/modules/base/getters.js b/ee/app/assets/javascripts/approvals/stores/modules/base/getters.js new file mode 100644 index 00000000000000..851813512c3595 --- /dev/null +++ b/ee/app/assets/javascripts/approvals/stores/modules/base/getters.js @@ -0,0 +1,3 @@ +export const isEmpty = state => !state.rules || !state.rules.length; + +export default () => {}; diff --git a/ee/app/assets/javascripts/approvals/stores/modules/base/index.js b/ee/app/assets/javascripts/approvals/stores/modules/base/index.js new file mode 100644 index 00000000000000..a7bcc2b0b31fd5 --- /dev/null +++ b/ee/app/assets/javascripts/approvals/stores/modules/base/index.js @@ -0,0 +1,9 @@ +import createState from './state'; +import * as getters from './getters'; +import mutations from './mutations'; + +export default () => ({ + state: createState(), + getters, + mutations, +}); diff --git a/ee/app/assets/javascripts/approvals/stores/mutation_types.js b/ee/app/assets/javascripts/approvals/stores/modules/base/mutation_types.js similarity index 64% rename from ee/app/assets/javascripts/approvals/stores/mutation_types.js rename to ee/app/assets/javascripts/approvals/stores/modules/base/mutation_types.js index ed91b8e4bea363..5e10c3f3f16218 100644 --- a/ee/app/assets/javascripts/approvals/stores/mutation_types.js +++ b/ee/app/assets/javascripts/approvals/stores/modules/base/mutation_types.js @@ -1,3 +1,2 @@ -export const SET_SETTINGS = 'SET_SETTINGS'; export const SET_LOADING = 'SET_LOADING'; export const SET_RULES = 'SET_RULES'; diff --git a/ee/app/assets/javascripts/approvals/stores/mutations.js b/ee/app/assets/javascripts/approvals/stores/modules/base/mutations.js similarity index 71% rename from ee/app/assets/javascripts/approvals/stores/mutations.js rename to ee/app/assets/javascripts/approvals/stores/modules/base/mutations.js index 228a4765ab4232..e7490f063463e2 100644 --- a/ee/app/assets/javascripts/approvals/stores/mutations.js +++ b/ee/app/assets/javascripts/approvals/stores/modules/base/mutations.js @@ -1,9 +1,6 @@ import * as types from './mutation_types'; export default { - [types.SET_SETTINGS](state, settings) { - state.settings = { ...settings }; - }, [types.SET_LOADING](state, isLoading) { state.isLoading = isLoading; }, diff --git a/ee/app/assets/javascripts/approvals/stores/modules/base/state.js b/ee/app/assets/javascripts/approvals/stores/modules/base/state.js new file mode 100644 index 00000000000000..6d6844f6363b80 --- /dev/null +++ b/ee/app/assets/javascripts/approvals/stores/modules/base/state.js @@ -0,0 +1,4 @@ +export default () => ({ + isLoading: false, + rules: [], +}); diff --git a/ee/app/assets/javascripts/approvals/stores/actions.js b/ee/app/assets/javascripts/approvals/stores/modules/project_settings/actions.js similarity index 73% rename from ee/app/assets/javascripts/approvals/stores/actions.js rename to ee/app/assets/javascripts/approvals/stores/modules/project_settings/actions.js index 6a133a851c7bc9..26a5d2dd715e61 100644 --- a/ee/app/assets/javascripts/approvals/stores/actions.js +++ b/ee/app/assets/javascripts/approvals/stores/modules/project_settings/actions.js @@ -1,12 +1,8 @@ import createFlash from '~/flash'; import { __ } from '~/locale'; import Api from 'ee/api'; -import * as types from './mutation_types'; -import { mapApprovalRuleRequest, mapApprovalRulesResponse } from '../mappers'; - -export const setSettings = ({ commit }, settings) => { - commit(types.SET_SETTINGS, settings); -}; +import * as types from '../base/mutation_types'; +import { mapApprovalRuleRequest, mapApprovalRulesResponse } from '../../../mappers'; export const requestRules = ({ commit }) => { commit(types.SET_LOADING, true); @@ -21,12 +17,8 @@ export const receiveRulesError = () => { createFlash(__('An error occurred fetching the approval rules.')); }; -export const fetchRules = ({ state, dispatch }) => { - if (state.isLoading) { - return Promise.resolve(); - } - - const { projectId } = state.settings; +export const fetchRules = ({ rootState, dispatch }) => { + const { projectId } = rootState.settings; dispatch('requestRules'); @@ -44,16 +36,16 @@ export const postRuleError = () => { createFlash(__('An error occurred while updating approvers')); }; -export const postRule = ({ state, dispatch }, rule) => { - const { projectId } = state.settings; +export const postRule = ({ rootState, dispatch }, rule) => { + const { projectId } = rootState.settings; return Api.postProjectApprovalRule(projectId, mapApprovalRuleRequest(rule)) .then(() => dispatch('postRuleSuccess')) .catch(() => dispatch('postRuleError')); }; -export const putRule = ({ state, dispatch }, { id, ...newRule }) => { - const { projectId } = state.settings; +export const putRule = ({ rootState, dispatch }, { id, ...newRule }) => { + const { projectId } = rootState.settings; return Api.putProjectApprovalRule(projectId, id, mapApprovalRuleRequest(newRule)) .then(() => dispatch('postRuleSuccess')) @@ -69,10 +61,12 @@ export const deleteRuleError = () => { createFlash(__('An error occurred while deleting the approvers group')); }; -export const deleteRule = ({ state, dispatch }, id) => { - const { projectId } = state.settings; +export const deleteRule = ({ rootState, dispatch }, id) => { + const { projectId } = rootState.settings; return Api.deleteProjectApprovalRule(projectId, id) .then(() => dispatch('deleteRuleSuccess')) .catch(() => dispatch('deleteRuleError')); }; + +export default () => {}; diff --git a/ee/app/assets/javascripts/approvals/stores/modules/project_settings/index.js b/ee/app/assets/javascripts/approvals/stores/modules/project_settings/index.js new file mode 100644 index 00000000000000..657b31ea6c4a1b --- /dev/null +++ b/ee/app/assets/javascripts/approvals/stores/modules/project_settings/index.js @@ -0,0 +1,7 @@ +import base from '../base'; +import * as actions from './actions'; + +export default () => ({ + ...base(), + actions, +}); diff --git a/ee/app/assets/javascripts/approvals/stores/state.js b/ee/app/assets/javascripts/approvals/stores/state.js index 34bde76e505335..5120f75ae33731 100644 --- a/ee/app/assets/javascripts/approvals/stores/state.js +++ b/ee/app/assets/javascripts/approvals/stores/state.js @@ -1,5 +1,10 @@ -export default () => ({ - settings: {}, - isLoading: false, - rules: [], +export const DEFAULT_SETTINGS = { + canEdit: true, +}; + +export default (settings = {}) => ({ + settings: { + ...DEFAULT_SETTINGS, + ...settings, + }, }); diff --git a/ee/spec/javascripts/approvals/stores/modules/base/getters_spec.js b/ee/spec/javascripts/approvals/stores/modules/base/getters_spec.js new file mode 100644 index 00000000000000..472919c6776a8e --- /dev/null +++ b/ee/spec/javascripts/approvals/stores/modules/base/getters_spec.js @@ -0,0 +1,17 @@ +import * as getters from 'ee/approvals/stores/modules/base/getters'; + +describe('EE store modules base getters', () => { + describe('isEmpty', () => { + it('when rules is falsey, is true', () => { + expect(getters.isEmpty({})).toBe(true); + }); + + it('when rules is empty, is true', () => { + expect(getters.isEmpty({ rules: [] })).toBe(true); + }); + + it('when rules has items, is false', () => { + expect(getters.isEmpty({ rules: [1] })).toBe(false); + }); + }); +}); diff --git a/ee/spec/javascripts/approvals/stores/mutations_spec.js b/ee/spec/javascripts/approvals/stores/modules/base/mutations_spec.js similarity index 58% rename from ee/spec/javascripts/approvals/stores/mutations_spec.js rename to ee/spec/javascripts/approvals/stores/modules/base/mutations_spec.js index 9c5a4f95a86ea8..b6753984d4f099 100644 --- a/ee/spec/javascripts/approvals/stores/mutations_spec.js +++ b/ee/spec/javascripts/approvals/stores/modules/base/mutations_spec.js @@ -1,24 +1,14 @@ import createState from 'ee/approvals/stores/state'; -import * as types from 'ee/approvals/stores/mutation_types'; -import mutations from 'ee/approvals/stores/mutations'; +import * as types from 'ee/approvals/stores/modules/base/mutation_types'; +import mutations from 'ee/approvals/stores/modules/base/mutations'; -describe('EE approvals store mutations', () => { +describe('EE approvals base module mutations', () => { let state; beforeEach(() => { state = createState(); }); - describe(types.SET_SETTINGS, () => { - it('sets the settings', () => { - const newSettings = { projectId: 7 }; - - mutations[types.SET_SETTINGS](state, newSettings); - - expect(state.settings).toEqual(newSettings); - }); - }); - describe(types.SET_LOADING, () => { it('sets isLoading', () => { state.isLoading = false; diff --git a/ee/spec/javascripts/approvals/stores/actions_spec.js b/ee/spec/javascripts/approvals/stores/modules/project_settings/actions_spec.js similarity index 91% rename from ee/spec/javascripts/approvals/stores/actions_spec.js rename to ee/spec/javascripts/approvals/stores/modules/project_settings/actions_spec.js index 5a493f2db3632d..08ed9dd4ba06c6 100644 --- a/ee/spec/javascripts/approvals/stores/actions_spec.js +++ b/ee/spec/javascripts/approvals/stores/modules/project_settings/actions_spec.js @@ -1,7 +1,7 @@ import Api from 'ee/api'; import testAction from 'spec/helpers/vuex_action_helper'; -import * as types from 'ee/approvals/stores/mutation_types'; -import actionsModule, * as actions from 'ee/approvals/stores/actions'; +import * as types from 'ee/approvals/stores/modules/base/mutation_types'; +import actionsModule, * as actions from 'ee/approvals/stores/modules/project_settings/actions'; import { mapApprovalRuleRequest, mapApprovalRulesResponse } from 'ee/approvals/mappers'; const TEST_PROJECT_ID = 9; @@ -21,7 +21,7 @@ const TEST_RULE_RESPONSE = { users: [{ id: 7 }, { id: 8 }], }; -describe('EE approvals store actions', () => { +describe('EE approvals project settings module actions', () => { let state; let flashSpy; @@ -36,21 +36,6 @@ describe('EE approvals store actions', () => { spyOn(Api, 'deleteProjectApprovalRule'); }); - describe('setSettings', () => { - it('sets the settings', done => { - const settings = { projectId: 7 }; - - testAction( - actions.setSettings, - settings, - {}, - [{ type: types.SET_SETTINGS, payload: settings }], - [], - done, - ); - }); - }); - describe('requestRules', () => { it('sets loading', done => { testAction( @@ -91,10 +76,6 @@ describe('EE approvals store actions', () => { }); describe('fetchRules', () => { - it('does nothing if loading', done => { - testAction(actions.fetchRules, null, { isLoading: true }, [], [], done); - }); - it('dispatches request/receive', done => { const response = { data: { rules: [TEST_RULE_RESPONSE] }, -- GitLab From fa45a8efdf4ae9acb1a30f7741dd9126831db258 Mon Sep 17 00:00:00 2001 From: Paul Slaughter Date: Tue, 15 Jan 2019 12:16:31 -0600 Subject: [PATCH 2/5] Update approvals components to support MR use **Why?** There's *alot* of common functionality between how these will be used in project settings and MR edit. This abstracts some of that so that it is reusable and extensible. --- .../components/{settings.vue => app.vue} | 37 +++---- .../components/project_settings/app.vue | 15 +++ .../project_settings/project_rules.vue | 55 +++++++++++ .../approvals/components/rule_controls.vue | 36 +++++++ .../approvals/components/rule_form.vue | 14 ++- .../approvals/components/rules.vue | 65 ++---------- .../approvals/components/rules_empty.vue | 8 +- .../{settings_spec.js => app_spec.js} | 99 +++++++++---------- .../components/approvers_list_item_spec.js | 1 + .../project_settings/project_rules_spec.js | 75 ++++++++++++++ .../components/rule_controls_spec.js | 88 +++++++++++++++++ .../approvals/components/rule_form_spec.js | 26 +++-- .../approvals/components/rules_empty_spec.js | 16 ++- .../approvals/components/rules_spec.js | 97 ------------------ 14 files changed, 388 insertions(+), 244 deletions(-) rename ee/app/assets/javascripts/approvals/components/{settings.vue => app.vue} (58%) create mode 100644 ee/app/assets/javascripts/approvals/components/project_settings/app.vue create mode 100644 ee/app/assets/javascripts/approvals/components/project_settings/project_rules.vue create mode 100644 ee/app/assets/javascripts/approvals/components/rule_controls.vue rename ee/spec/javascripts/approvals/components/{settings_spec.js => app_spec.js} (55%) create mode 100644 ee/spec/javascripts/approvals/components/project_settings/project_rules_spec.js create mode 100644 ee/spec/javascripts/approvals/components/rule_controls_spec.js delete mode 100644 ee/spec/javascripts/approvals/components/rules_spec.js diff --git a/ee/app/assets/javascripts/approvals/components/settings.vue b/ee/app/assets/javascripts/approvals/components/app.vue similarity index 58% rename from ee/app/assets/javascripts/approvals/components/settings.vue rename to ee/app/assets/javascripts/approvals/components/app.vue index d9b931553630f5..037a6355e31bc3 100644 --- a/ee/app/assets/javascripts/approvals/components/settings.vue +++ b/ee/app/assets/javascripts/approvals/components/app.vue @@ -1,27 +1,29 @@ @@ -44,13 +43,8 @@ export default { - - + + + diff --git a/ee/app/assets/javascripts/approvals/components/project_settings/app.vue b/ee/app/assets/javascripts/approvals/components/project_settings/app.vue new file mode 100644 index 00000000000000..3133244d61d7c3 --- /dev/null +++ b/ee/app/assets/javascripts/approvals/components/project_settings/app.vue @@ -0,0 +1,15 @@ + + + diff --git a/ee/app/assets/javascripts/approvals/components/project_settings/project_rules.vue b/ee/app/assets/javascripts/approvals/components/project_settings/project_rules.vue new file mode 100644 index 00000000000000..3def1cdf27d5da --- /dev/null +++ b/ee/app/assets/javascripts/approvals/components/project_settings/project_rules.vue @@ -0,0 +1,55 @@ + + + diff --git a/ee/app/assets/javascripts/approvals/components/rule_controls.vue b/ee/app/assets/javascripts/approvals/components/rule_controls.vue new file mode 100644 index 00000000000000..e39d48b7ebd477 --- /dev/null +++ b/ee/app/assets/javascripts/approvals/components/rule_controls.vue @@ -0,0 +1,36 @@ + + + diff --git a/ee/app/assets/javascripts/approvals/components/rule_form.vue b/ee/app/assets/javascripts/approvals/components/rule_form.vue index 10e6d8e148f819..7bded15b27bb37 100644 --- a/ee/app/assets/javascripts/approvals/components/rule_form.vue +++ b/ee/app/assets/javascripts/approvals/components/rule_form.vue @@ -35,11 +35,17 @@ export default { approversByType() { return _.groupBy(this.approvers, x => x.type); }, + users() { + return this.approversByType[TYPE_USER] || []; + }, + groups() { + return this.approversByType[TYPE_GROUP] || []; + }, userIds() { - return (this.approversByType[TYPE_USER] || []).map(x => x.id); + return this.users.map(x => x.id); }, groupIds() { - return (this.approversByType[TYPE_GROUP] || []).map(x => x.id); + return this.groups.map(x => x.id); }, validation() { if (!this.showValidation) { @@ -84,6 +90,8 @@ export default { approvalsRequired: this.approvalsRequired, users: this.userIds, groups: this.groupIds, + userRecords: this.users, + groupRecords: this.groups, }; this.showValidation = true; @@ -131,7 +139,7 @@ export default {