UNPKG

cadence-web

Version:

Cadence Web UI

364 lines (342 loc) 11.4 kB
// Copyright (c) 2017-2024 Uber Technologies Inc. // // // 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. const btoa = require('btoa'); const Long = require('long'); const wfHistoryThrift = [ { eventId: 1, timestamp: new Long(800610625, 351737684, false), eventType: 'WorkflowExecutionStarted', workflowExecutionStartedEventAttributes: { attempt: null, workflowType: { name: 'github.com/uber/cadence/demo', }, taskList: { name: 'ci-task-queue', kind: null, }, identity: null, input: Buffer.from( JSON.stringify({ emails: ['jane@example.com', 'bob@example.com'], includeFooter: true, }) ), expirationTimestamp: null, continuedExecutionRunId: null, continuedFailureDetails: null, continuedFailureReason: null, cronSchedule: null, firstDecisionTaskBackoffSeconds: null, firstExecutionRunId: null, header: null, initiator: null, lastCompletionResult: null, memo: null, originalExecutionRunId: null, parentInitiatedEventId: null, parentWorkflowDomain: null, parentWorkflowExecution: null, prevAutoResetPoints: null, retryPolicy: null, searchAttributes: null, taskStartToCloseTimeoutSeconds: 30, executionStartToCloseTimeoutSeconds: 1080, }, }, { eventId: 2, timestamp: new Long(800610625, 351737684, false), eventType: 'DecisionTaskScheduled', decisionTaskScheduledEventAttributes: { startToCloseTimeoutSeconds: 180, attempt: 1, taskList: { name: 'canary-task-queue', kind: null, }, }, }, { eventId: 3, timestamp: new Long(800610625, 351737688, false), eventType: 'DecisionTaskStarted', decisionTaskStartedEventAttributes: { identity: 'box1@ci-task-queue', requestId: 'fafa095d-b4ca-423a-a812-223e62b5ccf8', scheduledEventId: 2, }, }, ]; const wfHistoryGrpc = [ { eventId: 1, eventTime: { seconds: '1510701850', nanos: '351393089' }, eventType: 'WorkflowExecutionStarted', workflowExecutionStartedEventAttributes: { attempt: null, workflowType: { name: 'github.com/uber/cadence/demo', }, taskList: { name: 'ci-task-queue', kind: null, }, identity: null, input: { data: btoa( JSON.stringify({ emails: ['jane@example.com', 'bob@example.com'], includeFooter: true, }) ), }, expirationTimestamp: null, continuedExecutionRunId: null, continuedFailure: { details: null, reason: null, }, cronSchedule: null, firstDecisionTaskBackoff: null, firstExecutionRunId: null, header: null, initiator: null, lastCompletionResult: null, memo: null, originalExecutionRunId: null, parentInitiatedEventId: null, parentWorkflowDomain: null, parentWorkflowExecution: null, prevAutoResetPoints: null, retryPolicy: null, searchAttributes: null, taskStartToCloseTimeout: { seconds: 30 }, executionStartToCloseTimeout: { seconds: 1080 }, }, }, { eventId: 2, eventTime: { seconds: '1510701850', nanos: '351393089' }, eventType: 'DecisionTaskScheduled', decisionTaskScheduledEventAttributes: { startToCloseTimeout: { seconds: 180 }, attempt: 1, taskList: { name: 'canary-task-queue', kind: null, }, }, }, { eventId: 3, eventTime: { seconds: '1510701867', nanos: '531262273' }, eventType: 'DecisionTaskStarted', decisionTaskStartedEventAttributes: { identity: 'box1@ci-task-queue', requestId: 'fafa095d-b4ca-423a-a812-223e62b5ccf8', scheduledEventId: 2, }, }, ]; const wfHistoryJson = [ { eventId: 1, timestamp: '2017-11-14T23:24:10.351Z', eventType: 'WorkflowExecutionStarted', details: Object.assign( {}, wfHistoryThrift[0].workflowExecutionStartedEventAttributes, { input: { emails: ['jane@example.com', 'bob@example.com'], includeFooter: true, }, } ), }, { eventId: 2, timestamp: '2017-11-14T23:24:10.351Z', eventType: 'DecisionTaskScheduled', details: wfHistoryThrift[1].decisionTaskScheduledEventAttributes, }, { eventId: 3, timestamp: '2017-11-14T23:24:27.531Z', eventType: 'DecisionTaskStarted', details: { identity: 'box1@ci-task-queue', requestId: 'fafa095d-b4ca-423a-a812-223e62b5ccf8', scheduledEventId: 2, }, }, ]; describe('Workflow History', function() { it('should forward the request to the cadence frontend with workflowId and runId', function() { this.test.GetWorkflowExecutionHistory = ({ getRequest }) => { const request = { tchannel: { HistoryEventFilterType: null, domain: 'canary', execution: { workflowId: 'ci/demo', runId: 'run1', }, maximumPageSize: 100, nextPageToken: null, skipArchival: null, waitForNewEvent: null, }, grpc: { domain: 'canary', workflowExecution: { workflowId: 'ci/demo', runId: 'run1', }, pageSize: 1000, nextPageToken: null, waitForNewEvent: null, historyEventFilterType: 'EVENT_FILTER_TYPE_INVALID', skipArchival: null, }, }; getRequest.should.deep.equal(request[process.env.TRANSPORT_CLIENT_TYPE]); return { history: { events: wfHistoryThrift }, nextPageToken: new Buffer('page2'), }; }; return request() .get('/api/domains/canary/workflows/ci%2Fdemo/run1/history') .expect(200) .expect('Content-Type', /json/); }); it('should forward the nextPageToken', function() { this.test.GetWorkflowExecutionHistory = ({ getRequest }) => { const requestNextPageToken = { tchannel: 'page2', grpc: 'cGFnZTI=', }; getRequest.nextPageToken .toString() .should.equal(requestNextPageToken[process.env.TRANSPORT_CLIENT_TYPE]); return { history: { events: [] }, nextPageToken: new Buffer('page3'), }; }; return request() .get( '/api/domains/canary/workflows/ci%2Fdemo/run1/history?nextPageToken=cGFnZTI%3D' ) .expect(200) .expect('Content-Type', /json/) .expect({ archived: null, history: { events: [] }, nextPageToken: 'cGFnZTM=', rawHistory: null, }); }); it('should support long polling by forwarding the waitForNewEvent flag', function() { this.test.GetWorkflowExecutionHistory = ({ getRequest }) => { getRequest.waitForNewEvent.should.be.true; return { history: { events: [{ eventId: 1 }] } }; }; return request() .get( '/api/domains/canary/workflows/ci%2Fdemo/run1/history?waitForNewEvent=true' ) .expect(200) .expect('Content-Type', /json/) .then(() => request() .get( '/api/domains/canary/workflows/ci%2Fdemo/run1/history?waitForNewEvent' ) .expect(200) ); }); it('should transform Long numbers to JavaScript numbers, Long dates to ISO date strings, and line-delimited JSON buffers to JSON', function() { const events = { tchannel: wfHistoryThrift, grpc: wfHistoryGrpc, }; this.test.GetWorkflowExecutionHistory = ({ getRequest }) => ({ history: { events: events[process.env.TRANSPORT_CLIENT_TYPE] }, nextPageToken: new Buffer('page2'), }); return request() .get('/api/domains/canary/workflows/ci%2Fdemo/run1/history') .expect(200) .expect({ archived: null, history: { events: wfHistoryJson }, nextPageToken: 'cGFnZTI=', rawHistory: null, }); }); describe('Export', function() { const wfHistoryCliJson = `[{"eventId":1,"timestamp":1510701850351393089,"eventType":"WorkflowExecutionStarted","workflowExecutionStartedEventAttributes":{"workflowType":{"name":"github.com/uber/cadence/demo"},"taskList":{"name":"ci-task-queue"},"input":"eyJlbWFpbHMiOlsiamFuZUBleGFtcGxlLmNvbSIsImJvYkBleGFtcGxlLmNvbSJdLCJpbmNsdWRlRm9vdGVyIjp0cnVlfQ==","executionStartToCloseTimeoutSeconds":1080,"taskStartToCloseTimeoutSeconds":30}},{"eventId":2,"timestamp":1510701850351393089,"eventType":"DecisionTaskScheduled","decisionTaskScheduledEventAttributes":{"taskList":{"name":"canary-task-queue"},"startToCloseTimeoutSeconds":180,"attempt":1}},{"eventId":3,"timestamp":1510701867531262273,"eventType":"DecisionTaskStarted","decisionTaskStartedEventAttributes":{"scheduledEventId":2,"identity":"box1@ci-task-queue","requestId":"fafa095d-b4ca-423a-a812-223e62b5ccf8"}}]`; it('should be able to export history in a format compatible with the CLI', function() { const events = { tchannel: wfHistoryThrift, grpc: wfHistoryGrpc, }; this.test.GetWorkflowExecutionHistory = ({ getRequest }) => ({ history: { events: events[process.env.TRANSPORT_CLIENT_TYPE] }, }); return request() .get('/api/domains/canary/workflows/ci%2Fdemo/run1/export') .expect(200) .expect(wfHistoryCliJson); }); it('should page through all responses', async function() { let calls = 0; const events = { tchannel: wfHistoryThrift, grpc: wfHistoryGrpc, }; this.test.GetWorkflowExecutionHistory = ({ getRequest }) => { if (calls > 0) { getRequest.nextPageToken.should.be.ok; } else { should.not.exist(getRequest.nextPageToken); } const resp = { history: { events: [events[process.env.TRANSPORT_CLIENT_TYPE][calls]], }, }; if (++calls < events[process.env.TRANSPORT_CLIENT_TYPE].length) { resp.nextPageToken = new Buffer('page' + calls); } return resp; }; return request() .get('/api/domains/canary/workflows/ci%2Fdemo/run1/export') .expect(200) .expect(wfHistoryCliJson); }); }); });