sourcecode 2.5.17__tar.gz → 2.5.18__tar.gz
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.
- {sourcecode-2.5.17 → sourcecode-2.5.18}/CHANGELOG.md +42 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/PKG-INFO +1 -1
- {sourcecode-2.5.17 → sourcecode-2.5.18}/pyproject.toml +1 -1
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/__init__.py +1 -1
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/validation_surface.py +128 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/.gitignore +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/.ruff.toml +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/CONTRIBUTING.md +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/LICENSE +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/README.md +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/SECURITY.md +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/raw +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/scripts/perf_harness.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/archetype.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/cache.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/classifier.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/cli.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/explain.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/hibernate_strat.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/license.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/perf.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/progress.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/reconciliation.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/redactor.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/repository_ir.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/steps_graph.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/steps_impact.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/steps_intf.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/ris.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/scanner.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/schema.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/security_config.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/security_posture.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/semantic_integration_engine.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/serializer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/spring_impact.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/version_check.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/src/sourcecode/workspace.py +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/supabase/functions/README.md +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-2.5.17 → sourcecode-2.5.18}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.5.18] — 2026-07-20
|
|
4
|
+
|
|
5
|
+
**Engineering Decision Support — Part A, F1: standalone OpenAPI specs now reach the
|
|
6
|
+
validation surface.** First correctness-foundation fix of the capability milestone
|
|
7
|
+
(`docs/DEVELOPMENT-ROADMAP.md`): trust-critical, stability-permitted (consumer-wiring
|
|
8
|
+
over an existing producer, no new inference). A repo shipping a plain `openapi.yml`
|
|
9
|
+
with no openapi-generator interface-controller was reported "No OpenAPI spec found" —
|
|
10
|
+
a confirmed false negative that would launder into any downstream decision. Full suite
|
|
11
|
+
(3538 passed, 5 skipped) confirms confinement.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **F1 — standalone-spec discovery reaches the validation surface.** `extract_java_endpoints`
|
|
16
|
+
only links a spec when an `@RestController implements XxxApi` interface-controller
|
|
17
|
+
matches it; a repo with a plain `openapi.yml` and no such controller fell through to
|
|
18
|
+
"No OpenAPI spec found". The validation surface now consults the existing
|
|
19
|
+
`openapi_surface.build_openapi_surface` producer directly in that branch: a discovered
|
|
20
|
+
spec's body operations (POST/PUT/PATCH) contribute their declared constraints as
|
|
21
|
+
`source="openapi-spec"` routes (deduped against source-annotated routes by
|
|
22
|
+
method+path), the `openapi_spec` claim flips from `null` to the spec path, and the
|
|
23
|
+
note explains the standalone discovery. Genuinely absent specs are unchanged — the
|
|
24
|
+
`null` claim and source-DTO recovery path stand. New `TestStandaloneSpecRecovery`
|
|
25
|
+
regression class (4 tests).
|
|
26
|
+
- **F1 — `zero_result_reason` on genuine absence.** When no spec is on disk and no
|
|
27
|
+
in-repo DTO constraints are recoverable, the validation surface now carries a
|
|
28
|
+
machine-readable `zero_result_reason = "no_openapi_spec_and_no_source_dto_constraints"`
|
|
29
|
+
beside the prose note, so a consumer can distinguish an attested negative ("looked,
|
|
30
|
+
found nothing") from an un-run analysis. Absent on any positive result.
|
|
31
|
+
|
|
32
|
+
### Notes
|
|
33
|
+
|
|
34
|
+
- **Reconciliation rule deliberately NOT added for F1.** The roadmap's F1 strategy
|
|
35
|
+
suggested a "spec-present vs spec-absent" reconciliation rule as the honesty guard.
|
|
36
|
+
The flip was instead achieved at the true source (the validation surface now reads
|
|
37
|
+
the spec), and the two sibling surfaces that also report spec presence
|
|
38
|
+
(`serializer` arch_reasons, `confidence_analyzer` api_contract gap) were verified to
|
|
39
|
+
already detect a standalone `openapi.yml` via their filename check — so **no surface
|
|
40
|
+
makes the false "no spec" claim**, leaving the reconciliation layer no provable
|
|
41
|
+
contradiction to fire on. Adding a rule that never fires on any real repo violates
|
|
42
|
+
the layer's own contract (fire only on contradiction, EPV). Recorded here so the
|
|
43
|
+
roadmap item is closed by decision, not oversight.
|
|
44
|
+
|
|
3
45
|
## [2.5.17] — 2026-07-19
|
|
4
46
|
|
|
5
47
|
**Semantic Stability cleanup: dead code removed and a scanner fork collapsed, with
|
|
@@ -363,6 +363,98 @@ def _recover_source_endpoints(
|
|
|
363
363
|
return routes, total
|
|
364
364
|
|
|
365
365
|
|
|
366
|
+
def _recover_spec_endpoints(
|
|
367
|
+
root: Path,
|
|
368
|
+
existing_keys: "set[tuple[Any, Any]]",
|
|
369
|
+
catalog: "dict[str, Any]",
|
|
370
|
+
custom_used: "set[str]",
|
|
371
|
+
) -> "tuple[Optional[str], list[dict[str, Any]], int]":
|
|
372
|
+
"""Recover validation routes from a standalone OpenAPI spec on disk.
|
|
373
|
+
|
|
374
|
+
F1: ``extract_java_endpoints`` only links a spec when an openapi-generator
|
|
375
|
+
interface-controller (``@RestController implements XxxApi``) matches it. A
|
|
376
|
+
repo that ships a plain ``openapi.yml`` with no such controller therefore
|
|
377
|
+
reported "No OpenAPI spec found" — a confirmed false negative. This consults
|
|
378
|
+
the existing :func:`build_openapi_surface` producer directly so a discovered
|
|
379
|
+
spec's body operations contribute their declared constraints and flip the
|
|
380
|
+
"no spec" claim.
|
|
381
|
+
|
|
382
|
+
Returns ``(spec_path, routes, validated_field_count)``; ``spec_path`` is None
|
|
383
|
+
when no parseable spec with operations is present.
|
|
384
|
+
"""
|
|
385
|
+
from sourcecode.openapi_surface import build_openapi_surface
|
|
386
|
+
|
|
387
|
+
surface = build_openapi_surface(root)
|
|
388
|
+
if surface is None or not surface.operations:
|
|
389
|
+
return None, [], 0
|
|
390
|
+
|
|
391
|
+
routes: "list[dict[str, Any]]" = []
|
|
392
|
+
total = 0
|
|
393
|
+
for op in surface.operations:
|
|
394
|
+
if str(op.method).upper() not in ("POST", "PUT", "PATCH"):
|
|
395
|
+
continue
|
|
396
|
+
key = (op.method, op.path)
|
|
397
|
+
if key in existing_keys:
|
|
398
|
+
continue # already surfaced from source — dedupe by method+path
|
|
399
|
+
schema = surface.schemas.get(op.request_body_schema) if op.request_body_schema else None
|
|
400
|
+
constraints = [f.to_dict() for f in schema.fields] if schema is not None else []
|
|
401
|
+
validated = _validated_fields_from_constraints(constraints, catalog, custom_used)
|
|
402
|
+
total += len(validated)
|
|
403
|
+
route: "dict[str, Any]" = {
|
|
404
|
+
"method": op.method,
|
|
405
|
+
"path": op.path,
|
|
406
|
+
"controller": None,
|
|
407
|
+
"handler": op.operation_id or "(operation)",
|
|
408
|
+
"schema": op.request_body_schema,
|
|
409
|
+
"source": "openapi-spec",
|
|
410
|
+
"confidence": "high",
|
|
411
|
+
"validatedFields": validated,
|
|
412
|
+
}
|
|
413
|
+
routes.append(route)
|
|
414
|
+
existing_keys.add(key)
|
|
415
|
+
return surface.spec_path, routes, total
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _validated_fields_from_constraints(
|
|
419
|
+
constraints: "list[Any]",
|
|
420
|
+
catalog: "dict[str, Any]",
|
|
421
|
+
custom_used: "set[str]",
|
|
422
|
+
) -> "list[dict[str, Any]]":
|
|
423
|
+
"""Render a schema's constraint dicts into ``validatedFields`` entries.
|
|
424
|
+
|
|
425
|
+
Mirrors the built-in-rule + custom-validator linkage the main loop applies to
|
|
426
|
+
OpenAPI request bodies; shared so the standalone-spec path (F1) stays byte-
|
|
427
|
+
consistent with the interface-controller path."""
|
|
428
|
+
out: "list[dict[str, Any]]" = []
|
|
429
|
+
for fc in constraints:
|
|
430
|
+
if not isinstance(fc, dict):
|
|
431
|
+
continue
|
|
432
|
+
rules = _field_rules(fc)
|
|
433
|
+
customs: "list[dict[str, Any]]" = []
|
|
434
|
+
for ann in fc.get("extraAnnotations", []) or []:
|
|
435
|
+
cc = catalog.get(ann)
|
|
436
|
+
entry: "dict[str, Any]" = {"annotation": ann}
|
|
437
|
+
if cc is not None:
|
|
438
|
+
custom_used.add(ann)
|
|
439
|
+
if cc.validators:
|
|
440
|
+
entry["validators"] = cc.validators
|
|
441
|
+
if cc.message is not None:
|
|
442
|
+
entry["message"] = cc.message
|
|
443
|
+
entry["resolved"] = True
|
|
444
|
+
else:
|
|
445
|
+
entry["resolved"] = False
|
|
446
|
+
customs.append(entry)
|
|
447
|
+
if not rules and not customs:
|
|
448
|
+
continue
|
|
449
|
+
field_entry: "dict[str, Any]" = {"name": fc.get("name")}
|
|
450
|
+
if rules:
|
|
451
|
+
field_entry["rules"] = rules
|
|
452
|
+
if customs:
|
|
453
|
+
field_entry["customValidators"] = customs
|
|
454
|
+
out.append(field_entry)
|
|
455
|
+
return out
|
|
456
|
+
|
|
457
|
+
|
|
366
458
|
def _field_rules(fieldc: "dict[str, Any]") -> "list[dict[str, Any]]":
|
|
367
459
|
"""Render a constraint dict's built-in rules as a list of {kind, value}."""
|
|
368
460
|
rules: "list[dict[str, Any]]" = []
|
|
@@ -491,6 +583,36 @@ def build_validation_surface(
|
|
|
491
583
|
if spec_path:
|
|
492
584
|
result["openapi_spec"] = spec_path
|
|
493
585
|
else:
|
|
586
|
+
# No spec linked by extract_java_endpoints (no openapi-generator
|
|
587
|
+
# interface-controller matched). F1: consult the standalone-spec
|
|
588
|
+
# producer directly — a plain openapi.yml shipped in the repo is a real
|
|
589
|
+
# spec, so its body operations contribute constraints and flip the
|
|
590
|
+
# "no spec found" claim instead of reporting a false negative.
|
|
591
|
+
existing_keys = {(r.get("method"), r.get("path")) for r in out_endpoints}
|
|
592
|
+
spec_path2, spec_routes, spec_fields = _recover_spec_endpoints(
|
|
593
|
+
root, existing_keys, catalog, custom_used
|
|
594
|
+
)
|
|
595
|
+
if spec_path2 is not None:
|
|
596
|
+
result["openapi_spec"] = spec_path2
|
|
597
|
+
out_endpoints.extend(spec_routes)
|
|
598
|
+
total_validated_fields += spec_fields
|
|
599
|
+
spec_keys = {(r.get("method"), r.get("path")) for r in spec_routes}
|
|
600
|
+
gaps = [g for g in gaps if (g.get("method"), g.get("path")) not in spec_keys]
|
|
601
|
+
result["endpoints"] = out_endpoints
|
|
602
|
+
result["gaps"] = gaps
|
|
603
|
+
result["custom_validators"] = [catalog[k].to_dict() for k in sorted(catalog)]
|
|
604
|
+
result["summary"]["endpoints_with_body"] = len(out_endpoints)
|
|
605
|
+
result["summary"]["validated_fields"] = total_validated_fields
|
|
606
|
+
result["summary"]["custom_validators_linked"] = len(custom_used)
|
|
607
|
+
result["summary"]["gaps"] = len(gaps)
|
|
608
|
+
result["summary"]["spec_derived_routes"] = len(spec_routes)
|
|
609
|
+
result["note"] = (
|
|
610
|
+
"OpenAPI spec discovered on disk (standalone, not linked to a "
|
|
611
|
+
"generated interface-controller); body operations contributed "
|
|
612
|
+
"their declared constraints. Response shapes and non-body "
|
|
613
|
+
"operations are not modeled here."
|
|
614
|
+
)
|
|
615
|
+
return result
|
|
494
616
|
# No OpenAPI spec on disk / under target/generated-sources. Recover
|
|
495
617
|
# declarative constraints from the graph's DTO field nodes (bean-
|
|
496
618
|
# validation annotations on @Valid/@Validated handler bodies), so a
|
|
@@ -525,6 +647,12 @@ def build_validation_surface(
|
|
|
525
647
|
"partial; OpenAPI-carried constraints would be more complete."
|
|
526
648
|
)
|
|
527
649
|
else:
|
|
650
|
+
# Genuine absence — no spec on disk AND no in-repo DTO constraints.
|
|
651
|
+
# Emit a machine-readable zero_result_reason (F1) so a consumer can
|
|
652
|
+
# tell "we looked and found nothing" from "we did not look": an empty
|
|
653
|
+
# surface here is an attested negative, not a missing-validation
|
|
654
|
+
# finding.
|
|
655
|
+
result["zero_result_reason"] = "no_openapi_spec_and_no_source_dto_constraints"
|
|
528
656
|
result["note"] = (
|
|
529
657
|
"No OpenAPI spec found and no source DTO constraints recovered "
|
|
530
658
|
"(no handler validates an in-repo DTO via @Valid/@Validated, or "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|