Skip to content

Commit c3756bb

Browse files
Working implementation of opstatus cmd with progress bars.
1 parent b84a75e commit c3756bb

File tree

6 files changed

+386
-123
lines changed

6 files changed

+386
-123
lines changed

README.md

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ The following Environment variables are optional:
5050
* [`sc missionctrl broker delete`](#sc-missionctrl-broker-delete)
5151
* [`sc missionctrl broker display`](#sc-missionctrl-broker-display)
5252
* [`sc missionctrl broker list`](#sc-missionctrl-broker-list)
53+
* [`sc missionctrl broker opstatus`](#sc-missionctrl-broker-opstatus)
5354
* [`sc platform env create`](#sc-platform-env-create)
5455
* [`sc platform env delete`](#sc-platform-env-delete)
5556
* [`sc platform env display`](#sc-platform-env-display)
@@ -92,8 +93,8 @@ Create an event broker service. You must provide a unique name and select a serv
9293

9394
```
9495
USAGE
95-
$ sc missionctrl broker create -d <value> -n <value> -c <value> [-e <value>] [-l] [-s <value>] [-m <value>] [-r] [-v
96-
<value>]
96+
$ sc missionctrl broker create -d <value> -n <value> -c <value> [--json] [--log-level debug|warn|error|info|trace] [-e
97+
<value>] [-l] [-s <value>] [-m <value>] [-r] [-v <value>]
9798
9899
FLAGS
99100
-c, --service-class-id=<value>
@@ -126,6 +127,11 @@ FLAGS
126127
-v, --version=<value>
127128
The event broker version. A default version is provided when this is not specified.
128129
130+
GLOBAL FLAGS
131+
--json Format output as json.
132+
--log-level=<option> [default: info] Specify level for logging.
133+
<options: debug|warn|error|info|trace>
134+
129135
DESCRIPTION
130136
Create an event broker service. You must provide a unique name and select a service class and datacenter. You can
131137
optionally define other properties for the event broker service.
@@ -146,12 +152,17 @@ Delete a service using its unique identifier.
146152

147153
```
148154
USAGE
149-
$ sc missionctrl broker delete [-b <value>] [-n <value>]
155+
$ sc missionctrl broker delete [--json] [--log-level debug|warn|error|info|trace] [-b <value>] [-n <value>]
150156
151157
FLAGS
152158
-b, --broker-id=<value> Id of the event broker service.
153159
-n, --name=<value> Name of the event broker service.
154160
161+
GLOBAL FLAGS
162+
--json Format output as json.
163+
--log-level=<option> [default: info] Specify level for logging.
164+
<options: debug|warn|error|info|trace>
165+
155166
DESCRIPTION
156167
Delete a service using its unique identifier.
157168
@@ -173,12 +184,17 @@ Get the details of an event broker service using its identifier or name.
173184

174185
```
175186
USAGE
176-
$ sc missionctrl broker display [-b <value>] [-n <value>]
187+
$ sc missionctrl broker display [--json] [--log-level debug|warn|error|info|trace] [-b <value>] [-n <value>]
177188
178189
FLAGS
179190
-b, --broker-id=<value> Id of the event broker service.
180191
-n, --name=<value> Name of the event broker service.
181192
193+
GLOBAL FLAGS
194+
--json Format output as json.
195+
--log-level=<option> [default: info] Specify level for logging.
196+
<options: debug|warn|error|info|trace>
197+
182198
DESCRIPTION
183199
Get the details of an event broker service using its identifier or name.
184200
@@ -199,7 +215,8 @@ Get a listing of event broker services.
199215

200216
```
201217
USAGE
202-
$ sc missionctrl broker list [-n <value>] [--pageNumber <value>] [--pageSize <value>] [--sort <value>]
218+
$ sc missionctrl broker list [--json] [--log-level debug|warn|error|info|trace] [-n <value>] [--pageNumber <value>]
219+
[--pageSize <value>] [--sort <value>]
203220
204221
FLAGS
205222
-n, --name=<value>
@@ -219,6 +236,11 @@ FLAGS
219236
* attributes-names
220237
* attributes-names:sort-order
221238
239+
GLOBAL FLAGS
240+
--json Format output as json.
241+
--log-level=<option> [default: info] Specify level for logging.
242+
<options: debug|warn|error|info|trace>
243+
222244
DESCRIPTION
223245
Get a listing of event broker services.
224246
@@ -233,6 +255,43 @@ EXAMPLES
233255

234256
_See code: [src/commands/missionctrl/broker/list.ts](https://github.com/dishantlangayan/solace-cloud-cli/blob/v0.0.2/src/commands/missionctrl/broker/list.ts)_
235257

258+
## `sc missionctrl broker opstatus`
259+
260+
Get the status of all operations being performed on an event broker service.
261+
262+
```
263+
USAGE
264+
$ sc missionctrl broker opstatus [--json] [--log-level debug|warn|error|info|trace] [-b <value>] [-n <value>] [-p] [-w
265+
<value>]
266+
267+
FLAGS
268+
-b, --broker-id=<value> Id of the event broker service.
269+
-n, --name=<value> Name of the event broker service.
270+
-p, --show-progress Displays a status bar of the in-progress operations. The command will wait for completion of
271+
each step of the operation.
272+
-w, --wait-ms=<value> The milliseconds to wait between API calls for checking progress of the operation. Default is
273+
5000 ms.
274+
275+
GLOBAL FLAGS
276+
--json Format output as json.
277+
--log-level=<option> [default: info] Specify level for logging.
278+
<options: debug|warn|error|info|trace>
279+
280+
DESCRIPTION
281+
Get the status of all operations being performed on an event broker service.
282+
To get the operation status, you must provide the identifier or name of the event broker service.
283+
284+
Token Permissions: [ mission_control:access or services:get or services:get:self or services:view or
285+
services:view:self ]
286+
287+
EXAMPLES
288+
$ sc missionctrl broker opstatus -b <broker-id>
289+
290+
$ sc missionctrl broker opstatus -n <broker-name>
291+
```
292+
293+
_See code: [src/commands/missionctrl/broker/opstatus.ts](https://github.com/dishantlangayan/solace-cloud-cli/blob/v0.0.2/src/commands/missionctrl/broker/opstatus.ts)_
294+
236295
## `sc platform env create`
237296

238297
Create a new environment.
Lines changed: 105 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import { Flags } from '@oclif/core'
2-
import cliProgress from 'cli-progress'
2+
import { MultiBar, Presets, SingleBar } from 'cli-progress'
33

44
import { ScCommand } from '../../../sc-command.js'
5-
import { EventBrokerListApiResponse, OperationData, OperationResponse } from '../../../types/broker.js'
5+
import { AllOperationResponse, EventBrokerListApiResponse, OperationData, OperationResponse } from '../../../types/broker.js'
6+
import { renderTable, sleep } from '../../../util/internal.js'
67
import { ScConnection } from '../../../util/sc-connection.js'
7-
import { camelCaseToTitleCase, renderKeyValueTable, sleep } from '../../../util/internal.js'
88

99
export default class MissionctrlBrokerOpstatus extends ScCommand<typeof MissionctrlBrokerOpstatus> {
1010
static override args = {}
11-
static override description = `Get the status of an operation that being performed on an event broker service.
12-
To get the operation, you provide identifier of the operation and the identifier of the event broker service.
11+
static override description = `Get the status of all operations being performed on an event broker service.
12+
To get the operation status, you must provide the identifier or name of the event broker service.
1313
1414
Token Permissions: [ mission_control:access or services:get or services:get:self or services:view or services:view:self ]`
1515
static override examples = [
16-
'<%= config.bin %> <%= command.id %>',
16+
'<%= config.bin %> <%= command.id %> -b <broker-id>',
17+
'<%= config.bin %> <%= command.id %> -n <broker-name>',
1718
]
1819
static override flags = {
1920
'broker-id': Flags.string({
@@ -26,32 +27,27 @@ export default class MissionctrlBrokerOpstatus extends ScCommand<typeof Missionc
2627
description: 'Name of the event broker service.',
2728
exactlyOne: ['broker-id', 'name'],
2829
}),
29-
'operation-id': Flags.string({
30-
char: 'o',
31-
description: 'The identifier of the operation being performed on the event broker service.'
32-
}),
3330
'show-progress': Flags.boolean({
3431
char: 'p',
35-
description: 'Displays a status bar of the in-progress operation. The command will wait for completion of each step of the operation.'
32+
description: 'Displays a status bar of the in-progress operations. The command will wait for completion of each step of the operation.'
3633
}),
3734
'wait-ms': Flags.integer({
3835
char: 'w',
39-
description: 'The milliseconds to wait between API call in when showing progress. Default is 5000 ms.'
36+
description: 'The milliseconds to wait between API calls for checking progress of the operation. Default is 5000 ms.'
4037
}),
4138
}
4239

43-
public async run(): Promise<OperationData> {
40+
public async run(): Promise<OperationData[]> {
4441
const { flags } = await this.parse(MissionctrlBrokerOpstatus)
4542

4643
const name = flags.name ?? ''
4744
let brokerId = flags['broker-id'] ?? ''
48-
const operationId = flags['operation-id'] ?? ''
4945
const showProgress = flags['show-progress'] ?? false
5046
const waitMs = flags['wait-ms'] ?? 5000
5147

5248
const conn = new ScConnection()
5349

54-
// API url
50+
// Base API url
5551
let apiUrl: string = `/missionControl/eventBrokerServices`
5652

5753
// If broker name provided, retrieve the broker service id first
@@ -60,7 +56,7 @@ export default class MissionctrlBrokerOpstatus extends ScCommand<typeof Missionc
6056
// API call to get broker by name
6157
apiUrl += `?customAttributes=name=="${name}"`
6258
const resp = await conn.get<EventBrokerListApiResponse>(apiUrl)
63-
// TODO FUTURE: show status of multiple brokers operations that match the name
59+
// FUTURE: show status of multiple brokers operations that match the name
6460
if (resp.data.length > 1) {
6561
this.error(`Multiple broker services found with: ${name}. Exactly one broker service must match the provided name.`)
6662
} else {
@@ -69,59 +65,107 @@ export default class MissionctrlBrokerOpstatus extends ScCommand<typeof Missionc
6965
}
7066

7167
// API call to retrieve status of the broker operation
72-
apiUrl = `/missionControl/eventBrokerServices/${brokerId}/operations/${operationId}`
73-
if (showProgress) {
74-
apiUrl += '?expand=progressLogs'
75-
}
76-
let resp = await conn.get<OperationResponse>(apiUrl)
77-
this.print(resp.data)
78-
79-
// Display progress bar for each step included in the progress logs
80-
// Enable progress bar if set
81-
if (showProgress && resp.data.progressLogs && resp.data.progressLogs.length > 0) {
82-
let progressLogs = resp.data.progressLogs
83-
let numSteps = progressLogs.length
84-
85-
// Create a new progress bar instance and use shades_classic theme
86-
const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
87-
88-
// start the progress bar with a total value of size of the steps and start value of 0
89-
progressBar.start(numSteps, 0);
68+
apiUrl = `/missionControl/eventBrokerServices/${brokerId}/operations`
69+
70+
const resp = await conn.get<AllOperationResponse>(apiUrl)
71+
const opStatusArray = [
72+
['Operation Id', 'Operation Type', 'Status', 'Created Time', 'Completed Time'],
73+
...resp.data.map((item: OperationData) => [
74+
item.id,
75+
item.operationType,
76+
item.status,
77+
item.createdTime,
78+
item.completedTime,
79+
]),
80+
]
9081

91-
// Update the progress with the steps completed
92-
let completedNumSteps = 0
93-
while (completedNumSteps < numSteps) {
94-
// Wait before making the next API call
95-
await sleep(waitMs)
96-
// Make another API call to get the lastest progress
97-
resp = await conn.get<OperationResponse>(apiUrl)
98-
if (resp.data.progressLogs) {
99-
progressLogs = resp.data.progressLogs
100-
for (const progressLog of progressLogs) {
101-
if (progressLog.step === 'success') {
102-
completedNumSteps++
103-
}
104-
}
105-
// Update progress bar
82+
// Display results as a table
83+
this.log(renderTable(opStatusArray))
84+
85+
// If show-progress flag is set, display progress bars for each operation
86+
if(showProgress && resp.data.length > 0) {
87+
// Create progress bar for each operation
88+
const multiProgressBar = new MultiBar({
89+
clearOnComplete: false,
90+
format: ' {bar} | {operationType} | {value}/{total}',
91+
hideCursor: true,
92+
}, Presets.shades_classic)
93+
94+
// Get the initial progress for each operation
95+
const progressBars: [string, SingleBar][] = []
96+
let completedOperations = 0
97+
let allCompleted = false
98+
for (const operationData of resp.data) {
99+
const opStatusApiUrl = `/missionControl/eventBrokerServices/${brokerId}/operations/${operationData.id}?expand=progressLogs`
100+
// eslint-disable-next-line no-await-in-loop
101+
const opStatusResp = await conn.get<OperationResponse>(opStatusApiUrl)
102+
this.debug(`Operation ID: ${operationData.id}, Type: ${operationData.operationType}, Status: ${operationData.status}`)
103+
if (opStatusResp.data.progressLogs) {
104+
const numSteps = opStatusResp.data.progressLogs.length
105+
// start a new progress bar for the operation with a total value of size of the steps
106+
const progressBar = multiProgressBar.create(numSteps, 0, { operationType: operationData.operationType })
107+
// Update the progress with the steps completed
108+
const completedNumSteps = opStatusResp.data.progressLogs.filter(log => log.status === 'success').length
106109
progressBar.update(completedNumSteps)
107-
} else {
108-
break
110+
if (completedNumSteps === numSteps || opStatusResp.data.status === 'SUCCEEDED' || opStatusResp.data.status === 'FAILED') {
111+
completedOperations += 1
112+
progressBar.stop()
113+
}
114+
115+
// Add the operation ID and progress bar to the list
116+
progressBars.push([operationData.id, progressBar])
109117
}
110118
}
111119

112-
// stop the progress bar
113-
progressBar.stop()
120+
// Check if all operations are completed
121+
if(completedOperations === progressBars.length) {
122+
allCompleted = true
123+
}
124+
125+
// Loop until all operations are completed
126+
while (!allCompleted) {
127+
// Wait before making the next API call
128+
sleep(waitMs)
129+
// Poll the status of all operations and update the progress bars
130+
// eslint-disable-next-line no-await-in-loop
131+
allCompleted = await this.pollAllOperationStatus(conn, brokerId, progressBars)
132+
}
133+
134+
multiProgressBar.stop()
135+
this.log() // Add a new line for better readability
114136
}
115137

116138
return resp.data
117139
}
118140

119-
private print(environment: OperationData): void {
120-
const tableRows = [
121-
['Key', 'Value'],
122-
...Object.entries(environment).map(([key, value]) => [camelCaseToTitleCase(key), value]),
123-
]
124-
this.log()
125-
this.log(renderKeyValueTable(tableRows))
141+
private async pollAllOperationStatus(conn: ScConnection, brokerId: string, progressBars: [string, SingleBar][]): Promise<boolean> {
142+
let completedOperations = 0
143+
let allCompleted = false
144+
// For each operation, get the latest status and update the progress bar
145+
for (const [operationId, progressBar] of progressBars) {
146+
const opStatusApiUrl = `/missionControl/eventBrokerServices/${brokerId}/operations/${operationId}?expand=progressLogs`
147+
// eslint-disable-next-line no-await-in-loop
148+
const opStatusResp = await conn.get<OperationResponse>(opStatusApiUrl)
149+
if (opStatusResp.data.progressLogs) {
150+
const numSteps = opStatusResp.data.progressLogs.length
151+
const completedNumSteps = opStatusResp.data.progressLogs.filter(log => log.status === 'success').length
152+
// Update progress bar
153+
progressBar.setTotal(numSteps)
154+
progressBar.update(completedNumSteps)
155+
if (completedNumSteps === numSteps || opStatusResp.data.status === 'SUCCEEDED' || opStatusResp.data.status === 'FAILED') {
156+
completedOperations += 1
157+
progressBar.stop()
158+
}
159+
} else {
160+
progressBar.stop()
161+
}
162+
}
163+
164+
// Check if all operations are completed
165+
if(completedOperations === progressBars.length) {
166+
allCompleted = true
167+
}
168+
169+
return allCompleted
126170
}
127-
}
171+
}

0 commit comments

Comments
 (0)