Skip to content

Commit 63fd92f

Browse files
authored
Merge pull request #2186 from rocketstack-matt/feat/enable-strict-mode-calm-models-cli
feat: enable TypeScript strict mode for calm-models and cli
2 parents 3764208 + cb82689 commit 63fd92f

File tree

12 files changed

+43
-38
lines changed

12 files changed

+43
-38
lines changed

calm-models/src/canonical/template-models.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export type CalmFlowCanonicalModel = {
5959
name: string;
6060
description: string;
6161
transitions: CalmFlowTransitionCanonicalModel[];
62-
'requirement-url': string;
62+
'requirement-url'?: string;
6363
controls?: CalmControlsCanonicalModel;
6464
metadata?: CalmMetadataCanonicalModel;
6565
};

calm-models/src/model/moment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class CalmMoment extends CalmNode implements CalmAdaptable<CalmMomentSche
5151
name,
5252
description,
5353
validFrom,
54-
CalmNodeDetails.fromSchema(details),
54+
details ? CalmNodeDetails.fromSchema(details) : undefined,
5555
controls ? CalmControls.fromSchema(controls) : undefined,
5656
metadata ? CalmMetadata.fromSchema(metadata) : undefined,
5757
adrs,

calm-models/tsconfig.build.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"extends": "../tsconfig.base.json",
33
"compilerOptions": {
4+
"strict": true,
45
"outDir": "./dist",
56
"rootDir": "./src",
67
"declaration": true,
@@ -17,4 +18,3 @@
1718
"**/*.test.ts"
1819
]
1920
}
20-

cli/src/cli-config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ export async function loadCliConfig(): Promise<CLIConfig | null> {
2323
return parsed;
2424
}
2525
catch (err) {
26-
if (err.code === 'ENOENT') {
26+
if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {
2727
logger.debug('No config file found at ' + configFilePath);
2828
return null;
2929
}
30-
logger.error('Unexpected error loading user config: ', err);
30+
logger.error('Unexpected error loading user config: ' + String(err));
3131
return null;
3232
}
3333
}

cli/src/cli.e2e.spec.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,8 @@ describe('CLI Integration Tests', () => {
395395
try {
396396
await run(calm(), ['validate', '-p', patternPath, '-a', archPath]);
397397
expect.fail('Expected validation to fail');
398-
} catch (error) {
399-
const result = JSON.parse(error.stdout);
398+
} catch (error: unknown) {
399+
const result = JSON.parse((error as { stdout: string }).stdout);
400400
expect(result.hasErrors).toBe(true);
401401
expect(JSON.stringify(result)).toContain('owner');
402402
}
@@ -410,8 +410,8 @@ describe('CLI Integration Tests', () => {
410410
try {
411411
await run(calm(), ['validate', '-p', patternPath, '-a', archPath, '-u', mappingPath]);
412412
expect.fail('Expected validation to fail');
413-
} catch (error) {
414-
const result = JSON.parse(error.stdout);
413+
} catch (error: unknown) {
414+
const result = JSON.parse((error as { stdout: string }).stdout);
415415
expect(result.hasErrors).toBe(true);
416416
expect(JSON.stringify(result)).toContain('owner');
417417
}
@@ -452,7 +452,7 @@ describe('CLI Integration Tests', () => {
452452
expect(res.status).toBe(200);
453453
expect(res.data.status).toBe('OK');
454454
} finally {
455-
process.kill(-serverProcess.pid);
455+
if (serverProcess.pid) process.kill(-serverProcess.pid);
456456
}
457457
});
458458

@@ -486,7 +486,7 @@ describe('CLI Integration Tests', () => {
486486
expect(JSON.stringify(res.data)).toContain('hasErrors');
487487
expect(JSON.stringify(res.data)).toContain('hasWarnings');
488488
} finally {
489-
process.kill(-serverProcess.pid);
489+
if (serverProcess.pid) process.kill(-serverProcess.pid);
490490
}
491491
});
492492

@@ -834,18 +834,18 @@ describe('CLI Integration Tests', () => {
834834
fs.writeFileSync(filePath, JSON.stringify(obj, null, 2));
835835
}
836836

837-
function readJson(filePath: string) {
837+
function readJson(filePath: string): Record<string, unknown> {
838838
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
839839
}
840840

841-
function patchJson(filePath: string, patchFn: (o: object) => void) {
841+
function patchJson(filePath: string, patchFn: (o: Record<string, unknown>) => void) {
842842
const obj = readJson(filePath);
843843
patchFn(obj);
844844
writeJson(filePath, obj);
845845
}
846846

847847
// Utility to recursively remove specific line/character fields from JSON
848-
function removeLineNumbers(obj: object) {
848+
function removeLineNumbers(obj: unknown): void {
849849
const fieldsToRemove = [
850850
'line_start',
851851
'line_end',
@@ -854,12 +854,13 @@ describe('CLI Integration Tests', () => {
854854
];
855855
if (Array.isArray(obj)) {
856856
obj.forEach(removeLineNumbers);
857-
} else if (obj && typeof obj === 'object') {
858-
for (const key of Object.keys(obj)) {
857+
} else if (obj !== null && typeof obj === 'object') {
858+
const record = obj as Record<string, unknown>;
859+
for (const key of Object.keys(record)) {
859860
if (fieldsToRemove.includes(key)) {
860-
delete obj[key];
861+
delete record[key];
861862
} else {
862-
removeLineNumbers(obj[key]);
863+
removeLineNumbers(record[key]);
863864
}
864865
}
865866
}

cli/src/cli.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { CalmChoice } from '@finos/calm-shared/dist/commands/generate/components
66
import { buildDocumentLoader, DocumentLoader, DocumentLoaderOptions } from '@finos/calm-shared/dist/document-loader/document-loader';
77
import { loadCliConfig } from './cli-config';
88
import path from 'path';
9-
import inquirer from 'inquirer';
9+
import { select } from '@inquirer/prompts';
1010

1111
// Shared options used across multiple commands
1212
const ARCHITECTURE_OPTION = '-a, --architecture <file>';
@@ -242,13 +242,10 @@ Validation requires:
242242
const providers = AI_PROVIDER_CHOICES;
243243
let selectedProvider: string = options.provider;
244244
if (!selectedProvider) {
245-
const answer = await inquirer.prompt({
246-
type: 'list',
247-
name: 'provider',
245+
selectedProvider = await select({
248246
message: 'Select an AI provider:',
249-
choices: providers.map((p) => ({ name: p, value: p })),
247+
choices: providers.map((p: string) => ({ name: p, value: p })),
250248
});
251-
selectedProvider = answer.provider;
252249
}
253250
console.log(`Selected AI provider: ${selectedProvider}`);
254251

@@ -268,7 +265,7 @@ export async function parseDocumentLoaderConfig(
268265
urlToLocalMap?: Map<string, string>,
269266
basePath?: string
270267
): Promise<DocumentLoaderOptions> {
271-
const logger = initLogger(options.verbose, 'calm-cli');
268+
const logger = initLogger(options.verbose ?? false, 'calm-cli');
272269
const docLoaderOpts: DocumentLoaderOptions = {
273270
calmHubUrl: options.calmHubUrl,
274271
schemaDirectoryPath: options.schemaDirectory,

cli/src/command-helpers/template.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('getUrlToLocalFileMap', () => {
4040
});
4141
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
4242

43-
const processExitSpy = vi.spyOn(process, 'exit').mockImplementation((code?: number) => {
43+
const processExitSpy = vi.spyOn(process, 'exit').mockImplementation((code?: string | number | null) => {
4444
throw new Error(`process.exit: ${code}`);
4545
});
4646

cli/src/command-helpers/validate.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ export async function runValidate(options: ValidateOptions) {
3434
const schemaDirectory = await buildSchemaDirectory(docLoader, options.verbose);
3535
await schemaDirectory.loadSchemas();
3636

37-
let architecture: object;
38-
let pattern: object;
39-
let timeline: object;
37+
let architecture: object | undefined;
38+
let pattern: object | undefined;
39+
let timeline: object | undefined;
4040

4141
if (options.timelinePath) {
4242
const result = await loadTimeline(
@@ -50,8 +50,8 @@ export async function runValidate(options: ValidateOptions) {
5050
}
5151
else {
5252
const result = await loadArchitectureAndPattern(
53-
options.architecturePath,
54-
options.patternPath,
53+
options.architecturePath ?? '',
54+
options.patternPath ?? '',
5555
docLoader,
5656
schemaDirectory,
5757
logger
@@ -60,15 +60,20 @@ export async function runValidate(options: ValidateOptions) {
6060
pattern = result.pattern;
6161
}
6262
const documentContexts = buildDocumentContexts(options, logger);
63+
if (!architecture && !pattern && !timeline) {
64+
throw new Error('You must provide an architecture, a pattern, or a timeline');
65+
}
6366
const outcome = await validate(architecture, pattern, timeline, schemaDirectory, options.verbose);
6467
enrichWithDocumentPositions(outcome, documentContexts);
6568
const content = getFormattedOutput(outcome, options.outputFormat, toFormattingOptions(documentContexts));
6669
writeOutputFile(options.outputPath, content);
6770
exitBasedOffOfValidationOutcome(outcome, options.strict);
6871
}
6972
catch (err) {
70-
logger.error('An error occurred while validating: ' + err.message);
71-
logger.debug(err.stack);
73+
const message = err instanceof Error ? err.message : String(err);
74+
const stack = err instanceof Error ? err.stack : undefined;
75+
logger.error('An error occurred while validating: ' + message);
76+
if (stack) logger.debug(stack);
7277
process.exit(1);
7378
}
7479
}

cli/src/server/routes/routes.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ vi.mock('@finos/calm-shared', () =>{
3131
};
3232
});
3333
describe('CLIServerRoutes', () => {
34-
let schemaDirectory: SchemaDirectory;
34+
const schemaDirectory = {} as SchemaDirectory;
3535
let cliServerRoutes: CLIServerRoutes;
3636
let mockRouter: Router;
3737

cli/src/server/routes/validation-route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export class ValidationRouter {
6060
const outcome = await validate(architecture, foundSchema, undefined, this.schemaDirectory, true);
6161
return res.status(201).type('json').send(outcome);
6262
} catch (error) {
63-
return res.status(500).type('json').send(new ErrorResponse(error.message));
63+
const message = error instanceof Error ? error.message : String(error);
64+
return res.status(500).type('json').send(new ErrorResponse(message));
6465
}
6566
};
6667
}
@@ -72,6 +73,6 @@ class ErrorResponse {
7273
}
7374
};
7475

75-
class ValidationRequest {
76+
interface ValidationRequest {
7677
architecture: string;
7778
}

0 commit comments

Comments
 (0)