Skip to content

Commit baef15e

Browse files
committed
Address #665
There is now much more explicit information available when loading custom functions and the rules loaded is accurate.
1 parent 6a70794 commit baef15e

File tree

6 files changed

+74
-7
lines changed

6 files changed

+74
-7
lines changed

cmd/shared_functions.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,19 @@ func LoadCustomFunctions(functionsFlag string, silence bool) (map[string]model.R
124124
pterm.Println()
125125
return nil, err
126126
}
127+
128+
customFunctions := pm.GetCustomFunctions()
127129
pterm.Info.Printf("Loaded %d custom function(s) successfully.\n", pm.LoadedFunctionCount())
128-
return pm.GetCustomFunctions(), nil
130+
131+
if !silence && len(customFunctions) > 0 {
132+
pterm.Info.Println("Available custom functions:")
133+
for funcName := range customFunctions {
134+
pterm.Printf(" - %s\n", pterm.LightCyan(funcName))
135+
}
136+
pterm.Println()
137+
}
138+
139+
return customFunctions, nil
129140
}
130141
return nil, nil
131142
}

motor/rule_applicator.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,38 @@ func buildResults(ctx ruleContext, ruleAction model.RuleAction, nodes []*yaml.No
964964
}
965965

966966
}
967+
} else {
968+
// Function not found - report detailed error
969+
if !ctx.silenceLogs {
970+
// Build list of available custom functions for debugging
971+
var availableCustomFuncs []string
972+
if ctx.customFunctions != nil {
973+
for funcName := range ctx.customFunctions {
974+
availableCustomFuncs = append(availableCustomFuncs, funcName)
975+
}
976+
}
977+
978+
if len(availableCustomFuncs) > 0 {
979+
pterm.Error.Printf("Rule '%s' uses unknown function '%s'. Available custom functions: %v\n",
980+
ctx.rule.Id, ruleAction.Function, availableCustomFuncs)
981+
} else {
982+
pterm.Error.Printf("Rule '%s' uses unknown function '%s'. No custom functions loaded. Use --functions flag to load custom functions.\n",
983+
ctx.rule.Id, ruleAction.Function)
984+
}
985+
}
986+
987+
// Add error result to make the missing function visible in reports
988+
lock.Lock()
989+
*ctx.ruleResults = append(*ctx.ruleResults, model.RuleFunctionResult{
990+
Message: fmt.Sprintf("Unknown function '%s' in rule '%s'", ruleAction.Function, ctx.rule.Id),
991+
Rule: ctx.rule,
992+
StartNode: &yaml.Node{},
993+
EndNode: &yaml.Node{},
994+
RuleId: ctx.rule.Id,
995+
RuleSeverity: "error",
996+
Path: fmt.Sprint(ctx.rule.Given),
997+
})
998+
lock.Unlock()
967999
}
9681000
return ctx.ruleResults
9691001
}

motor/rule_applicator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2225,7 +2225,7 @@ paths:
22252225
results := ApplyRulesToRuleSet(rse)
22262226

22272227
assert.Len(t, results.Results, 1)
2228-
assert.Equal(t, "more than a single path exists, there are 3", results.Results[0].Message)
2228+
assert.Equal(t, "More than a single path exists, found 3 paths", results.Results[0].Message)
22292229
}
22302230

22312231
func TestApplyRules_TestRules_Custom_JS_Function_CustomDoc_CoreFunction_FunctionOptions(t *testing.T) {

plugin/plugin_loader.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,29 @@ func LoadFunctions(path string, silence bool) (*Manager, error) {
6767

6868
// found something
6969
if !silence {
70-
pterm.Info.Printf("Located custom javascript function: '%s'\n", function.GetSchema().Name)
70+
pterm.Info.Printf("Located custom javascript function: '%s' from file: %s\n", function.GetSchema().Name, fPath)
7171
}
7272
// check if the function is valid
7373
sErr := function.CheckScript()
7474

7575
if sErr != nil {
7676
pterm.Error.Printf("Failed to load function '%s': %s\n", fName, sErr.Error())
77+
continue // Skip registering invalid functions
78+
} else {
79+
if !silence {
80+
pterm.Success.Printf("Successfully validated JavaScript function: '%s'\n", fName)
81+
}
7782
}
7883

7984
// register core functions with this custom function.
8085
RegisterCoreFunctions(function)
8186

8287
// register this function with the plugin manager
8388
pm.RegisterFunction(fName, function)
89+
90+
if !silence {
91+
pterm.Info.Printf("Registered custom function: '%s' -> available for use in rulesets\n", fName)
92+
}
8493
}
8594
}
8695
return pm, nil

plugin/sample/js/check_single_path.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
1+
function getSchema() {
2+
return {
3+
"name": "checkSinglePath",
4+
"description": "Checks that there is only a single path defined in the OpenAPI spec"
5+
};
6+
}
7+
18
function runRule(input) {
9+
// input should be the paths object from the OpenAPI spec
10+
if (!input || typeof input !== 'object') {
11+
return [];
12+
}
13+
214
const numPaths = Object.keys(input).length;
315
if (numPaths > 1) {
416
return [
517
{
6-
message: 'more than a single path exists, there are ' + numPaths
18+
message: 'More than a single path exists, found ' + numPaths + ' paths'
719
}
820
];
921
}
22+
23+
// Return empty array when rule passes (no violations)
24+
return [];
1025
}

rulesets/examples/sample-plugin-ruleset.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ rules:
88
formats: [oas2, oas3]
99
given: $
1010
then:
11-
function: uselessFunc
11+
function: useless_func
1212
howToFix: You can't, it's just an example.
1313
sample-paths-rule:
1414
description: Load a custom function that checks for a single path
1515
severity: error
1616
recommended: true
1717
formats: [ oas2, oas3 ]
18-
given: $
18+
given: $.paths
1919
then:
20-
function: checkSinglePathExists
20+
function: check_single_path
2121
howToFix: use a spec with only a single path defined.

0 commit comments

Comments
 (0)