Skip to content

Commit dd93b69

Browse files
Add get execution details command. fixes #32 (#33)
1 parent 8904519 commit dd93b69

File tree

5 files changed

+264
-1
lines changed

5 files changed

+264
-1
lines changed

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"@oclif/errors": "^1.1.2",
1313
"cli-ux": "^5.1.0",
1414
"halfred": "^1.1.1",
15+
"moment": "^2.24.0",
1516
"node-fetch": "^2.3.0",
1617
"uritemplate": "^0.3.4"
1718
},
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
Copyright 2019 Adobe. All rights reserved.
3+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License. You may obtain a copy
5+
of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
Unless required by applicable law or agreed to in writing, software distributed under
8+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
OF ANY KIND, either express or implied. See the License for the specific language
10+
governing permissions and limitations under the License.
11+
*/
12+
13+
const { Command } = require('@oclif/command')
14+
const { accessToken: getAccessToken } = require('@adobe/aio-cli-plugin-jwt-auth')
15+
const { getApiKey, getOrgId, getProgramId } = require('../../cloudmanager-helpers')
16+
const { cli } = require('cli-ux')
17+
const _ = require("lodash")
18+
const halfred = require('halfred')
19+
const moment = require("moment")
20+
const Client = require('../../client')
21+
const commonFlags = require('../../common-flags')
22+
23+
async function _getExecution (programId, pipelineId, executionId, passphrase) {
24+
const apiKey = await getApiKey()
25+
const accessToken = await getAccessToken(passphrase)
26+
const orgId = await getOrgId()
27+
return new Client(orgId, accessToken, apiKey).getExecution(programId, pipelineId, executionId)
28+
}
29+
30+
function formatAction (stepState) {
31+
if (stepState.action === 'deploy') {
32+
return `${_.startCase(stepState.environmentType)} ${_.startCase(stepState.action)}`
33+
} else {
34+
return _.startCase(stepState.action)
35+
}
36+
}
37+
38+
function formatTime (property) {
39+
return (stepState) => stepState[property] ? moment(stepState[property]).format('LLL') : ''
40+
}
41+
42+
function formatDuration (stepState) {
43+
return stepState.startedAt && stepState.finishedAt ?
44+
moment.duration(moment(stepState.finishedAt).diff(stepState.startedAt)).humanize() :
45+
''
46+
}
47+
48+
class GetExecutionStepDetails extends Command {
49+
async run () {
50+
const { args, flags } = this.parse(GetExecutionStepDetails)
51+
52+
const programId = await getProgramId(flags)
53+
54+
let result;
55+
56+
try {
57+
result = await this.getExecution(programId, args.pipelineId, args.executionId, flags.passphrase)
58+
} catch (error) {
59+
this.error(error.message)
60+
}
61+
62+
if (result) {
63+
result = halfred.parse(result)
64+
65+
const stepStates = result.embeddedArray('stepStates')
66+
67+
const buildStep = stepStates.find(stepState => stepState.action === 'build')
68+
const codeQualityStep = stepStates.find(stepState => stepState.action === 'codeQuality')
69+
codeQualityStep.startedAt = buildStep.finishedAt
70+
71+
cli.table(stepStates, {
72+
action: {
73+
header: 'Action',
74+
get: formatAction
75+
},
76+
status: {
77+
header: 'Status',
78+
get: (stepState) => _.startCase(stepState.status.toLowerCase())
79+
},
80+
startedAt: {
81+
header: 'Started At',
82+
get: formatTime('startedAt')
83+
},
84+
finishedAt: {
85+
header: 'Finished At',
86+
get: formatTime('finishedAt')
87+
},
88+
duration: {
89+
header: 'Duration',
90+
get: formatDuration
91+
}
92+
}, {
93+
printLine: this.log
94+
})
95+
96+
return stepStates
97+
}
98+
99+
return result
100+
}
101+
102+
async getExecution (programId, pipelineId, executionId, passphrase = null) {
103+
return _getExecution(programId, pipelineId, executionId, passphrase)
104+
}
105+
}
106+
107+
GetExecutionStepDetails.description = 'get quality gate results'
108+
109+
GetExecutionStepDetails.flags = {
110+
...commonFlags.global,
111+
...commonFlags.programId
112+
}
113+
114+
GetExecutionStepDetails.args = [
115+
{name: 'pipelineId', required: true, description: "the pipeline id"},
116+
{name: 'executionId', required: true, description: "the execution id"},
117+
]
118+
119+
120+
module.exports = GetExecutionStepDetails

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const CloudManagerCommand = require('./commands/cloudmanager')
2020
const CancelCurrentExecution = require('./commands/cloudmanager/cancel-current-execution')
2121
const AdvanceCurrentExecution = require('./commands/cloudmanager/advance-current-execution')
2222
const ListEnvironments = require('./commands/cloudmanager/list-environments')
23+
const GetExecutionStepDetails = require('./commands/cloudmanager/get-execution-step-details')
2324

2425
module.exports = {
2526
'aaa': CloudManagerCommand, // needs to be first alphabetically
@@ -31,5 +32,6 @@ module.exports = {
3132
'get-quality-gate-results': new GetQualityGateResults().getQualityGateResults,
3233
'cancel-current-execution': new CancelCurrentExecution().cancelCurrentExecution,
3334
'advance-current-execution': new AdvanceCurrentExecution().advanceCurrentExecution,
34-
'list-environments': new ListEnvironments().listEnvironments
35+
'list-environments': new ListEnvironments().listEnvironments,
36+
'get-execution-step-details': new GetExecutionStepDetails().getExecution
3537
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
Copyright 2019 Adobe. All rights reserved.
3+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License. You may obtain a copy
5+
of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
Unless required by applicable law or agreed to in writing, software distributed under
8+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
OF ANY KIND, either express or implied. See the License for the specific language
10+
governing permissions and limitations under the License.
11+
*/
12+
13+
const { cli } = require('cli-ux')
14+
const { setStore } = require('@adobe/aio-cna-core-config')
15+
const GetExecutionStepDetails = require('../../src/commands/cloudmanager/get-execution-step-details')
16+
17+
beforeEach(() => {
18+
setStore({})
19+
})
20+
21+
test('get-execution-step-details - missing arg', async () => {
22+
expect.assertions(2)
23+
24+
let runResult = GetExecutionStepDetails.run([])
25+
await expect(runResult instanceof Promise).toBeTruthy()
26+
await expect(runResult).rejects.toSatisfy(err => err.message.indexOf("Missing 2 required args") === 0)
27+
})
28+
29+
test('get-execution-step-details - missing config', async () => {
30+
expect.assertions(2)
31+
32+
let runResult = GetExecutionStepDetails.run(["5", "--programId", "7", "1001"])
33+
await expect(runResult instanceof Promise).toBeTruthy()
34+
await expect(runResult).rejects.toEqual(new Error('missing config data: jwt-auth'))
35+
})
36+
37+
test('get-execution-step-details - failure', async () => {
38+
setStore({
39+
'jwt-auth': JSON.stringify({
40+
client_id: '1234',
41+
jwt_payload: {
42+
iss: "good"
43+
}
44+
}),
45+
})
46+
47+
expect.assertions(2)
48+
49+
let runResult = GetExecutionStepDetails.run(["5", "--programId", "5", "1002"])
50+
await expect(runResult instanceof Promise).toBeTruthy()
51+
await expect(runResult).rejects.toEqual(new Error('Cannot get execution: https://cloudmanager.adobe.io/api/program/5/pipeline/5/execution/1002 (404 Not Found)'))
52+
})
53+
54+
test('get-execution-step-details - success', async () => {
55+
setStore({
56+
'jwt-auth': JSON.stringify({
57+
client_id: '1234',
58+
jwt_payload: {
59+
iss: "good"
60+
}
61+
}),
62+
})
63+
64+
expect.assertions(4)
65+
66+
let runResult = GetExecutionStepDetails.run(["--programId", "5", "7", "1001"])
67+
await expect(runResult instanceof Promise).toBeTruthy()
68+
await expect(runResult).resolves.toHaveLength(11)
69+
70+
const tableCall = cli.table.mock.calls[0];
71+
const tableData = tableCall[0];
72+
const columns = tableCall[1];
73+
74+
const columnResults = tableData.map(row => {
75+
const rowMapped = {};
76+
Object.keys(columns).forEach(column => {
77+
rowMapped[column] = columns[column].get(row)
78+
})
79+
return rowMapped;
80+
})
81+
82+
expect(columnResults).toMatchObject([{
83+
"action": "Validate",
84+
"status": "Finished"
85+
},{
86+
"action": "Build",
87+
"status": "Finished"
88+
},{
89+
"action": "Code Quality",
90+
"status": "Finished"
91+
},{
92+
"action": "Stage Deploy",
93+
"status": "Finished"
94+
},{
95+
"action": "Security Test",
96+
"status": "Finished"
97+
},{
98+
"action": "Load Test",
99+
"status": "Finished"
100+
},{
101+
"action": "Assets Test",
102+
"status": "Finished"
103+
},{
104+
"action": "Report Performance Test",
105+
"status": "Finished"
106+
},{
107+
"action": "Approval",
108+
"status": "Finished"
109+
},{
110+
"action": "Managed",
111+
"status": "Waiting"
112+
},{
113+
"action": "Prod Deploy",
114+
"status": "Not Started"
115+
}])
116+
117+
expect(columnResults[2].startedAt).toBeTruthy()
118+
})
119+
120+
test('get-execution-step-details - bad pipeline', async () => {
121+
setStore({
122+
'jwt-auth': JSON.stringify({
123+
client_id: '1234',
124+
jwt_payload: {
125+
iss: "good"
126+
}
127+
}),
128+
})
129+
130+
expect.assertions(2)
131+
132+
let runResult = GetExecutionStepDetails.run(["--programId", "5", "100", "1001"])
133+
await expect(runResult instanceof Promise).toBeTruthy()
134+
await expect(runResult).rejects.toEqual(new Error("Cannot get execution. Pipeline 100 does not exist."))
135+
})

0 commit comments

Comments
 (0)