Skip to content

Commit bc21f23

Browse files
committed
feat: Refactored the import orchestrator and default renderer to use the new form
1 parent e0727de commit bc21f23

File tree

8 files changed

+98
-73
lines changed

8 files changed

+98
-73
lines changed

package-lock.json

Lines changed: 4 additions & 4 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"codify": "./bin/run.js"
55
},
66
"dependencies": {
7-
"@codifycli/ink-form": "0.0.1",
7+
"@codifycli/ink-form": "0.0.3",
88
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
99
"@inkjs/ui": "^2",
1010
"@oclif/core": "^4.0.8",

src/entities/resource-info.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ interface ParameterInfo {
55
type?: string;
66
description?: string;
77
isRequired: boolean;
8+
value: unknown;
89
}
910

1011
export class ResourceInfo implements GetResourceInfoResponseData {
@@ -20,7 +21,7 @@ export class ResourceInfo implements GetResourceInfoResponseData {
2021

2122
static fromResponseData(data: GetResourceInfoResponseData): ResourceInfo {
2223
const resourceInfo = new ResourceInfo()
23-
Object.assign(data, resourceInfo);
24+
Object.assign(resourceInfo, data);
2425
return resourceInfo;
2526
}
2627

@@ -47,7 +48,7 @@ export class ResourceInfo implements GetResourceInfoResponseData {
4748
}
4849
})
4950
}
50-
51+
5152
getRequiredParameters(): ParameterInfo[] {
5253
return this.getParameterInfo()
5354
.filter((info) => info.isRequired);

src/orchestrators/import.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { ResourceJson } from 'codify-schemas';
2-
31
import { Project } from '../entities/project.js';
42
import { ResourceConfig } from '../entities/resource-config.js';
3+
import { ResourceInfo } from '../entities/resource-info.js';
54
import { ProcessName, SubProcessName, ctx } from '../events/context.js';
65
import { CodifyParser } from '../parser/index.js';
76
import { DependencyMap, PluginManager } from '../plugins/plugin-manager.js';
8-
import { Reporter } from '../ui/reporters/reporter.js';
7+
import { PromptType, Reporter } from '../ui/reporters/reporter.js';
98
import { InitializeOrchestrator } from './initialize.js';
109

1110
export type RequiredParameters = Map<string, RequiredParameter[]>;
@@ -52,11 +51,21 @@ export class ImportOrchestrator {
5251
reporter
5352
);
5453
await ImportOrchestrator.validate(typeIds, project, pluginManager, dependencyMap)
54+
const resourceInfoList = await pluginManager.getMultipleResourceInfo(typeIds);
5555

56-
const requiredParameters = await pluginManager.getRequiredParameters(typeIds);
57-
const userSuppliedParameters = await reporter.promptUserForParameterValues(requiredParameters);
56+
const [noPrompt, askPrompt] = resourceInfoList.reduce((result, info) => {
57+
info.getRequiredParameters().length === 0 ? result[0].push(info) : result[1].push(info);
58+
59+
return result;
60+
}, [<ResourceInfo[]>[], <ResourceInfo[]>[]])
5861

59-
const importResult = await ImportOrchestrator.getImportedConfigs(pluginManager, typeIds, userSuppliedParameters)
62+
const userSupplied = await reporter.promptUserForValues(askPrompt, PromptType.IMPORT);
63+
64+
const valuesToImport = [
65+
...noPrompt.map((info) => new ResourceConfig({ type: info.type })),
66+
...userSupplied
67+
]
68+
const importResult = await ImportOrchestrator.getImportedConfigs(pluginManager, typeIds, valuesToImport)
6069

6170
ctx.processFinished(ProcessName.IMPORT)
6271
reporter.displayImportResult(importResult);
@@ -65,34 +74,29 @@ export class ImportOrchestrator {
6574
static async getImportedConfigs(
6675
pluginManager: PluginManager,
6776
typeIds: string[],
68-
userSuppliedParameters: UserSuppliedParameters
77+
resources: ResourceConfig[],
6978
): Promise<ImportResult> {
7079
const importedConfigs = [];
7180
const errors = [];
7281

73-
for (const type of typeIds) {
74-
ctx.subprocessStarted(SubProcessName.IMPORT_RESOURCE, type);
82+
for (const resource of resources) {
83+
ctx.subprocessStarted(SubProcessName.IMPORT_RESOURCE, resource.type);
7584
try {
76-
const config: ResourceJson = {
77-
core: { type },
78-
parameters: userSuppliedParameters.get(type) ?? {},
79-
};
80-
81-
const response = await pluginManager.importResource(config);
85+
const response = await pluginManager.importResource(resource.toJson());
8286

8387
if (response.result !== null && response.result.length > 0) {
8488
importedConfigs.push(...response
8589
?.result
8690
?.map((r) => ResourceConfig.fromJson(r)) ?? []
8791
);
8892
} else {
89-
errors.push(`Unable to import resource '${type}', resource not found`);
93+
errors.push(`Unable to import resource '${resource.type}', resource not found`);
9094
}
9195
} catch (error: any) {
9296
errors.push(error.message ?? error);
9397
}
9498

95-
ctx.subprocessFinished(SubProcessName.IMPORT_RESOURCE, type);
99+
ctx.subprocessFinished(SubProcessName.IMPORT_RESOURCE, resource.type);
96100
}
97101

98102
return {

src/plugins/plugin-manager.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { InternalError } from '../common/errors.js';
99
import { Plan, ResourcePlan } from '../entities/plan.js';
1010
import { Project } from '../entities/project.js';
11+
import { ResourceInfo } from '../entities/resource-info.js';
1112
import { SubProcessName, ctx } from '../events/context.js';
1213
import { RequiredParameter, RequiredParameters } from '../orchestrators/import.js';
1314
import { groupBy } from '../utils/index.js';
@@ -67,6 +68,26 @@ export class PluginManager {
6768
return plugin.getResourceInfo(type);
6869
}
6970

71+
async getMultipleResourceInfo(typeIds: string[]): Promise<ResourceInfo[]> {
72+
return Promise.all(typeIds.map((type) => this.getResourceInfoV2(type)))
73+
}
74+
75+
async getResourceInfoV2(type: string): Promise<ResourceInfo> {
76+
const pluginName = this.resourceToPluginMapping.get(type);
77+
if (!pluginName) {
78+
throw new Error(`Unable to find plugin for resource: ${type}`);
79+
}
80+
81+
const plugin = this.plugins.get(pluginName)
82+
if (!plugin) {
83+
throw new Error(`Unable to find plugin for resource ${type}`);
84+
}
85+
86+
const result = await plugin.getResourceInfo(type);
87+
return ResourceInfo.fromResponseData(result);
88+
}
89+
90+
7091
async importResource(config: ResourceJson): Promise<ImportResponseData> {
7192
const pluginName = this.resourceToPluginMapping.get(config.core.type);
7293
if (!pluginName) {

src/ui/components/default-component.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Form, FormProps } from '@codifycli/ink-form';
12
import { PasswordInput, Select } from '@inkjs/ui';
23
import chalk from 'chalk';
34
import { Box, Static, Text } from 'ink';
@@ -6,11 +7,10 @@ import { EventEmitter } from 'node:events';
67
import React, { useLayoutEffect, useState } from 'react';
78

89
import { Plan } from '../../entities/plan.js';
9-
import { ImportResult, RequiredParameters } from '../../orchestrators/import.js';
10+
import { ImportResult } from '../../orchestrators/import.js';
1011
import { RenderEvent } from '../reporters/reporter.js';
1112
import { RenderStatus, store } from '../store/index.js';
1213
import { ImportResultComponent } from './import/import-result.js';
13-
import { ImportParametersForm } from './import/index.js';
1414
import { PlanComponent } from './plan/plan.js';
1515
import { ProgressDisplay } from './progress/progress-display.js';
1616

@@ -79,9 +79,9 @@ export function DefaultComponent(props: {
7979
}
8080
{
8181
renderStatus === RenderStatus.IMPORT_PROMPT && (
82-
<ImportParametersForm onSubmit={(result) => {
82+
<Form onSubmit={(result) => {
8383
emitter.emit(RenderEvent.PROMPT_IMPORT_PARAMETERS_RESULT, result)
84-
}} requiredParameters={renderData as RequiredParameters}/>
84+
}} { ...renderData as FormProps}/>
8585
)
8686
}
8787
{

src/ui/reporters/default-reporter.tsx

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1+
import { FormReturnValue } from '@codifycli/ink-form';
12
import chalk from 'chalk';
23
import { SudoRequestData, SudoRequestResponseData } from 'codify-schemas';
34
import { render } from 'ink';
45
import { EventEmitter } from 'node:events';
56
import React from 'react';
67

78
import { Plan } from '../../entities/plan.js';
9+
import { ResourceConfig } from '../../entities/resource-config.js';
10+
import { ResourceInfo } from '../../entities/resource-info.js';
811
import { Event, ProcessName, SubProcessName, ctx } from '../../events/context.js';
9-
import { ImportResult, RequiredParameters, UserSuppliedParameters } from '../../orchestrators/import.js';
12+
import { ImportResult } from '../../orchestrators/import.js';
1013
import { sleep } from '../../utils/index.js';
1114
import { SudoUtils } from '../../utils/sudo.js';
1215
import { DefaultComponent } from '../components/default-component.js';
1316
import { ProgressState, ProgressStatus } from '../components/progress/progress-display.js';
1417
import { RenderStatus, store } from '../store/index.js';
15-
import { RenderEvent, Reporter } from './reporter.js';
18+
import { PromptType, RenderEvent, Reporter } from './reporter.js';
1619

1720
const ProgressLabelMapping = {
1821
[ProcessName.APPLY]: 'Codify apply',
@@ -44,33 +47,56 @@ export class DefaultReporter implements Reporter {
4447
ctx.on(Event.SUB_PROCESS_FINISH, (name, additionalName) => this.onSubprocessFinishEvent(name, additionalName))
4548
}
4649

47-
async promptUserForParameterValues(requiredParameters: RequiredParameters): Promise<UserSuppliedParameters> {
48-
if (requiredParameters.size === 0) {
49-
return new Map();
50+
async promptUserForValues(resources: Array<ResourceInfo>, promptType: PromptType): Promise<ResourceConfig[]> {
51+
if (resources.length === 0) {
52+
return [];
5053
}
5154

5255
fullscreen()
5356
process.on('beforeExit', exitFullScreen);
5457

55-
const userInput = await this.updateStateAndAwaitEvent<object>(() => {
56-
this.updateRenderState(RenderStatus.IMPORT_PROMPT, requiredParameters);
57-
}, RenderEvent.PROMPT_IMPORT_PARAMETERS_RESULT);
58+
const formProps = {
59+
form: {
60+
title: 'codify import',
61+
description: 'specify the resource to import',
62+
sections: resources.map((info) => ({
63+
title: info.type,
64+
description: info.description,
65+
fields: info.getRequiredParameters().map((parameter) => ({
66+
type: parameter.type,
67+
name: parameter.name,
68+
label: parameter.name,
69+
description: parameter.description,
70+
required: true,
71+
})),
72+
})),
73+
},
74+
}
75+
76+
const userInput = await this.updateStateAndAwaitEvent<FormReturnValue>(() =>
77+
this.updateRenderState(RenderStatus.IMPORT_PROMPT, formProps),
78+
RenderEvent.PROMPT_IMPORT_PARAMETERS_RESULT
79+
);
5880

5981

6082
exitFullScreen()
6183
process.off('beforeExit', exitFullScreen);
84+
6285
this.updateRenderState(RenderStatus.PROGRESS);
6386

64-
return this.extractUserSuppliedParametersFromResult(userInput);
87+
return userInput.map((v) => ResourceConfig.fromJson({
88+
core: { type: v.section.title },
89+
parameters: v.value,
90+
}))
6591

6692
function fullscreen() {
67-
process.stdout.write('\x1b[?1049h');
68-
process.stdout.write('\x1b[?1000h');
93+
process.stdout.write('\u001B[?1049h');
94+
process.stdout.write('\u001B[?1000h');
6995
}
7096

7197
function exitFullScreen() {
72-
process.stdout.write('\x1b[?1049l');
73-
process.stdout.write('\x1b[?1000l');
98+
process.stdout.write('\u001B[?1049l');
99+
process.stdout.write('\u001B[?1000l');
74100
}
75101
}
76102

@@ -201,27 +227,6 @@ export class DefaultReporter implements Reporter {
201227
throw new Error('sudo: 3 incorrect password attempts')
202228
}
203229

204-
private extractUserSuppliedParametersFromResult(result: object): Map<string, Record<string, unknown>> {
205-
const resources = Object.entries(result)
206-
.map(([key, value]) => {
207-
const [resourceName, parameterName] = key.split('.');
208-
return [resourceName, parameterName, value] as const;
209-
})
210-
.reduce((result, parameter) => {
211-
const [resourceName, parameterName, value] = parameter
212-
213-
if (!result[resourceName]) {
214-
result[resourceName] = {}
215-
}
216-
217-
result[resourceName][parameterName] = value
218-
219-
return result;
220-
}, {} as Record<string, Record<string, unknown>>)
221-
222-
return new Map(Object.entries(resources));
223-
}
224-
225230
private updateRenderState(status: RenderStatus | null, data?: unknown): void {
226231
store.set(store.renderState, { status, data });
227232
}

src/ui/reporters/reporter.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DebugReporter } from './debug-reporter.js';
66
import { DefaultReporter } from './default-reporter.js';
77
import { PlainReporter } from './plain-reporter.js';
88
import { ResourceInfo } from '../../entities/resource-info.js';
9+
import { ResourceConfig } from '../../entities/resource-config.js';
910

1011
export enum RenderEvent {
1112
LOG = 'log',
@@ -39,13 +40,6 @@ export enum PromptType {
3940
CREATE,
4041
}
4142

42-
export interface PromptParameterValueRequest {
43-
typeIds?: Array<string>;
44-
resourceInfoList: Array<ResourceInfo>;
45-
46-
promptType: PromptType;
47-
}
48-
4943
export interface Reporter {
5044
displayApplyComplete(message: string[]): Promise<void> | void;
5145

@@ -55,7 +49,7 @@ export interface Reporter {
5549

5650
promptSudo(pluginName: string, data: SudoRequestData, secureMode: boolean): Promise<SudoRequestResponseData>;
5751

58-
promptUserForValues(request: PromptParameterValueRequest): Promise<UserSuppliedParameters>;
52+
promptUserForValues(resources: Array<ResourceInfo>, promptType: PromptType): Promise<ResourceConfig[]>;
5953

6054
displayImportResult(importResult: ImportResult): void;
6155
}
@@ -74,9 +68,9 @@ export const ReporterFactory = {
7468
return new DefaultReporter();
7569
}
7670

77-
case ReporterType.PLAIN: {
78-
return new PlainReporter();
79-
}
71+
// case ReporterType.PLAIN: {
72+
// return new PlainReporter();
73+
// }
8074

8175
case ReporterType.JSON: {
8276
return new DefaultReporter();

0 commit comments

Comments
 (0)