Skip to content

Commit cbe9334

Browse files
committed
Added --updateExisting flag for import command. Made refresh orchestrator empty and reliant on the import orchestrator. Refactored save file type to an individual function
1 parent 92bf7b3 commit cbe9334

File tree

5 files changed

+149
-279
lines changed

5 files changed

+149
-279
lines changed

src/commands/import.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import { Flags } from '@oclif/core';
12
import chalk from 'chalk';
23
import fs from 'node:fs/promises';
3-
import path from 'node:path';
44

55
import { BaseCommand } from '../common/base-command.js';
66
import { ImportOrchestrator } from '../orchestrators/import.js';
@@ -45,6 +45,12 @@ For more information, visit: https://docs.codifycli.com/commands/import`
4545
'<%= config.bin %> <%= command.id %> \\*'
4646
]
4747

48+
static override flags = {
49+
'updateExisting': Flags.boolean({
50+
description: 'Force the CLI to try to update an existing file instead of prompting the user with the option of creating a new file',
51+
}),
52+
}
53+
4854
public async run(): Promise<void> {
4955
const { raw, flags } = await this.parse(Import)
5056

@@ -65,6 +71,7 @@ For more information, visit: https://docs.codifycli.com/commands/import`
6571
typeIds: cleanedArgs,
6672
path: resolvedPath,
6773
secureMode: flags.secure,
74+
updateExisting: flags.updateExisting,
6875
}, this.reporter)
6976

7077
process.exit(0)

src/connect/http-routes/handlers/import-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export function importHandler() {
5555
}
5656
}
5757

58-
return spawn('zsh', ['-c', `${ConnectOrchestrator.nodeBinary} ${ConnectOrchestrator.rootCommand} import ${args} -p ${filePath}`], {
58+
return spawn('zsh', ['-c', `${ConnectOrchestrator.nodeBinary} ${ConnectOrchestrator.rootCommand} import ${args} -p ${filePath} --updateExisting`], {
5959
name: 'xterm-color',
6060
cols: 80,
6161
rows: 30,

src/entities/project.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ ${JSON.stringify(projectConfigs, null, 2)}`);
5757
return this.resourceConfigs.length === 0;
5858
}
5959

60+
exists(): boolean {
61+
return this.codifyFiles.length > 0;
62+
}
63+
6064
isStateful(): boolean {
6165
return this.stateConfigs !== null && this.stateConfigs !== undefined && this.stateConfigs.length > 0;
6266
}

src/orchestrators/import.ts

Lines changed: 131 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ export type ImportResult = { result: ResourceConfig[], errors: string[] }
2121
export interface ImportArgs {
2222
typeIds?: string[];
2323
path: string;
24+
updateExisting?: boolean;
2425
secureMode?: boolean;
2526
verbosityLevel?: number;
2627
}
2728

29+
enum SaveType {
30+
EXISTING,
31+
NEW,
32+
NONE
33+
}
34+
2835
export class ImportOrchestrator {
2936
static async run(
3037
args: ImportArgs,
@@ -39,11 +46,11 @@ export class ImportOrchestrator {
3946
);
4047

4148
await (!typeIds || typeIds.length === 0
42-
? ImportOrchestrator.autoImportAll(reporter, initializationResult)
43-
: ImportOrchestrator.runNewImport(typeIds, reporter, initializationResult));
49+
? ImportOrchestrator.autoImportAll(reporter, initializationResult, args)
50+
: ImportOrchestrator.runNewImport(typeIds, reporter, initializationResult, args));
4451
}
4552

46-
static async autoImportAll(reporter: Reporter, initializeResult: InitializationResult) {
53+
static async autoImportAll(reporter: Reporter, initializeResult: InitializationResult, args: ImportArgs) {
4754
const { project, pluginManager, typeIdsToDependenciesMap } = initializeResult;
4855

4956
ctx.subprocessStarted(SubProcessName.IMPORT_RESOURCE)
@@ -74,18 +81,18 @@ export class ImportOrchestrator {
7481
[...project.resourceConfigs, ...importedResources].map((r) => r.type),
7582
);
7683

77-
await ImportOrchestrator.updateExistingFiles(
84+
await ImportOrchestrator.saveResults(
7885
reporter,
79-
project,
8086
{ result: importedResources, errors: [] },
87+
project,
8188
resourceInfoList,
82-
project.codifyFiles[0],
8389
pluginManager,
84-
);
90+
args
91+
)
8592
}
8693

8794
/** Import new resources. Type ids supplied. This will ask for any required parameters */
88-
static async runNewImport(typeIds: string[], reporter: Reporter, initializeResult: InitializationResult): Promise<void> {
95+
static async runNewImport(typeIds: string[], reporter: Reporter, initializeResult: InitializationResult, args: ImportArgs): Promise<void> {
8996
const { project, pluginManager, typeIdsToDependenciesMap } = initializeResult;
9097

9198
const matchedTypes = this.matchTypeIds(typeIds, [...typeIdsToDependenciesMap.keys()])
@@ -104,7 +111,7 @@ export class ImportOrchestrator {
104111
resourceInfoList.push(...(await pluginManager.getMultipleResourceInfo(
105112
project.resourceConfigs.map((r) => r.type)
106113
)));
107-
await ImportOrchestrator.saveResults(reporter, importResult, project, resourceInfoList, pluginManager)
114+
await ImportOrchestrator.saveResults(reporter, importResult, project, resourceInfoList, pluginManager, args)
108115
}
109116

110117
static async import(
@@ -146,98 +153,20 @@ export class ImportOrchestrator {
146153
}
147154
}
148155

149-
private static matchTypeIds(typeIds: string[], validTypeIds: string[]): string[] {
150-
const result: string[] = [];
151-
const unsupportedTypeIds: string[] = [];
152-
153-
for (const typeId of typeIds) {
154-
if (!typeId.includes('*') && !typeId.includes('?')) {
155-
const matched = validTypeIds.includes(typeId);
156-
if (!matched) {
157-
unsupportedTypeIds.push(typeId);
158-
continue;
159-
}
160-
161-
result.push(typeId)
162-
continue;
163-
}
164-
165-
const matched = validTypeIds.filter((valid) => wildCardMatch(valid, typeId))
166-
if (matched.length === 0) {
167-
unsupportedTypeIds.push(typeId);
168-
continue;
169-
}
170-
171-
result.push(...matched);
172-
}
173-
174-
if (unsupportedTypeIds.length > 0) {
175-
throw new Error(`The following resources cannot be imported. No plugins found that support the following types:
176-
${JSON.stringify(unsupportedTypeIds)}`);
177-
}
178-
179-
return result;
180-
}
181-
182-
private static async validate(typeIds: string[], project: Project, pluginManager: PluginManager, dependencyMap: DependencyMap): Promise<void> {
183-
project.validateTypeIds(dependencyMap);
184-
185-
const unsupportedTypeIds = typeIds.filter((type) => !dependencyMap.has(type));
186-
if (unsupportedTypeIds.length > 0) {
187-
throw new Error(`The following resources cannot be imported. No plugins found that support the following types:
188-
${JSON.stringify(unsupportedTypeIds)}`);
189-
}
190-
}
191-
192-
private static async getImportParameters(reporter: Reporter, project: Project, resourceInfoList: ResourceInfo[]): Promise<Array<ResourceConfig>> {
193-
// Figure out which resources we need to prompt the user for additional info (based on the resource info)
194-
const [noPrompt, askPrompt] = resourceInfoList.reduce((result, info) => {
195-
info.getRequiredParameters().length === 0 ? result[0].push(info) : result[1].push(info);
196-
return result;
197-
}, [<ResourceInfo[]>[], <ResourceInfo[]>[]])
198-
199-
askPrompt.forEach((info) => {
200-
const matchedResources = project.findAll(info.type);
201-
if (matchedResources.length > 0) {
202-
info.attachDefaultValues(matchedResources[0]);
203-
}
204-
})
205-
206-
if (askPrompt.length > 0) {
207-
await reporter.displayImportWarning(askPrompt.map((r) => r.type), noPrompt.map((r) => r.type));
208-
}
209-
210-
const userSupplied = await reporter.promptUserForValues(askPrompt, PromptType.IMPORT);
211-
212-
return [
213-
...noPrompt.map((info) => new ResourceConfig({ type: info.type })),
214-
...userSupplied
215-
]
216-
}
217-
218-
private static async saveResults(
156+
static async saveResults(
219157
reporter: Reporter,
220158
importResult: ImportResult,
221159
project: Project,
222160
resourceInfoList: ResourceInfo[],
223161
pluginManager: PluginManager,
162+
args: ImportArgs,
224163
): Promise<void> {
225-
const projectExists = !project.isEmpty();
226164
const multipleCodifyFiles = project.codifyFiles.length > 1;
227165

228-
const promptResult = await reporter.promptOptions(
229-
'\nDo you want to save the results?',
230-
[
231-
projectExists ?
232-
multipleCodifyFiles ? 'Update existing files' : `Update existing file (${project.codifyFiles})`
233-
: undefined,
234-
'In a new file',
235-
'No'
236-
].filter(Boolean) as string[]
237-
)
166+
const saveType = await ImportOrchestrator.getSaveType(reporter, project, args);
238167

239168
// Update an existing file
240-
if (projectExists && promptResult === 0) {
169+
if (saveType === SaveType.EXISTING) {
241170
const file = multipleCodifyFiles
242171
? project.codifyFiles[await reporter.promptOptions('\nIf new resources are added, where to write them?', project.codifyFiles)]
243172
: project.codifyFiles[0];
@@ -246,7 +175,7 @@ ${JSON.stringify(unsupportedTypeIds)}`);
246175
}
247176

248177
// Write to a new file
249-
if ((!projectExists && promptResult === 0) || (projectExists && promptResult === 1)) {
178+
if (saveType === SaveType.NEW) {
250179
const newFileName = await ImportOrchestrator.generateNewImportFileName();
251180
await ImportOrchestrator.saveNewFile(reporter, newFileName, importResult);
252181
return;
@@ -259,7 +188,7 @@ ${JSON.stringify(unsupportedTypeIds)}`);
259188
await sleep(100);
260189
}
261190

262-
private static async updateExistingFiles(
191+
static async updateExistingFiles(
263192
reporter: Reporter,
264193
existingProject: Project,
265194
importResult: ImportResult,
@@ -330,6 +259,114 @@ ${JSON.stringify(unsupportedTypeIds)}`);
330259
await sleep(100);
331260
}
332261

262+
private static matchTypeIds(typeIds: string[], validTypeIds: string[]): string[] {
263+
const result: string[] = [];
264+
const unsupportedTypeIds: string[] = [];
265+
266+
for (const typeId of typeIds) {
267+
if (!typeId.includes('*') && !typeId.includes('?')) {
268+
const matched = validTypeIds.includes(typeId);
269+
if (!matched) {
270+
unsupportedTypeIds.push(typeId);
271+
continue;
272+
}
273+
274+
result.push(typeId)
275+
continue;
276+
}
277+
278+
const matched = validTypeIds.filter((valid) => wildCardMatch(valid, typeId))
279+
if (matched.length === 0) {
280+
unsupportedTypeIds.push(typeId);
281+
continue;
282+
}
283+
284+
result.push(...matched);
285+
}
286+
287+
if (unsupportedTypeIds.length > 0) {
288+
throw new Error(`The following resources cannot be imported. No plugins found that support the following types:
289+
${JSON.stringify(unsupportedTypeIds)}`);
290+
}
291+
292+
return result;
293+
}
294+
295+
private static async validate(typeIds: string[], project: Project, pluginManager: PluginManager, dependencyMap: DependencyMap): Promise<void> {
296+
project.validateTypeIds(dependencyMap);
297+
298+
const unsupportedTypeIds = typeIds.filter((type) => !dependencyMap.has(type));
299+
if (unsupportedTypeIds.length > 0) {
300+
throw new Error(`The following resources cannot be imported. No plugins found that support the following types:
301+
${JSON.stringify(unsupportedTypeIds)}`);
302+
}
303+
}
304+
305+
private static async getImportParameters(reporter: Reporter, project: Project, resourceInfoList: ResourceInfo[]): Promise<Array<ResourceConfig>> {
306+
// Figure out which resources we need to prompt the user for additional info (based on the resource info)
307+
const [noPrompt, askPrompt] = resourceInfoList.reduce((result, info) => {
308+
info.getRequiredParameters().length === 0 ? result[0].push(info) : result[1].push(info);
309+
return result;
310+
}, [<ResourceInfo[]>[], <ResourceInfo[]>[]])
311+
312+
askPrompt.forEach((info) => {
313+
const matchedResources = project.findAll(info.type);
314+
if (matchedResources.length > 0) {
315+
info.attachDefaultValues(matchedResources[0]);
316+
}
317+
})
318+
319+
if (askPrompt.length > 0) {
320+
await reporter.displayImportWarning(askPrompt.map((r) => r.type), noPrompt.map((r) => r.type));
321+
}
322+
323+
const userSupplied = await reporter.promptUserForValues(askPrompt, PromptType.IMPORT);
324+
325+
return [
326+
...noPrompt.map((info) => new ResourceConfig({ type: info.type })),
327+
...userSupplied
328+
]
329+
}
330+
331+
private static async getSaveType(
332+
reporter: Reporter,
333+
project: Project,
334+
args: ImportArgs,
335+
): Promise<SaveType> {
336+
const projectExists = project.exists();
337+
const multipleCodifyFiles = project.codifyFiles.length > 1;
338+
339+
if (args.updateExisting && projectExists) {
340+
return SaveType.EXISTING;
341+
}
342+
343+
const promptResult = await reporter.promptOptions(
344+
'\nDo you want to save the results?',
345+
[
346+
projectExists ?
347+
multipleCodifyFiles ? 'Update existing files' : `Update existing file (${project.codifyFiles})`
348+
: undefined,
349+
'In a new file',
350+
'No'
351+
].filter(Boolean) as string[]
352+
);
353+
354+
if (projectExists) {
355+
switch (promptResult) {
356+
case 0: { return SaveType.EXISTING; }
357+
case 1: { return SaveType.NEW; }
358+
case 2: { return SaveType.NONE; }
359+
}
360+
} else {
361+
switch (promptResult) {
362+
case 0: { return SaveType.NEW; }
363+
case 1: { return SaveType.NONE; }
364+
}
365+
}
366+
367+
throw new Error('Unexpected response from prompt');
368+
}
369+
333370
private static async saveNewFile(reporter: Reporter, filePath: string, importResult: ImportResult): Promise<void> {
334371
const newFile = JSON.stringify(importResult.result.map((r) => r.raw), null, 2);
335372
const diff = prettyFormatFileDiff('', newFile);
@@ -363,7 +400,7 @@ ${JSON.stringify(unsupportedTypeIds)}`);
363400
let fileName = path.join(folderPath, 'import.codify.jsonc')
364401
let counter = 1;
365402

366-
while(true) {
403+
while (true) {
367404
if (!(await FileUtils.fileExists(fileName))) {
368405
return fileName;
369406
}

0 commit comments

Comments
 (0)