Commit d86ee4b7 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '208470_02-package-file-actions' into 'master'

Geo Package Files - Store Actions

See merge request gitlab-org/gitlab!33864
parents 38c1ed7c ad8202ce
fragment PageInfo on PageInfo { fragment PageInfo on PageInfo {
hasNextPage hasNextPage
hasPreviousPage
startCursor
endCursor endCursor
} }
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query($before: String!, $after: String!) {
geoNode {
packageFileRegistries(first: 20, before: $before, after: $after) {
pageInfo {
...PageInfo
}
edges {
cursor
node {
id
packageFileId
state
retryCount
lastSyncFailure
retryAt
lastSyncedAt
createdAt
}
}
}
}
}
...@@ -7,6 +7,8 @@ import { ...@@ -7,6 +7,8 @@ import {
normalizeHeaders, normalizeHeaders,
convertObjectPropsToCamelCase, convertObjectPropsToCamelCase,
} from '~/lib/utils/common_utils'; } from '~/lib/utils/common_utils';
import packageFilesQuery from '../graphql/package_files.query.graphql';
import { gqClient } from '../utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { FILTER_STATES } from './constants'; import { FILTER_STATES } from './constants';
...@@ -26,6 +28,28 @@ export const receiveReplicableItemsError = ({ state, commit }) => { ...@@ -26,6 +28,28 @@ export const receiveReplicableItemsError = ({ state, commit }) => {
export const fetchReplicableItems = ({ state, dispatch }) => { export const fetchReplicableItems = ({ state, dispatch }) => {
dispatch('requestReplicableItems'); dispatch('requestReplicableItems');
return state.useGraphQl
? dispatch('fetchReplicableItemsGraphQl')
: dispatch('fetchReplicableItemsRestful');
};
export const fetchReplicableItemsGraphQl = ({ dispatch }) => {
gqClient
.query({
query: packageFilesQuery,
})
.then(res => {
const registries = res.data.geoNode.packageFileRegistries;
const data = registries.edges.map(e => e.node);
dispatch('receiveReplicableItemsSuccess', { data });
})
.catch(() => {
dispatch('receiveReplicableItemsError');
});
};
export const fetchReplicableItemsRestful = ({ state, dispatch }) => {
const { filterOptions, currentFilterIndex, currentPage, searchFilter } = state; const { filterOptions, currentFilterIndex, currentPage, searchFilter } = state;
const statusFilter = currentFilterIndex ? filterOptions[currentFilterIndex] : filterOptions[0]; const statusFilter = currentFilterIndex ? filterOptions[currentFilterIndex] : filterOptions[0];
......
...@@ -2,6 +2,7 @@ import { FILTER_STATES } from './constants'; ...@@ -2,6 +2,7 @@ import { FILTER_STATES } from './constants';
const createState = replicableType => ({ const createState = replicableType => ({
replicableType, replicableType,
useGraphQl: false,
isLoading: false, isLoading: false,
replicableItems: [], replicableItems: [],
......
import createGqClient, { fetchPolicies } from '~/lib/graphql';
/*
This file uses a NO_CACHE policy due to the need for Geo data to always be fresh.
The UI this serves is used to watch the "syncing" process of items and their statuses
will need to be constantly re-queried as the user navigates around to not mistakenly
think the sync process is broken.
*/
// eslint-disable-next-line import/prefer-default-export
export const gqClient = createGqClient(
{},
{
fetchPolicy: fetchPolicies.NO_CACHE,
},
);
...@@ -89,8 +89,9 @@ describe('GeoReplicableApp', () => { ...@@ -89,8 +89,9 @@ describe('GeoReplicableApp', () => {
describe('with replicableItems', () => { describe('with replicableItems', () => {
beforeEach(() => { beforeEach(() => {
wrapper.vm.$store.state.replicableItems = MOCK_BASIC_FETCH_DATA_MAP.data; wrapper.vm.$store.state.replicableItems = MOCK_BASIC_FETCH_DATA_MAP;
wrapper.vm.$store.state.totalReplicableItems = MOCK_BASIC_FETCH_DATA_MAP.total; wrapper.vm.$store.state.totalReplicableItems =
wrapper.vm.$store.state.replicableItems.length;
}); });
it('shows replicable items', () => { it('shows replicable items', () => {
......
...@@ -11,7 +11,7 @@ localVue.use(Vuex); ...@@ -11,7 +11,7 @@ localVue.use(Vuex);
describe('GeoReplicableItem', () => { describe('GeoReplicableItem', () => {
let wrapper; let wrapper;
const mockReplicable = MOCK_BASIC_FETCH_DATA_MAP.data[0]; const mockReplicable = MOCK_BASIC_FETCH_DATA_MAP[0];
const actionSpies = { const actionSpies = {
initiateReplicableSync: jest.fn(), initiateReplicableSync: jest.fn(),
......
...@@ -70,7 +70,7 @@ describe('GeoReplicable', () => { ...@@ -70,7 +70,7 @@ describe('GeoReplicable', () => {
describe('GeoReplicableItem', () => { describe('GeoReplicableItem', () => {
beforeEach(() => { beforeEach(() => {
wrapper.vm.$store.state.replicableItems = MOCK_BASIC_FETCH_DATA_MAP.data; wrapper.vm.$store.state.replicableItems = MOCK_BASIC_FETCH_DATA_MAP;
}); });
it('renders an instance for each replicableItem in the store', () => { it('renders an instance for each replicableItem in the store', () => {
......
...@@ -30,12 +30,51 @@ export const MOCK_BASIC_FETCH_RESPONSE = { ...@@ -30,12 +30,51 @@ export const MOCK_BASIC_FETCH_RESPONSE = {
}, },
}; };
export const MOCK_BASIC_FETCH_DATA_MAP = { export const MOCK_BASIC_FETCH_DATA_MAP = convertObjectPropsToCamelCase(
data: convertObjectPropsToCamelCase(MOCK_BASIC_FETCH_RESPONSE.data, { deep: true }), MOCK_BASIC_FETCH_RESPONSE.data,
{ deep: true },
);
export const MOCK_RESTFUL_PAGINATION_DATA = {
perPage: MOCK_BASIC_FETCH_RESPONSE.headers['x-per-page'], perPage: MOCK_BASIC_FETCH_RESPONSE.headers['x-per-page'],
total: MOCK_BASIC_FETCH_RESPONSE.headers['x-total'], total: MOCK_BASIC_FETCH_RESPONSE.headers['x-total'],
}; };
export const MOCK_GRAPHQL_PAGINATION_DATA = {
hasNextPage: true,
hasPreviousPage: true,
startCursor: 'abc123',
endCursor: 'abc124',
};
export const MOCK_BASIC_GRAPHQL_QUERY_RESPONSE = {
geoNode: {
packageFileRegistries: {
pageInfo: MOCK_GRAPHQL_PAGINATION_DATA,
edges: [
{
cursor: 'abc123',
node: {
id: 'git/1',
packageFileId: '1',
state: 'PENDING',
lastSyncedAt: null,
},
},
{
cursor: 'abc124',
node: {
id: 'git/2',
packageFileId: '2',
state: 'SYNCED',
lastSyncedAt: null,
},
},
],
},
},
};
export const MOCK_BASIC_POST_RESPONSE = { export const MOCK_BASIC_POST_RESPONSE = {
status: 'ok', status: 'ok',
}; };
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import flash from '~/flash'; import flash from '~/flash';
import toast from '~/vue_shared/plugins/global_toast'; import toast from '~/vue_shared/plugins/global_toast';
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
import Api from 'ee/api'; import Api from 'ee/api';
import * as actions from 'ee/geo_replicable/store/actions'; import * as actions from 'ee/geo_replicable/store/actions';
import * as types from 'ee/geo_replicable/store/mutation_types'; import * as types from 'ee/geo_replicable/store/mutation_types';
import createState from 'ee/geo_replicable/store/state'; import createState from 'ee/geo_replicable/store/state';
import { ACTION_TYPES } from 'ee/geo_replicable/store/constants'; import { ACTION_TYPES } from 'ee/geo_replicable/store/constants';
import { gqClient } from 'ee/geo_replicable/utils';
import packageFilesQuery from 'ee/geo_replicable/graphql/package_files.query.graphql';
import { import {
MOCK_BASIC_FETCH_DATA_MAP, MOCK_BASIC_FETCH_DATA_MAP,
MOCK_BASIC_FETCH_RESPONSE, MOCK_BASIC_FETCH_RESPONSE,
MOCK_BASIC_POST_RESPONSE, MOCK_BASIC_POST_RESPONSE,
MOCK_REPLICABLE_TYPE, MOCK_REPLICABLE_TYPE,
MOCK_RESTFUL_PAGINATION_DATA,
MOCK_BASIC_GRAPHQL_QUERY_RESPONSE,
} from '../mock_data'; } from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -40,9 +45,14 @@ describe('GeoReplicable Store Actions', () => { ...@@ -40,9 +45,14 @@ describe('GeoReplicable Store Actions', () => {
it('should commit mutation RECEIVE_REPLICABLE_ITEMS_SUCCESS', done => { it('should commit mutation RECEIVE_REPLICABLE_ITEMS_SUCCESS', done => {
testAction( testAction(
actions.receiveReplicableItemsSuccess, actions.receiveReplicableItemsSuccess,
MOCK_BASIC_FETCH_DATA_MAP, { data: MOCK_BASIC_FETCH_DATA_MAP, pagination: MOCK_RESTFUL_PAGINATION_DATA },
state, state,
[{ type: types.RECEIVE_REPLICABLE_ITEMS_SUCCESS, payload: MOCK_BASIC_FETCH_DATA_MAP }], [
{
type: types.RECEIVE_REPLICABLE_ITEMS_SUCCESS,
payload: { data: MOCK_BASIC_FETCH_DATA_MAP, pagination: MOCK_RESTFUL_PAGINATION_DATA },
},
],
[], [],
done, done,
); );
...@@ -65,6 +75,95 @@ describe('GeoReplicable Store Actions', () => { ...@@ -65,6 +75,95 @@ describe('GeoReplicable Store Actions', () => {
}); });
describe('fetchReplicableItems', () => { describe('fetchReplicableItems', () => {
describe('with graphql', () => {
beforeEach(() => {
state.useGraphQl = true;
});
it('calls fetchReplicableItemsGraphQl', done => {
testAction(
actions.fetchReplicableItems,
null,
state,
[],
[{ type: 'requestReplicableItems' }, { type: 'fetchReplicableItemsGraphQl' }],
done,
);
});
});
describe('without graphql', () => {
beforeEach(() => {
state.useGraphQl = false;
});
it('calls fetchReplicableItemsRestful', done => {
testAction(
actions.fetchReplicableItems,
null,
state,
[],
[{ type: 'requestReplicableItems' }, { type: 'fetchReplicableItemsRestful' }],
done,
);
});
});
});
describe('fetchReplicableItemsGraphQl', () => {
describe('on success', () => {
const registries = MOCK_BASIC_GRAPHQL_QUERY_RESPONSE.geoNode?.packageFileRegistries;
const data = registries.edges.map(e => e.node);
beforeEach(() => {
jest.spyOn(gqClient, 'query').mockResolvedValue({
data: MOCK_BASIC_GRAPHQL_QUERY_RESPONSE,
});
});
it('should call gqClient with no before/after variables', () => {
testAction(
actions.fetchReplicableItemsGraphQl,
null,
state,
[],
[
{
type: 'receiveReplicableItemsSuccess',
payload: { data },
},
],
() => {
expect(gqClient.query).toHaveBeenCalledWith({
query: packageFilesQuery,
});
},
);
});
});
describe('on error', () => {
beforeEach(() => {
jest.spyOn(gqClient, 'query').mockRejectedValue();
});
it('should dispatch the request and error actions', done => {
testAction(
actions.fetchReplicableItemsGraphQl,
null,
state,
[],
[{ type: 'receiveReplicableItemsError' }],
done,
);
});
});
});
describe('fetchReplicableItemsRestful', () => {
const normalizedHeaders = normalizeHeaders(MOCK_BASIC_FETCH_RESPONSE.headers);
const pagination = parseIntPagination(normalizedHeaders);
describe('on success', () => { describe('on success', () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(Api, 'getGeoReplicableItems').mockResolvedValue(MOCK_BASIC_FETCH_RESPONSE); jest.spyOn(Api, 'getGeoReplicableItems').mockResolvedValue(MOCK_BASIC_FETCH_RESPONSE);
...@@ -79,13 +178,19 @@ describe('GeoReplicable Store Actions', () => { ...@@ -79,13 +178,19 @@ describe('GeoReplicable Store Actions', () => {
it('should call getGeoReplicableItems with default queryParams', () => { it('should call getGeoReplicableItems with default queryParams', () => {
testAction( testAction(
actions.fetchReplicableItems, actions.fetchReplicableItemsRestful,
{}, {},
state, state,
[], [],
[ [
{ type: 'requestReplicableItems' }, {
{ type: 'receiveReplicableItemsSuccess', payload: MOCK_BASIC_FETCH_DATA_MAP }, type: 'receiveReplicableItemsSuccess',
payload: {
data: MOCK_BASIC_FETCH_DATA_MAP,
perPage: pagination.perPage,
total: pagination.total,
},
},
], ],
() => { () => {
expect(Api.getGeoReplicableItems).toHaveBeenCalledWith( expect(Api.getGeoReplicableItems).toHaveBeenCalledWith(
...@@ -106,13 +211,19 @@ describe('GeoReplicable Store Actions', () => { ...@@ -106,13 +211,19 @@ describe('GeoReplicable Store Actions', () => {
it('should call getGeoReplicableItems with default queryParams', () => { it('should call getGeoReplicableItems with default queryParams', () => {
testAction( testAction(
actions.fetchReplicableItems, actions.fetchReplicableItemsRestful,
{}, {},
state, state,
[], [],
[ [
{ type: 'requestReplicableItems' }, {
{ type: 'receiveReplicableItemsSuccess', payload: MOCK_BASIC_FETCH_DATA_MAP }, type: 'receiveReplicableItemsSuccess',
payload: {
data: MOCK_BASIC_FETCH_DATA_MAP,
perPage: pagination.perPage,
total: pagination.total,
},
},
], ],
() => { () => {
expect(Api.getGeoReplicableItems).toHaveBeenCalledWith(MOCK_REPLICABLE_TYPE, { expect(Api.getGeoReplicableItems).toHaveBeenCalledWith(MOCK_REPLICABLE_TYPE, {
...@@ -133,11 +244,11 @@ describe('GeoReplicable Store Actions', () => { ...@@ -133,11 +244,11 @@ describe('GeoReplicable Store Actions', () => {
it('should dispatch the request and error actions', done => { it('should dispatch the request and error actions', done => {
testAction( testAction(
actions.fetchReplicableItems, actions.fetchReplicableItemsRestful,
{}, {},
state, state,
[], [],
[{ type: 'requestReplicableItems' }, { type: 'receiveReplicableItemsError' }], [{ type: 'receiveReplicableItemsError' }],
done, done,
); );
}); });
......
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