diff --git a/app/assets/javascripts/ci/pipeline_details/graph/constants.js b/app/assets/javascripts/ci/pipeline_details/graph/constants.js index 6d23c553f69adbbcb9fff98e207cdf880ce8a28f..f1498600aaa37d224212f1927abb8b02a05a8e4e 100644 --- a/app/assets/javascripts/ci/pipeline_details/graph/constants.js +++ b/app/assets/javascripts/ci/pipeline_details/graph/constants.js @@ -25,3 +25,10 @@ export const IID_FAILURE = 'missing_iid'; export const RETRY_ACTION_TITLE = 'Retry'; export const MANUAL_ACTION_TITLE = 'Run'; + +/* + this poll interval is shared between the graph, + pipeline header, jobs tab and failed jobs tab to + keep all the data relatively in sync +*/ +export const POLL_INTERVAL = 10000; diff --git a/app/assets/javascripts/ci/pipeline_details/graph/graph_component_wrapper.vue b/app/assets/javascripts/ci/pipeline_details/graph/graph_component_wrapper.vue index e5a25073d2454f26fc69920c8c8f04d5856d5ff8..e09c3da9c2cbd944a47d69ec7b782965505ae3f2 100644 --- a/app/assets/javascripts/ci/pipeline_details/graph/graph_component_wrapper.vue +++ b/app/assets/javascripts/ci/pipeline_details/graph/graph_component_wrapper.vue @@ -15,6 +15,7 @@ import { SKIP_RETRY_MODAL_KEY, STAGE_VIEW, VIEW_TYPE_KEY, + POLL_INTERVAL, } from './constants'; import PipelineGraph from './components/graph_component.vue'; import GraphViewSelector from './components/graph_view_selector.vue'; @@ -129,7 +130,7 @@ export default { return getQueryHeaders(this.graphqlResourceEtag); }, query: getPipelineDetails, - pollInterval: 10000, + pollInterval: POLL_INTERVAL, variables() { return { projectPath: this.pipelineProjectPath, diff --git a/app/assets/javascripts/ci/pipeline_details/header/constants.js b/app/assets/javascripts/ci/pipeline_details/header/constants.js index a4aed7b8f46e7df8007f90453317fc347a283eff..2bc110d9dc21cdd8e6b8693da1d9bc07b449c856 100644 --- a/app/assets/javascripts/ci/pipeline_details/header/constants.js +++ b/app/assets/javascripts/ci/pipeline_details/header/constants.js @@ -1,7 +1,5 @@ export const DELETE_MODAL_ID = 'pipeline-delete-modal'; -export const POLL_INTERVAL = 10000; - export const SCHEDULE_SOURCE = 'schedule'; export const AUTO_DEVOPS_SOURCE = 'AUTO_DEVOPS_SOURCE'; export const DETACHED_EVENT_TYPE = 'DETACHED'; diff --git a/app/assets/javascripts/ci/pipeline_details/header/pipeline_header.vue b/app/assets/javascripts/ci/pipeline_details/header/pipeline_header.vue index 9a9f971dedfa47c05afbcbabb4db2641eeb4ceda..6564588486f16c5ac859e91617ae7a2c8713b485 100644 --- a/app/assets/javascripts/ci/pipeline_details/header/pipeline_header.vue +++ b/app/assets/javascripts/ci/pipeline_details/header/pipeline_header.vue @@ -14,10 +14,10 @@ import cancelPipelineMutation from '../graphql/mutations/cancel_pipeline.mutatio import deletePipelineMutation from '../graphql/mutations/delete_pipeline.mutation.graphql'; import retryPipelineMutation from '../graphql/mutations/retry_pipeline.mutation.graphql'; import { getQueryHeaders } from '../graph/utils'; +import { POLL_INTERVAL } from '../graph/constants'; import HeaderActions from './components/header_actions.vue'; import HeaderBadges from './components/header_badges.vue'; import getPipelineQuery from './graphql/queries/get_pipeline_header_data.query.graphql'; -import { POLL_INTERVAL } from './constants'; export default { name: 'PipelineHeader', diff --git a/app/assets/javascripts/ci/pipeline_details/jobs/failed_jobs_app.vue b/app/assets/javascripts/ci/pipeline_details/jobs/failed_jobs_app.vue index b946a40e590102048411c863346edb0ed3d6f9ae..dc11af69bb4c23a40fbe252d0ba83a9f8836e19f 100644 --- a/app/assets/javascripts/ci/pipeline_details/jobs/failed_jobs_app.vue +++ b/app/assets/javascripts/ci/pipeline_details/jobs/failed_jobs_app.vue @@ -2,6 +2,8 @@ import { GlLoadingIcon } from '@gitlab/ui'; import { s__ } from '~/locale'; import { createAlert } from '~/alert'; +import { getQueryHeaders } from '../graph/utils'; +import { POLL_INTERVAL } from '../graph/constants'; import GetFailedJobsQuery from './graphql/queries/get_failed_jobs.query.graphql'; import FailedJobsTable from './components/failed_jobs_table.vue'; @@ -17,10 +19,17 @@ export default { pipelineIid: { default: '', }, + graphqlResourceEtag: { + default: '', + }, }, apollo: { failedJobs: { + context() { + return getQueryHeaders(this.graphqlResourceEtag); + }, query: GetFailedJobsQuery, + pollInterval: POLL_INTERVAL, variables() { return { fullPath: this.projectPath, diff --git a/app/assets/javascripts/ci/pipeline_details/jobs/jobs_app.vue b/app/assets/javascripts/ci/pipeline_details/jobs/jobs_app.vue index 81b6152347dcd5f8b4510c3d25f02ab890f040fe..0c1a8e995ee21bdadeefe5bac1eb9f809d2bd98c 100644 --- a/app/assets/javascripts/ci/pipeline_details/jobs/jobs_app.vue +++ b/app/assets/javascripts/ci/pipeline_details/jobs/jobs_app.vue @@ -6,6 +6,8 @@ import { __ } from '~/locale'; import eventHub from '~/ci/jobs_page/event_hub'; import JobsTable from '~/ci/jobs_page/components/jobs_table.vue'; import { JOBS_TAB_FIELDS } from '~/ci/jobs_page/constants'; +import { getQueryHeaders } from '../graph/utils'; +import { POLL_INTERVAL } from '../graph/constants'; import getPipelineJobs from './graphql/queries/get_pipeline_jobs.query.graphql'; export default { @@ -23,10 +25,17 @@ export default { pipelineIid: { default: '', }, + graphqlResourceEtag: { + default: '', + }, }, apollo: { jobs: { + context() { + return getQueryHeaders(this.graphqlResourceEtag); + }, query: getPipelineJobs, + pollInterval: POLL_INTERVAL, variables() { return { ...this.queryVariables, diff --git a/spec/frontend/ci/pipeline_details/jobs/failed_jobs_app_spec.js b/spec/frontend/ci/pipeline_details/jobs/failed_jobs_app_spec.js index 17b43aa422b3399fd74855811dd219c0cdcf7e76..a0806cabadb08dcfb259b4336d24a7acad025295 100644 --- a/spec/frontend/ci/pipeline_details/jobs/failed_jobs_app_spec.js +++ b/spec/frontend/ci/pipeline_details/jobs/failed_jobs_app_spec.js @@ -8,6 +8,7 @@ import { createAlert } from '~/alert'; import FailedJobsApp from '~/ci/pipeline_details/jobs/failed_jobs_app.vue'; import FailedJobsTable from '~/ci/pipeline_details/jobs/components/failed_jobs_table.vue'; import GetFailedJobsQuery from '~/ci/pipeline_details/jobs/graphql/queries/get_failed_jobs.query.graphql'; +import { POLL_INTERVAL } from '~/ci/pipeline_details/graph/constants'; import { mockFailedJobsQueryResponse } from 'jest/ci/pipeline_details/mock_data'; Vue.use(VueApollo); @@ -27,11 +28,14 @@ describe('Failed Jobs App', () => { return createMockApollo(requestHandlers); }; + const graphqlResourceEtag = '/api/graphql:pipelines/id/1'; + const createComponent = (resolver) => { wrapper = shallowMount(FailedJobsApp, { provide: { fullPath: 'root/ci-project', pipelineIid: 1, + graphqlResourceEtag, }, apolloProvider: createMockApolloProvider(resolver), }); @@ -77,4 +81,18 @@ describe('Failed Jobs App', () => { message: 'There was a problem fetching the failed jobs.', }); }); + + describe('polling', () => { + beforeEach(() => { + createComponent(resolverSpy); + }); + + it('polls for query data', () => { + expect(resolverSpy).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(POLL_INTERVAL); + + expect(resolverSpy).toHaveBeenCalledTimes(2); + }); + }); }); diff --git a/spec/frontend/ci/pipeline_details/jobs/jobs_app_spec.js b/spec/frontend/ci/pipeline_details/jobs/jobs_app_spec.js index 4a3a901502eccc60e66c9ae0a4efacb487d01608..ff02b8542b9e2e45432bc7818f18aa8e160a8dea 100644 --- a/spec/frontend/ci/pipeline_details/jobs/jobs_app_spec.js +++ b/spec/frontend/ci/pipeline_details/jobs/jobs_app_spec.js @@ -8,6 +8,7 @@ import { createAlert } from '~/alert'; import JobsApp from '~/ci/pipeline_details/jobs/jobs_app.vue'; import JobsTable from '~/ci/jobs_page/components/jobs_table.vue'; import getPipelineJobsQuery from '~/ci/pipeline_details/jobs/graphql/queries/get_pipeline_jobs.query.graphql'; +import { POLL_INTERVAL } from '~/ci/pipeline_details/graph/constants'; import { mockPipelineJobsQueryResponse } from '../mock_data'; Vue.use(VueApollo); @@ -31,11 +32,14 @@ describe('Jobs app', () => { return createMockApollo(requestHandlers); }; + const graphqlResourceEtag = '/api/graphql:pipelines/id/1'; + const createComponent = (resolver) => { wrapper = shallowMount(JobsApp, { provide: { projectPath: 'root/ci-project', pipelineIid: 1, + graphqlResourceEtag, }, apolloProvider: createMockApolloProvider(resolver), }); @@ -124,4 +128,18 @@ describe('Jobs app', () => { expect(findSkeletonLoader().exists()).toBe(false); }); + + describe('polling', () => { + beforeEach(() => { + createComponent(resolverSpy); + }); + + it('polls for query data', () => { + expect(resolverSpy).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(POLL_INTERVAL); + + expect(resolverSpy).toHaveBeenCalledTimes(2); + }); + }); });