Skip to content

Commit e961730

Browse files
fix: remove unnecessary creation of rules/instances
Signed-off-by: Fredrik Nordlander <fredrik.nordlander@digg.se>
1 parent dccd71b commit e961730

File tree

5 files changed

+95
-14
lines changed

5 files changed

+95
-14
lines changed

src/cli-mode.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { parseApiSpecInput,detectSpecFormatPreference, ParseResult} from './util
2121
import { SpecParseError } from './util/RapLPSpecParseError.js';
2222
import * as path from 'node:path';
2323
import { RuleExecutionContext } from './util/RuleExecutionContext.js';
24+
import { parseRuleCategories,resolveRuleCategories } from './rulesets/util/ruleModules.js';
2425

2526
declare var AggregateError: {
2627
prototype: AggregateError;
@@ -44,7 +45,9 @@ export async function execCLI<T extends CliArgs>(argv: T) {
4445
try {
4546
// Parse command-line arguments using yargs
4647
const apiSpecFileName = (argv.file as string) || '';
47-
const ruleCategories = argv.categories ? (argv.categories as string).split(',') : undefined;
48+
//const ruleCategories = argv.categories ? (argv.categories as string).split(',') : undefined;
49+
const ruleCategories = parseRuleCategories(argv.categories as string | undefined);
50+
const resolvedCategories = resolveRuleCategories(ruleCategories);
4851
const logErrorFilePath = argv.logError as string | undefined;
4952
const logDiagnosticFilePath = argv.logDiagnostic as string | undefined;
5053
const strict = argv.strict as boolean ?? false;
@@ -128,7 +131,7 @@ export async function execCLI<T extends CliArgs>(argv: T) {
128131

129132
try {
130133
// Import and create rule instances in RAP-LP
131-
const enabledRulesAndCategorys = await importAndCreateRuleInstances(context,ruleCategories);
134+
const enabledRulesAndCategorys = await importAndCreateRuleInstances(context,resolvedCategories);
132135
// Load API specification into a Document object
133136
const parser: IParser<any> = (parseResult.format === 'json' ? Parsers.Json : Parsers.Yaml) as unknown as IParser<any>;
134137
apiSpecDocument = new SpectralDocument(parseResult.raw, parser, apiSpecFileName);

src/routes/urlValidation.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { importAndCreateRuleInstances } from '../util/ruleUtil.js';
1111
import { ERROR_TYPE, RapLPBaseApiError } from '../util/RapLPBaseApiErrorHandling.js';
1212
import { loadUrlValidationConfiguration } from '../util/urlValidationConfig.js';
1313
import { RuleExecutionContext } from '../util/RuleExecutionContext.js';
14+
import { parseRuleCategories,resolveRuleCategories } from '../rulesets/util/ruleModules.js';
1415

1516
export const registerUrlValidationRoutes = (app: Express, urlValidationConfigFile?: string) => {
1617
const config = loadUrlValidationConfiguration(urlValidationConfigFile);
@@ -34,10 +35,12 @@ export const registerUrlValidationRoutes = (app: Express, urlValidationConfigFil
3435
const yamlContentString = await response.text();
3536

3637
validateYamlInput(yamlContentString);
38+
39+
const apiSpecDocument = new Document(yamlContentString, Parsers.Yaml, 'payload.yaml');
40+
const ruleCategories = parseRuleCategories(dto.categories);
41+
const resolvedCategories = resolveRuleCategories(ruleCategories);
3742

38-
const apiSpecDocument = new Document(yamlContentString, Parsers.Yaml, '');
39-
40-
const rules = await importAndCreateRuleInstances(context, dto.categories);
43+
const rules = await importAndCreateRuleInstances(context, resolvedCategories);
4144

4245
const result = await processApiSpec(context,rules, apiSpecDocument);
4346
res.send(result);

src/routes/validate.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { ERROR_TYPE, RapLPBaseApiError } from '../util/RapLPBaseApiErrorHandling
2020
import type { IParser } from '@stoplight/spectral-parsers';
2121
import { stringify } from 'node:querystring';
2222
import { RuleExecutionContext } from '../util/RuleExecutionContext.js';
23+
import { parseRuleCategories,resolveRuleCategories,RULE_REGISTRY} from '../rulesets/util/ruleModules.js';
24+
2325

2426

2527

@@ -28,16 +30,24 @@ export const registerValidationRoutes = (app: Express) => {
2830
app.post('/api/v1/validation/validate', async (req, res, next) => {
2931
try {
3032
const context = new RuleExecutionContext();
33+
34+
const body: SpecValidationRequestDto = req.body;
35+
const strict = body.strict ?? true;
36+
const categories = body.categories ?? [];
37+
3138
const yamlContent: YamlContentDto = req.body;
3239

33-
let yamlContentString: string;
34-
yamlContentString = decodeBase64String(yamlContent.yaml);
40+
//let yamlContentString: string;
41+
//yamlContentString = decodeBase64String(yamlContent.yaml);
42+
const raw = decodeBase64String(body.spec);
3543

36-
validateYamlInput(yamlContentString);
44+
validateYamlInput(raw); // Do we need this ?
3745

38-
const apiSpecDocument = new Document(yamlContentString, Parsers.Yaml, '');
46+
const apiSpecDocument = new Document(raw, Parsers.Yaml, 'payload.yaml');
47+
const ruleCategories = parseRuleCategories(categories);
48+
const resolvedCategories = resolveRuleCategories(ruleCategories);
3949

40-
const rules = await importAndCreateRuleInstances(context, yamlContent.categories);
50+
const rules = await importAndCreateRuleInstances(context, resolvedCategories);
4151

4252
const result = await processApiSpec(context,rules, apiSpecDocument);
4353
res.send(result);
@@ -47,7 +57,7 @@ export const registerValidationRoutes = (app: Express) => {
4757
});
4858

4959
app.get('/api/v1/validation/rules', (req, res) => {
50-
res.send(validationRules);
60+
res.send(RULE_REGISTRY);
5161
});
5262

5363
app.get('/api/v1/api-info', async (req, res, next) => {
@@ -144,8 +154,12 @@ export const registerValidationRoutes = (app: Express) => {
144154

145155
// 5. No strict-errors → run raplp ruleengine
146156
const parser: IParser<any> = (parseResult.format === 'json' ? Parsers.Json : Parsers.Yaml) as unknown as IParser<any>;
147-
const apiSpecDocument = new Document(parseResult.raw, parser, 'payload.yaml'); // In-memory-file to calculate correct positions when parsing
148-
const rules = await importAndCreateRuleInstances(context, categories);
157+
const apiSpecDocument = new Document(parseResult.raw, parser, 'payload.yaml'); // In-memory-file to calculate correct positions when parsing
158+
159+
const ruleCategories = parseRuleCategories(categories);
160+
const resolvedCategories = resolveRuleCategories(ruleCategories);
161+
162+
const rules = await importAndCreateRuleInstances(context, resolvedCategories);
149163
const result = await processApiSpec(context, rules, apiSpecDocument);
150164

151165
const hasRuleViolations = result.result.some(

src/rulesets/util/ruleModules.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-FileCopyrightText: 2025 Digg - Agency for Digital Government
2+
//
3+
// SPDX-License-Identifier: EUPL-1.2
4+
5+
export const RULE_REGISTRY = [
6+
{ rule: 'UfnRules', description: 'URL Format och namngivning' },
7+
{ rule: 'SakRules', description: 'Säkerhet' },
8+
{ rule: 'VerRules', description: 'Versionshantering' },
9+
{ rule: 'FnsRules', description: 'Filtrering, paginering och sökparametrar' },
10+
{ rule: 'ArqRules', description: 'API Request' },
11+
{ rule: 'DokRules', description: 'Dokumentation' },
12+
{ rule: 'AmeRules', description: 'API Message' },
13+
{ rule: 'ForRules', description: 'Förutsättningar' },
14+
{ rule: 'DotRules', description: 'Datum- och tidsformat' },
15+
{ rule: 'ResRules', description: 'Mognad' },
16+
{ rule: 'MogRules', description: 'Datum- och tidsformat' },
17+
{ rule: 'FelRules', description: 'Felhantering' },
18+
] as const;
19+
20+
export type RuleModuleName = typeof RULE_REGISTRY[number]['rule'];
21+
export const RULE_MODULE_NAMES: RuleModuleName[] =
22+
RULE_REGISTRY.map(r => r.rule);
23+
24+
export function parseRuleCategories(
25+
input?: string | string[]
26+
): RuleModuleName[] | undefined {
27+
28+
if (!input) return undefined;
29+
30+
const categories =
31+
typeof input === "string"
32+
? input.split(",").map(c => c.trim())
33+
: input;
34+
35+
const invalid = categories.filter(
36+
c => !RULE_MODULE_NAMES.includes(c as RuleModuleName)
37+
);
38+
39+
if (invalid.length) {
40+
throw new Error(`Invalid rule categories: ${invalid.join(", ")}`);
41+
}
42+
43+
return categories as RuleModuleName[];
44+
}
45+
export function resolveRuleCategories(
46+
categories?: RuleModuleName[]
47+
): RuleModuleName[] {
48+
49+
if (!categories || categories.length === 0) {
50+
return [...RULE_MODULE_NAMES];
51+
}
52+
53+
const result = [...categories];
54+
55+
if (!result.includes('ForRules')) {
56+
result.push('ForRules');
57+
}
58+
59+
return result;
60+
}

src/util/ruleUtil.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import { RuleCategoryError } from './RapLPBaseApiErrorHandling.js';
66
import { RuleExecutionContext } from './RuleExecutionContext.js';
7+
import { RuleModuleName } from '../rulesets/util/ruleModules.js';
78

89
// ruleUtil.ts
910
interface CustomSchema {
@@ -47,7 +48,7 @@ export function getRuleModules() {
4748
* @returns Promise object with enabled rules in RAP-LP to run
4849
*/
4950
export async function importAndCreateRuleInstances(
50-
context: RuleExecutionContext, ruleCategories?: string[],
51+
context: RuleExecutionContext, ruleCategories: RuleModuleName[],
5152
): Promise<{ rules: Record<string, any>; instanceCategoryMap: Map<string, any> }> {
5253
const ruleInstances: Record<string, any> = {}; // store instances of rule classes
5354
const ruleTypes: any[] = []; // array to store rule classes.

0 commit comments

Comments
 (0)