spring-api-scanner 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/openapi.d.ts CHANGED
@@ -7,6 +7,7 @@ export interface OpenApiSchema {
7
7
  properties?: Record<string, OpenApiSchema>;
8
8
  required?: string[];
9
9
  $ref?: string;
10
+ enum?: string[];
10
11
  }
11
12
  export interface OpenApiParameter {
12
13
  in: "path" | "query" | "header";
package/dist/ui.js CHANGED
@@ -26,7 +26,7 @@ export function buildUiModel(input) {
26
26
  })),
27
27
  requestSchema,
28
28
  responseSchema,
29
- curl: buildCurl(endpoint)
29
+ curl: buildCurl(endpoint, requestSchema, input.openapi.components.schemas)
30
30
  };
31
31
  });
32
32
  return {
@@ -474,16 +474,107 @@ function pickResponseSchema(responses, openapi) {
474
474
  const best = responses["200"] ?? responses["201"];
475
475
  return deref(best?.content?.["application/json"]?.schema, openapi);
476
476
  }
477
- function buildCurl(endpoint) {
478
- const parts = [`curl -X ${endpoint.httpMethod} "http://localhost:3000${endpoint.fullPath}"`];
477
+ function buildCurl(endpoint, requestSchema, schemas) {
478
+ // Build URL with query parameters
479
+ let urlPath = endpoint.fullPath;
480
+ // Replace path variables with placeholder values
481
+ for (const pv of endpoint.pathVariables) {
482
+ const placeholder = getExampleValueForType(pv.type);
483
+ urlPath = urlPath.replace(`{${pv.name}}`, String(placeholder));
484
+ }
485
+ // Add query parameters to URL
486
+ const queryParts = [];
487
+ for (const qp of endpoint.queryParams) {
488
+ const exampleValue = getExampleValueForType(qp.type);
489
+ queryParts.push(`${qp.name}=${encodeURIComponent(String(exampleValue))}`);
490
+ }
491
+ if (queryParts.length > 0) {
492
+ urlPath += `?${queryParts.join("&")}`;
493
+ }
494
+ const parts = [`curl -X ${endpoint.httpMethod} "http://localhost:3000${urlPath}"`];
495
+ // Add headers
479
496
  for (const header of endpoint.headers) {
480
- parts.push(`-H "${header.name}: <${header.name}>"`);
497
+ const exampleValue = getExampleValueForType(header.type);
498
+ parts.push(`-H "${header.name}: ${exampleValue}"`);
481
499
  }
500
+ // Add request body with generated example
482
501
  if (endpoint.requestBody) {
483
502
  parts.push('-H "Content-Type: application/json"');
484
- parts.push("-d '{ }'");
503
+ const exampleBody = generateExampleFromSchema(requestSchema, schemas);
504
+ const bodyJson = JSON.stringify(exampleBody, null, 2);
505
+ // Escape the JSON for shell
506
+ const escapedBody = bodyJson.replace(/'/g, "'\"'\"'");
507
+ parts.push(`-d '${escapedBody}'`);
508
+ }
509
+ return parts.join(" \\\n ");
510
+ }
511
+ function getExampleValueForType(type) {
512
+ const cleanType = type.replace("?", "").trim();
513
+ switch (cleanType) {
514
+ case "String":
515
+ return "example";
516
+ case "Long":
517
+ case "Int":
518
+ return 123;
519
+ case "Boolean":
520
+ return true;
521
+ case "Double":
522
+ case "Float":
523
+ return 123.45;
524
+ default:
525
+ return "<value>";
526
+ }
527
+ }
528
+ function generateExampleFromSchema(schema, schemas, visited = new Set()) {
529
+ if (!schema) {
530
+ return {};
531
+ }
532
+ // Handle $ref references
533
+ if (schema.$ref) {
534
+ const refName = schema.$ref.split("/").pop();
535
+ if (refName && schemas && !visited.has(refName)) {
536
+ visited.add(refName);
537
+ const resolved = schemas[refName];
538
+ if (resolved) {
539
+ return generateExampleFromSchema(resolved, schemas, visited);
540
+ }
541
+ }
542
+ return {};
543
+ }
544
+ // Handle enums - use first enum value
545
+ if (schema.enum && schema.enum.length > 0) {
546
+ return schema.enum[0];
547
+ }
548
+ // Handle arrays
549
+ if (schema.type === "array" && schema.items) {
550
+ return [generateExampleFromSchema(schema.items, schemas, visited)];
551
+ }
552
+ // Handle objects
553
+ if (schema.type === "object" || schema.properties) {
554
+ const result = {};
555
+ if (schema.properties) {
556
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
557
+ result[key] = generateExampleFromSchema(propSchema, schemas, visited);
558
+ }
559
+ }
560
+ return result;
561
+ }
562
+ // Handle primitives based on type and format
563
+ switch (schema.type) {
564
+ case "string":
565
+ if (schema.format === "date-time") {
566
+ return "2026-01-01T00:00:00Z";
567
+ }
568
+ return "example";
569
+ case "integer":
570
+ return 123;
571
+ case "number":
572
+ return 123.45;
573
+ case "boolean":
574
+ return true;
575
+ default:
576
+ return null;
485
577
  }
486
- return parts.join(" \\\n+");
487
578
  }
488
579
  function guessController(sourceFile) {
489
580
  const file = sourceFile.replaceAll("\\", "/").split("/").at(-1) ?? sourceFile;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spring-api-scanner",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "CLI for scanning Spring Boot Kotlin APIs and generating OpenAPI + UI output",
5
5
  "type": "module",
6
6
  "bin": {