Skip to content

Commit ac9e4bc

Browse files
committed
feat: Refactored the initialization process to return all info about a plugin. Simplifies the design of the CLI a bit especially for import
1 parent 5fb7d70 commit ac9e4bc

File tree

14 files changed

+88
-98
lines changed

14 files changed

+88
-98
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
@@ -17,7 +17,7 @@
1717
"ajv": "^8.12.0",
1818
"ajv-formats": "^3.0.1",
1919
"chalk": "^5.3.0",
20-
"codify-schemas": "^1.0.77",
20+
"codify-schemas": "^1.0.80",
2121
"cors": "^2.8.5",
2222
"debug": "^4.3.4",
2323
"detect-indent": "^7.0.1",

src/commands/import.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ For more information, visit: https://docs.codifycli.com/commands/import`
7070
verbosityLevel: flags.debug ? 3 : 0,
7171
typeIds: cleanedArgs,
7272
path: resolvedPath,
73-
secureMode: flags.secure,
7473
updateExisting: flags.updateExisting,
7574
}, this.reporter)
7675

src/common/initialize-plugins.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import * as fs from 'node:fs/promises'
22
import * as os from 'node:os'
33
import * as path from 'node:path'
4+
import { validate } from 'uuid';
45

6+
import { DashboardApiClient } from '../api/dashboard/index.js';
7+
import { LoginHelper } from '../connect/login-helper.js';
58
import { Project } from '../entities/project.js';
69
import { SubProcessName, ctx } from '../events/context.js';
710
import { CODIFY_FILE_REGEX, CodifyParser } from '../parser/index.js';
8-
import { DependencyMap, PluginManager } from '../plugins/plugin-manager.js';
11+
import { PluginManager, ResourceInfoMap } from '../plugins/plugin-manager.js';
912
import { Reporter } from '../ui/reporters/reporter.js';
10-
import { LoginHelper } from '../connect/login-helper.js';
1113
import { FileUtils } from '../utils/file.js';
12-
import { validate } from 'uuid';
13-
import { DashboardApiClient } from '../api/dashboard/index.js';
1414

1515
export interface InitializeArgs {
1616
path?: string;
@@ -21,7 +21,7 @@ export interface InitializeArgs {
2121
}
2222

2323
export interface InitializationResult {
24-
typeIdsToDependenciesMap: DependencyMap
24+
resourceInfoMap: ResourceInfoMap
2525
pluginManager: PluginManager,
2626
project: Project,
2727
}
@@ -42,10 +42,10 @@ export class PluginInitOrchestrator {
4242

4343
ctx.subprocessStarted(SubProcessName.INITIALIZE_PLUGINS)
4444
const pluginManager = new PluginManager();
45-
const typeIdsToDependenciesMap = await pluginManager.initialize(project, args.secure, args.verbosityLevel);
45+
const resourceInfoMap = await pluginManager.initialize(project, args.secure, args.verbosityLevel);
4646
ctx.subprocessFinished(SubProcessName.INITIALIZE_PLUGINS)
4747

48-
return { typeIdsToDependenciesMap, pluginManager, project };
48+
return { resourceInfoMap, pluginManager, project };
4949
}
5050

5151
private static async parse(

src/entities/project.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { validate } from 'uuid'
44
import { PluginValidationError, PluginValidationErrorParams, TypeNotFoundError } from '../common/errors.js';
55
import { ctx } from '../events/context.js';
66
import { SourceMapCache } from '../parser/source-maps.js';
7-
import { DependencyMap } from '../plugins/plugin-manager.js';
7+
import { ResourceInfoMap } from '../plugins/plugin-manager.js';
88
import { DependencyGraphResolver } from '../utils/dependency-graph-resolver.js';
99
import { groupBy } from '../utils/index.js';
1010
import { ConfigBlock, ConfigType } from './config.js';
@@ -155,16 +155,16 @@ ${JSON.stringify(projectConfigs, null, 2)}`);
155155
}
156156
}
157157

158-
validateTypeIds(resourceMap: Map<string, string[]>) {
159-
const invalidConfigs = this.resourceConfigs.filter((c) => !resourceMap.get(c.type));
158+
validateTypeIds(resourceInfoMap: ResourceInfoMap) {
159+
const invalidConfigs = this.resourceConfigs.filter((c) => !resourceInfoMap.has(c.type));
160160

161161
if (invalidConfigs.length > 0) {
162162
throw new TypeNotFoundError(invalidConfigs, this.sourceMaps);
163163
}
164164
}
165165

166-
resolveDependenciesAndCalculateEvalOrder(dependencyMap?: DependencyMap) {
167-
this.resolveResourceDependencies(dependencyMap);
166+
resolveDependenciesAndCalculateEvalOrder(resourceInfoMap?: ResourceInfoMap) {
167+
this.resolveResourceDependencies(resourceInfoMap);
168168
this.calculateEvaluationOrder();
169169
}
170170

@@ -189,7 +189,7 @@ ${JSON.stringify(projectConfigs, null, 2)}`);
189189
) ?? null;
190190
}
191191

192-
private resolveResourceDependencies(dependencyMap?: DependencyMap) {
192+
private resolveResourceDependencies(resourceInfoMap?: ResourceInfoMap) {
193193
const resourceMap = new Map(this.resourceConfigs.map((r) => [r.id, r] as const));
194194

195195
for (const r of this.resourceConfigs) {
@@ -198,7 +198,7 @@ ${JSON.stringify(projectConfigs, null, 2)}`);
198198
r.addDependenciesBasedOnParameters((id) => resourceMap.has(id));
199199

200200
// Plugin dependencies are soft dependencies. They only activate if the dependent resource is present.
201-
r.addDependencies(dependencyMap?.get(r.type)
201+
r.addDependencies(resourceInfoMap?.get(r.type)?.dependencies
202202
?.filter((type) => [...resourceMap.values()].some((r) => r.type === type))
203203
?.flatMap((type) => [...resourceMap.values()].filter((r) => r.type === type).map((r) => r.id)) ?? []
204204
);

src/orchestrators/destroy.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Project } from '../entities/project.js';
44
import { ResourceConfig } from '../entities/resource-config.js';
55
import { ResourceInfo } from '../entities/resource-info.js';
66
import { ProcessName, SubProcessName, ctx } from '../events/context.js';
7-
import { DependencyMap, PluginManager } from '../plugins/plugin-manager.js';
7+
import { PluginManager, ResourceInfoMap } from '../plugins/plugin-manager.js';
88
import { PromptType, Reporter } from '../ui/reporters/reporter.js';
99
import { wildCardMatch } from '../utils/wild-card-match.js';
1010

@@ -68,13 +68,15 @@ Open a new terminal or source '.zshrc' for the new changes to be reflected`);
6868
reporter: Reporter,
6969
initializeResult: InitializationResult
7070
): Promise<{ plan: Plan, destroyProject: Project }> {
71-
const { project, pluginManager, typeIdsToDependenciesMap } = initializeResult;
71+
const { project, pluginManager, resourceInfoMap } = initializeResult;
7272

7373
// TODO: In the future if a user supplies resourceId.name (naming a specific resource) destroy that resource instead of stripping the name out.
74-
const matchedTypes = this.matchTypeIds(typeIds.map((id) => id.split('.').at(0) ?? ''), [...typeIdsToDependenciesMap.keys()])
75-
await DestroyOrchestrator.validateTypeIds(matchedTypes, project, pluginManager, typeIdsToDependenciesMap);
74+
const matchedTypes = this.matchTypeIds(typeIds.map((id) => id.split('.').at(0) ?? ''), [...resourceInfoMap.keys()])
75+
await DestroyOrchestrator.validateTypeIds(matchedTypes, project, resourceInfoMap);
76+
77+
const resourceInfoList = [...resourceInfoMap.values()]
78+
.filter((info) => matchedTypes.includes(info.type))
7679

77-
const resourceInfoList = (await pluginManager.getMultipleResourceInfo(matchedTypes));
7880
const resourcesToDestroy = await DestroyOrchestrator.getDestroyParameters(reporter, project, resourceInfoList);
7981

8082
const destroyProject = new Project(
@@ -83,7 +85,7 @@ Open a new terminal or source '.zshrc' for the new changes to be reflected`);
8385
project.codifyFiles
8486
).toDestroyProject();
8587

86-
destroyProject.resolveDependenciesAndCalculateEvalOrder(typeIdsToDependenciesMap);
88+
destroyProject.resolveDependenciesAndCalculateEvalOrder(resourceInfoMap);
8789
const plan = await ctx.subprocess(ProcessName.PLAN, () =>
8890
pluginManager.plan(destroyProject)
8991
)
@@ -96,16 +98,16 @@ Open a new terminal or source '.zshrc' for the new changes to be reflected`);
9698
reporter: Reporter,
9799
initializeResult: InitializationResult
98100
): Promise<{ plan: Plan, destroyProject: Project }> {
99-
const { pluginManager, project, typeIdsToDependenciesMap } = initializeResult;
101+
const { pluginManager, project, resourceInfoMap } = initializeResult;
100102

101103
await ctx.subprocess(SubProcessName.VALIDATE, async () => {
102-
project.validateTypeIds(typeIdsToDependenciesMap);
104+
project.validateTypeIds(resourceInfoMap);
103105
const validationResults = await pluginManager.validate(project);
104106
project.handlePluginResourceValidationResults(validationResults);
105107
})
106108

107109
const destroyProject = project.toDestroyProject();
108-
destroyProject.resolveDependenciesAndCalculateEvalOrder(typeIdsToDependenciesMap);
110+
destroyProject.resolveDependenciesAndCalculateEvalOrder(resourceInfoMap);
109111

110112
const plan = await ctx.subprocess(ProcessName.PLAN, () =>
111113
pluginManager.plan(destroyProject)
@@ -147,10 +149,10 @@ ${JSON.stringify(unsupportedTypeIds)}`);
147149
return result;
148150
}
149151

150-
private static async validateTypeIds(typeIds: string[], project: Project, pluginManager: PluginManager, dependencyMap: DependencyMap): Promise<void> {
151-
project.validateTypeIds(dependencyMap);
152+
private static async validateTypeIds(typeIds: string[], project: Project, resourceInfoMap: ResourceInfoMap): Promise<void> {
153+
project.validateTypeIds(resourceInfoMap);
152154

153-
const unsupportedTypeIds = typeIds.filter((type) => !dependencyMap.has(type));
155+
const unsupportedTypeIds = typeIds.filter((type) => !resourceInfoMap.has(type));
154156
if (unsupportedTypeIds.length > 0) {
155157
throw new Error(`The following resources cannot be destroyed. No plugins found that support the following types:
156158
${JSON.stringify(unsupportedTypeIds)}`);
@@ -160,6 +162,7 @@ ${JSON.stringify(unsupportedTypeIds)}`);
160162
private static async getDestroyParameters(reporter: Reporter, project: Project, resourceInfoList: ResourceInfo[]): Promise<Array<ResourceConfig>> {
161163
// Figure out which resources we need to prompt the user for additional info (based on the resource info)
162164
const [noPrompt, askPrompt] = resourceInfoList.reduce((result, info) => {
165+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
163166
info.getRequiredParameters().length === 0 ? result[0].push(info) : result[1].push(info);
164167
return result;
165168
}, [<ResourceInfo[]>[], <ResourceInfo[]>[]])

src/orchestrators/import.ts

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { FileModificationCalculator } from '../generators/file-modification-calc
1313
import { ModificationType } from '../generators/index.js';
1414
import { FileUpdater } from '../generators/writer.js';
1515
import { CodifyParser } from '../parser/index.js';
16-
import { DependencyMap, PluginManager } from '../plugins/plugin-manager.js';
16+
import { PluginManager, ResourceInfoMap } from '../plugins/plugin-manager.js';
1717
import { prettyFormatFileDiff } from '../ui/file-diff-pretty-printer.js';
1818
import { PromptType, Reporter } from '../ui/reporters/reporter.js';
1919
import { FileUtils } from '../utils/file.js';
@@ -27,7 +27,6 @@ export interface ImportArgs {
2727
typeIds?: string[];
2828
path: string;
2929
updateExisting?: boolean;
30-
secureMode?: boolean;
3130
verbosityLevel?: number;
3231
}
3332

@@ -56,10 +55,10 @@ export class ImportOrchestrator {
5655
}
5756

5857
static async autoImportAll(reporter: Reporter, initializeResult: InitializationResult, args: ImportArgs) {
59-
const { project, pluginManager, typeIdsToDependenciesMap } = initializeResult;
58+
const { project, pluginManager, resourceInfoMap } = initializeResult;
6059

6160
ctx.subprocessStarted(SubProcessName.IMPORT_RESOURCE)
62-
const importResults = await Promise.all([...typeIdsToDependenciesMap.keys()].map(async (typeId) => {
61+
const importResults = await Promise.all([...resourceInfoMap.keys()].map(async (typeId) => {
6362
try {
6463
return await pluginManager.importResource({
6564
core: { type: typeId },
@@ -82,28 +81,25 @@ export class ImportOrchestrator {
8281
const importedResources = flattenedResults.filter((r) => r && userSelectedTypes.includes(r.core.type))
8382
.map((r) => ResourceConfig.fromJson(r!));
8483

85-
const resourceInfoList = await pluginManager.getMultipleResourceInfo(
86-
[...project.resourceConfigs, ...importedResources].map((r) => r.type),
87-
);
88-
8984
await ImportOrchestrator.saveResults(
9085
reporter,
9186
{ result: importedResources, errors: [] },
9287
project,
93-
resourceInfoList,
88+
resourceInfoMap,
9489
pluginManager,
9590
args
9691
)
9792
}
9893

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

103-
const matchedTypes = this.matchTypeIds(typeIds, [...typeIdsToDependenciesMap.keys()])
104-
await ImportOrchestrator.validate(matchedTypes, project, pluginManager, typeIdsToDependenciesMap);
98+
const matchedTypes = this.matchTypeIds(typeIds, [...resourceInfoMap.keys()])
99+
await ImportOrchestrator.validate(matchedTypes, project, pluginManager, resourceInfoMap);
105100

106-
const resourceInfoList = (await pluginManager.getMultipleResourceInfo(matchedTypes))
101+
const resourceInfoList = [...resourceInfoMap.values()]
102+
.filter((info) => matchedTypes.includes(info.type))
107103
.filter((info) => info.canImport)
108104

109105
const resourcesToImport = await ImportOrchestrator.getImportParameters(reporter, project, resourceInfoList);
@@ -113,10 +109,7 @@ export class ImportOrchestrator {
113109

114110
reporter.displayImportResult(importResult, false);
115111

116-
resourceInfoList.push(...(await pluginManager.getMultipleResourceInfo(
117-
project.resourceConfigs.map((r) => r.type)
118-
)));
119-
await ImportOrchestrator.saveResults(reporter, importResult, project, resourceInfoList, pluginManager, args)
112+
await ImportOrchestrator.saveResults(reporter, importResult, project, resourceInfoMap, pluginManager, args)
120113
}
121114

122115
static async import(
@@ -162,7 +155,7 @@ export class ImportOrchestrator {
162155
reporter: Reporter,
163156
importResult: ImportResult,
164157
project: Project,
165-
resourceInfoList: ResourceInfo[],
158+
resourceInfoMap: ResourceInfoMap,
166159
pluginManager: PluginManager,
167160
args: ImportArgs,
168161
): Promise<void> {
@@ -177,7 +170,7 @@ export class ImportOrchestrator {
177170
const file = multipleCodifyFiles
178171
? project.codifyFiles[await reporter.promptOptions('\nIf new resources are added, where to write them?', project.codifyFiles)]
179172
: project.codifyFiles[0];
180-
await ImportOrchestrator.updateExistingFiles(reporter, project, importResult, resourceInfoList, file, pluginManager);
173+
await ImportOrchestrator.updateExistingFiles(reporter, project, importResult, resourceInfoMap, file, pluginManager);
181174
return;
182175
}
183176

@@ -199,7 +192,7 @@ export class ImportOrchestrator {
199192
reporter: Reporter,
200193
existingProject: Project,
201194
importResult: ImportResult,
202-
resourceInfoList: ResourceInfo[],
195+
resourceInfoMap: ResourceInfoMap,
203196
preferredFile: string, // File to write any new resources (unknown file path)
204197
pluginManager: PluginManager,
205198
): Promise<void> {
@@ -218,8 +211,8 @@ export class ImportOrchestrator {
218211

219212
const diffs = await Promise.all(Object.entries(groupedResults).map(async ([filePath, imported]) => {
220213
const existing = await CodifyParser.parse(filePath!);
221-
ImportOrchestrator.attachResourceInfo(imported, resourceInfoList);
222-
ImportOrchestrator.attachResourceInfo(existing.resourceConfigs, resourceInfoList);
214+
ImportOrchestrator.attachResourceInfo(imported, resourceInfoMap);
215+
ImportOrchestrator.attachResourceInfo(existing.resourceConfigs, resourceInfoMap);
223216

224217
const modificationCalculator = new FileModificationCalculator(existing);
225218
const modification = await modificationCalculator.calculate(
@@ -364,10 +357,10 @@ ${JSON.stringify(unsupportedTypeIds)}`);
364357
return result;
365358
}
366359

367-
private static async validate(typeIds: string[], project: Project, pluginManager: PluginManager, dependencyMap: DependencyMap): Promise<void> {
368-
project.validateTypeIds(dependencyMap);
360+
private static async validate(typeIds: string[], project: Project, pluginManager: PluginManager, resourceInfoMap: ResourceInfoMap): Promise<void> {
361+
project.validateTypeIds(resourceInfoMap);
369362

370-
const unsupportedTypeIds = typeIds.filter((type) => !dependencyMap.has(type));
363+
const unsupportedTypeIds = typeIds.filter((type) => !resourceInfoMap.has(type));
371364
if (unsupportedTypeIds.length > 0) {
372365
throw new Error(`The following resources cannot be imported. No plugins found that support the following types:
373366
${JSON.stringify(unsupportedTypeIds)}`);
@@ -377,6 +370,7 @@ ${JSON.stringify(unsupportedTypeIds)}`);
377370
private static async getImportParameters(reporter: Reporter, project: Project, resourceInfoList: ResourceInfo[]): Promise<Array<ResourceConfig>> {
378371
// Figure out which resources we need to prompt the user for additional info (based on the resource info)
379372
const [noPrompt, askPrompt] = resourceInfoList.reduce((result, info) => {
373+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
380374
info.getRequiredParameters().length === 0 ? result[0].push(info) : result[1].push(info);
381375
return result;
382376
}, [<ResourceInfo[]>[], <ResourceInfo[]>[]])
@@ -472,6 +466,7 @@ ${JSON.stringify(unsupportedTypeIds)}`);
472466
let fileName = path.join(folderPath, 'import.codify.jsonc')
473467
let counter = 1;
474468

469+
// eslint-disable-next-line no-constant-condition
475470
while (true) {
476471
if (!(await FileUtils.fileExists(fileName))) {
477472
return fileName;
@@ -483,9 +478,9 @@ ${JSON.stringify(unsupportedTypeIds)}`);
483478
}
484479

485480
// We have to attach additional info to the imported configs to make saving easier
486-
private static attachResourceInfo(resources: ResourceConfig[], resourceInfoList: ResourceInfo[]): void {
481+
private static attachResourceInfo(resources: ResourceConfig[], resourceInfoMap: ResourceInfoMap): void {
487482
resources.forEach((resource) => {
488-
const matchedInfo = resourceInfoList.find((info) => info.type === resource.type)!;
483+
const matchedInfo = resourceInfoMap.get(resource.type);
489484
if (!matchedInfo) {
490485
throw new Error(`Could not find type ${resource.type} in the resource info`);
491486
}

src/orchestrators/init.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ export interface InitArgs {
1313
verbosityLevel?: number;
1414
}
1515

16+
/**
17+
* Orchestrator for the init command. This command is used by new users to quickly get started with Codify.
18+
* Not to be confused with the plugin init command which is used by every command to initialize plugins.
19+
*/
1620
export const InitializeOrchestrator = {
1721

1822
async run(args: InitArgs, reporter: Reporter) {
@@ -22,10 +26,10 @@ export const InitializeOrchestrator = {
2226
await reporter.displayProgress();
2327

2428

25-
const { pluginManager, typeIdsToDependenciesMap } = await PluginInitOrchestrator.run(args, reporter);
29+
const { pluginManager, resourceInfoMap } = await PluginInitOrchestrator.run(args, reporter);
2630

2731
ctx.subprocessStarted(SubProcessName.IMPORT_RESOURCE)
28-
const importResults = await Promise.all([...typeIdsToDependenciesMap.keys()].map(async (typeId) => {
32+
const importResults = await Promise.all([...resourceInfoMap.keys()].map(async (typeId) => {
2933
try {
3034
return await pluginManager.importResource({
3135
core: { type: typeId },

0 commit comments

Comments
 (0)