Skip to content

Commit 55b0f95

Browse files
committed
fix(cli): wheels routes prints route table instead of AI-docs JSON dump
`wheels routes` was hitting `/wheels/ai?context=routing` — the framework's AI-documentation endpoint, which dumps ~500KB of JSON describing routing patterns/helpers. The user expected (and the CLI help promised) the application's actual route table. Two coordinated fixes: - cli/lucli/Module.cfc: switch `routes()` to call `/wheels/cli?command=routes&format=json` (the existing endpoint that serializes `application.wheels.routes`), parse the JSON, and print a formatted table with METHOD / PATTERN / CONTROLLER#ACTION columns plus the route name in parentheses where set. Pattern leading "/" is normalised so each row has exactly one. - vendor/wheels/public/views/cli.cfm: the `case "routes":` branch was reading `application.wheels.appKey` as a property — but `appKey` is a function (`$appKey()`), not a property. The lookup failed silently and `data.routes` came back empty with `success: false`. Routes live at `application.wheels.routes` directly (the convention every other case in this file already uses); switch to that. Sample output: METHOD PATTERN CONTROLLER#ACTION ---------------------------------------------- GET /wheels/info wheels.public#info (wheelsInfo) GET / main#index (root) ... 35 route(s) Fixes #2317.
1 parent a85a43e commit 55b0f95

2 files changed

Lines changed: 66 additions & 8 deletions

File tree

‎cli/lucli/Module.cfc‎

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,10 +505,68 @@ component extends="modules.BaseModule" {
505505
var serverPort = $requireRunningServer();
506506

507507
try {
508-
var routesUrl = "http://localhost:#serverPort#/wheels/ai?context=routing";
508+
// /wheels/cli?command=routes returns the actual application route
509+
// table as JSON. (The previous endpoint, /wheels/ai?context=routing,
510+
// returns AI-documentation about routing patterns — not what users
511+
// asking "what routes does my app have?" expect to see.)
512+
var routesUrl = "http://localhost:#serverPort#/wheels/cli?command=routes&format=json";
509513
var httpResult = makeHttpRequest(routesUrl);
510514

511-
out(httpResult);
515+
var result = "";
516+
try {
517+
result = deserializeJSON(httpResult);
518+
} catch (any jsonErr) {
519+
out("Failed to parse routes response", "red");
520+
verbose(httpResult);
521+
return "";
522+
}
523+
524+
if (!structKeyExists(result, "success") || !result.success) {
525+
out("Failed to fetch routes: #result.message ?: 'unknown error'#", "red");
526+
return "";
527+
}
528+
529+
if (!structKeyExists(result, "routes") || !arrayLen(result.routes)) {
530+
out("No routes configured.", "yellow");
531+
return "";
532+
}
533+
534+
// Normalise patterns so the leading "/" is shown exactly once. The
535+
// framework stores routes with the leading slash already present,
536+
// but defensively handle the case where it isn't.
537+
var formatPattern = function(p) {
538+
p = p ?: "";
539+
return left(p, 1) == "/" ? p : "/" & p;
540+
};
541+
542+
// Compute column widths from the data so the table aligns cleanly.
543+
var maxMethod = len("METHOD");
544+
var maxPattern = len("PATTERN");
545+
var maxAction = len("CONTROLLER##ACTION");
546+
for (var route in result.routes) {
547+
var methodWidth = len(uCase(route.methods ?: ""));
548+
var patternWidth = len(formatPattern(route.pattern));
549+
var actionWidth = len((route.controller ?: "") & "##" & (route.action ?: ""));
550+
if (methodWidth > maxMethod) maxMethod = methodWidth;
551+
if (patternWidth > maxPattern) maxPattern = patternWidth;
552+
if (actionWidth > maxAction) maxAction = actionWidth;
553+
}
554+
555+
out(lJustify("METHOD", maxMethod) & " " & lJustify("PATTERN", maxPattern) & " " & "CONTROLLER##ACTION", "bold");
556+
out(repeatString("-", maxMethod + maxPattern + maxAction + 4));
557+
558+
for (var route in result.routes) {
559+
var line = lJustify(uCase(route.methods ?: ""), maxMethod)
560+
& " " & lJustify(formatPattern(route.pattern), maxPattern)
561+
& " " & (route.controller ?: "") & "##" & (route.action ?: "");
562+
if (structKeyExists(route, "name") && len(route.name)) {
563+
line &= " (" & route.name & ")";
564+
}
565+
out(line);
566+
}
567+
568+
out("");
569+
out("#arrayLen(result.routes)# route(s)", "cyan");
512570
} catch (any e) {
513571
out("Failed to fetch routes: #e.message#", "red");
514572
}

‎vendor/wheels/public/views/cli.cfm‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -417,14 +417,14 @@ try {
417417
break;
418418
419419
case "routes":
420-
// Return application routes
420+
// Return application routes. Routes live at application.wheels.routes
421+
// (the convention every other case in this file uses); the previous
422+
// `application[application.wheels.appKey]` indirection was broken
423+
// because `appKey` is a function, not a property.
421424
data.success = true;
422425
data.routes = [];
423-
424-
// Get routes from application
425-
local.appKey = application.wheels.appKey;
426-
if (structKeyExists(application, local.appKey) && structKeyExists(application[local.appKey], "routes")) {
427-
for (local.route in application[local.appKey].routes) {
426+
if (structKeyExists(application, "wheels") && structKeyExists(application.wheels, "routes")) {
427+
for (local.route in application.wheels.routes) {
428428
local.routeInfo = {
429429
name = structKeyExists(local.route, "name") ? local.route.name : "",
430430
pattern = structKeyExists(local.route, "pattern") ? local.route.pattern : "",

0 commit comments

Comments
 (0)