Skip to content

Commit b119bf9

Browse files
committed
Added plan rendering
1 parent f68a209 commit b119bf9

8 files changed

Lines changed: 205 additions & 7 deletions

File tree

src/commands/plan/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ export default class Plan extends Command {
4040

4141
const resolvedPath = path.resolve(flags.path ?? '.');
4242

43-
await PlanOrchestrator.run(resolvedPath);
43+
const { plan } = await PlanOrchestrator.run(resolvedPath);
44+
reporter.displayPlan(plan);
4445

4546
this.exit(0);
4647
}
Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,32 @@ import { EventEmitter } from 'node:events';
44
import React, { useEffect, useState } from 'react';
55

66
import { ProcessState, ProcessStatus } from '../reporters/default-reporter.js';
7+
import { PlanResponseData } from 'codify-schemas';
8+
import { PlanComponent } from './plan/plan.js';
9+
10+
export function DefaultComponent(props: {
11+
emitter: EventEmitter
12+
}) {
13+
const { emitter } = props;
714

8-
export function PlanComponent({ eventTarget }: { eventTarget: EventEmitter }) {
915
const [staticOutput, setStaticOutput] = useState([] as Array<string>);
1016
const [processState, setProcessState] = useState({
1117
process: [],
1218
} as ProcessState);
19+
const [planState, setPlanState] = useState(null as PlanResponseData[] | null);
1320

1421
useEffect(() => {
15-
eventTarget.on('static_output', (newValue: any) => {
22+
emitter.on('static_output', (newValue: any) => {
1623
setStaticOutput([...newValue]);
1724
});
1825

19-
eventTarget.on('process', (state: ProcessState) => {
26+
emitter.on('process', (state: ProcessState) => {
2027
setProcessState(structuredClone(state));
2128
});
29+
30+
emitter.on('plan', (plan: PlanResponseData[]) => {
31+
setPlanState(plan);
32+
});
2233
}, []);
2334

2435
return <Box flexDirection="column">
@@ -47,5 +58,12 @@ export function PlanComponent({ eventTarget }: { eventTarget: EventEmitter }) {
4758
</Box>
4859
) ?? []
4960
}
61+
{
62+
planState
63+
? <Static items={[planState]}>{
64+
(plan, idx) => <PlanComponent key={idx} plan={plan}/>
65+
}</Static>
66+
: <></>
67+
}
5068
</Box>
5169
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { ParameterOperation, ResourceOperation } from 'codify-schemas';
2+
import { Box, Text } from 'ink';
3+
import React from 'react';
4+
5+
export function ResourceOperationSymbol(props: {
6+
resourceOperation: ResourceOperation
7+
}) {
8+
switch (props.resourceOperation) {
9+
case ResourceOperation.NOOP: {
10+
return <Text></Text>
11+
}
12+
13+
case ResourceOperation.CREATE: {
14+
return <Text color="green">+</Text>
15+
}
16+
17+
case ResourceOperation.DESTROY: {
18+
return <Text color="red">-</Text>
19+
}
20+
21+
case ResourceOperation.RECREATE: {
22+
return <Box>
23+
<Text color="red">-</Text><Text color="green">+</Text>
24+
</Box>
25+
}
26+
27+
case ResourceOperation.MODIFY: {
28+
return <Text color="yellow">~</Text>
29+
}
30+
}
31+
}
32+
33+
export function ParameterOperationSymbol(props: {
34+
parameterOperation: ParameterOperation
35+
}) {
36+
switch (props.parameterOperation) {
37+
case ParameterOperation.NOOP: {
38+
return <Text></Text>
39+
}
40+
41+
case ParameterOperation.ADD: {
42+
return <Text color="green">+</Text>
43+
}
44+
45+
case ParameterOperation.REMOVE: {
46+
return <Text color="red">-</Text>
47+
}
48+
49+
case ParameterOperation.MODIFY: {
50+
return <Text color="yellow">~</Text>
51+
}
52+
}
53+
}

src/ui/components/plan/plan.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { OrderedList } from '@inkjs/ui';
2+
import { PlanResponseData } from 'codify-schemas';
3+
import { Box, Text } from 'ink';
4+
import React from 'react';
5+
6+
import { ResourceText } from './resource-text.js';
7+
8+
export function PlanComponent(props: {
9+
plan: PlanResponseData[]
10+
}) {
11+
// console.log(JSON.stringify(props.plan, null, 2));
12+
13+
return <Box flexDirection="column">
14+
<Box borderStyle="round" borderColor="green">
15+
<Text>Codify Plan</Text>
16+
</Box>
17+
<Text>The following actions will be performed: </Text>
18+
<Text> </Text>
19+
<Box marginLeft={1}>
20+
<OrderedList>{
21+
props.plan.map((p, idx) =>
22+
<OrderedList.Item key={idx}>
23+
<Box flexDirection="column" marginBottom={1}>
24+
<ResourceText plan={p}/>
25+
<Text>
26+
<Text>Parameters: </Text>
27+
<Text>{JSON.stringify(p.parameters, null, 2)}</Text>
28+
{/* <Box flexDirection='column' marginLeft={2}>{ */}
29+
{/* p.parameters.map((parameter, idx2) => */}
30+
{/* <Box key={idx2}> */}
31+
{/* /!* <ParameterOperationSymbol parameterOperation={parameter.operation}/> *!/ */}
32+
{/* /!* <Text>{parameter.name}</Text> *!/ */}
33+
{/* /!* <Spacer/> *!/ */}
34+
{/* /!* <Text> *!/ */}
35+
{/* /!* <Text>{String(parameter.previousValue)}</Text> *!/ */}
36+
{/* /!* <Text>{' -> '}</Text> *!/ */}
37+
{/* /!* <Text>{String(parameter.newValue)}</Text> *!/ */}
38+
{/* /!* </Text> *!/ */}
39+
{/* <Text>{JSON.stringify(parameter, null, 2)}</Text> */}
40+
{/* </Box> */}
41+
{/* ) */}
42+
{/* }</Box> */}
43+
</Text>
44+
</Box>
45+
</OrderedList.Item>
46+
)
47+
}</OrderedList>
48+
</Box>
49+
</Box>
50+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { PlanResponseData, ResourceOperation } from 'codify-schemas';
2+
import { Box, Text } from 'ink';
3+
import React from 'react';
4+
5+
import { ResourceOperationSymbol } from './operation-symbol.js';
6+
7+
export function ResourceText(props: {
8+
plan: PlanResponseData
9+
}) {
10+
const { plan } = props;
11+
const { operation, resourceName, resourceType } = plan;
12+
13+
const fullyQualifiedName = resourceType + (resourceName ? `.${resourceName}` : '');
14+
let backgroundColor = '';
15+
let operationName = '';
16+
17+
switch (operation) {
18+
case ResourceOperation.NOOP: {
19+
backgroundColor = '#D3D3D3';
20+
operationName = 'not be modified'
21+
break;
22+
}
23+
24+
case ResourceOperation.CREATE: {
25+
backgroundColor = 'green'
26+
operationName = 'be created'
27+
break;
28+
}
29+
30+
case ResourceOperation.DESTROY: {
31+
backgroundColor = 'red'
32+
operationName = 'be destroyed'
33+
break;
34+
}
35+
36+
case ResourceOperation.MODIFY: {
37+
backgroundColor = 'yellow'
38+
operationName = 'be modified'
39+
break;
40+
}
41+
42+
case ResourceOperation.RECREATE: {
43+
backgroundColor = 'yellow'
44+
operationName = 'be re-created'
45+
break;
46+
}
47+
}
48+
49+
return <Box>
50+
<Text backgroundColor={backgroundColor}>
51+
<Text bold>{fullyQualifiedName}</Text>
52+
<Text> resource will {operationName}</Text>
53+
</Text>
54+
<Text> </Text>
55+
<ResourceOperationSymbol resourceOperation={operation}/>
56+
</Box>
57+
}

src/ui/reporters/default-reporter.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { PlanResponseData } from 'codify-schemas';
12
import { render } from 'ink';
23
import { EventEmitter } from 'node:events';
34
import React from 'react';
45

56
import { ctx, Event } from '../../events/context.js';
6-
import { PlanComponent } from '../components/plan-component.js';
7+
import { DefaultComponent } from '../components/default-component.js';
78
import { Reporter } from './reporter.js';
89

910
export enum ProcessStatus {
@@ -38,14 +39,19 @@ export class DefaultReporter implements Reporter {
3839
ctx.on(Event.SUB_PROCESS_START, (name, processName) => this.onSubprocessStartEvent(name, processName));
3940
ctx.on(Event.SUB_PROCESS_FINISH, (name, processName) => this.onSubprocessFinishEvent(name, processName))
4041

41-
render(<PlanComponent eventTarget={this.renderEmitter}/>)
42+
render(<DefaultComponent emitter={this.renderEmitter}/>)
4243

4344
}
4445

4546
async promptConfirmation(): Promise<boolean> {
4647
return true;
4748
}
4849

50+
displayPlan(plan: PlanResponseData[]): void {
51+
this.renderEmitter.emit('process', []);
52+
this.renderEmitter.emit('plan', plan);
53+
}
54+
4955
private onOutputEvent(...args: unknown[]) {
5056
this.staticOutput.push(...args)
5157
this.renderEmitter.emit('static_output', this.staticOutput);
@@ -58,6 +64,7 @@ export class DefaultReporter implements Reporter {
5864
subprocess: [],
5965
})
6066

67+
this.onOutputEvent(`${name} started`)
6168
this.renderEmitter.emit('process', this.processState);
6269
}
6370

@@ -70,6 +77,7 @@ export class DefaultReporter implements Reporter {
7077

7178
process.status = ProcessStatus.FINISHED;
7279

80+
this.onOutputEvent(`${name} finished successfully`)
7381
this.renderEmitter.emit('process', this.processState.process);
7482

7583
}
@@ -85,6 +93,7 @@ export class DefaultReporter implements Reporter {
8593
status: ProcessStatus.IN_PROGRESS,
8694
})
8795

96+
this.onOutputEvent(`${name} started`)
8897
this.renderEmitter.emit('process', this.processState);
8998
}
9099

@@ -102,7 +111,7 @@ export class DefaultReporter implements Reporter {
102111

103112
subprocess.status = ProcessStatus.FINISHED;
104113

105-
this.onOutputEvent(`${name} finished processing`)
114+
this.onOutputEvent(`${name} finished successfully`)
106115
this.renderEmitter.emit('process', this.processState);
107116
}
108117

src/ui/reporters/plain-reporter.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { PlanResponseData } from 'codify-schemas';
2+
13
import { ctx, Event } from '../../events/context.js';
24
import { Reporter } from './reporter.js';
35

@@ -15,4 +17,8 @@ export class PlainReporter implements Reporter {
1517
return true;
1618
}
1719

20+
displayPlan(plan: PlanResponseData[]): void {
21+
console.log(JSON.stringify(plan));
22+
}
23+
1824
}

src/ui/reporters/reporter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { PlanResponseData } from 'codify-schemas';
2+
13
export interface Reporter {
24
promptConfirmation(): Promise<boolean>
5+
6+
displayPlan(plan: PlanResponseData[]): void
37
}

0 commit comments

Comments
 (0)