CoverageApiController.java
package com.taxonomy.relations.controller;
import com.taxonomy.dto.CoverageStatistics;
import com.taxonomy.relations.model.RequirementCoverage;
import com.taxonomy.relations.service.RequirementCoverageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* REST API for the Requirement Coverage feature.
*
* <p>Endpoints:
* <ul>
* <li>{@code POST /api/coverage/record} — record coverage from an analysis result</li>
* <li>{@code GET /api/coverage/node/{code}} — requirements covering a specific node</li>
* <li>{@code GET /api/coverage/requirement/{id}} — nodes covered by a requirement</li>
* <li>{@code GET /api/coverage/statistics} — overall coverage statistics</li>
* <li>{@code GET /api/coverage/density} — requirement density map</li>
* <li>{@code DELETE /api/coverage/requirement/{id}} — remove coverage for a requirement</li>
* </ul>
*/
@RestController
@RequestMapping("/api/coverage")
@Tag(name = "Requirement Coverage")
public class CoverageApiController {
private final RequirementCoverageService coverageService;
public CoverageApiController(RequirementCoverageService coverageService) {
this.coverageService = coverageService;
}
/**
* Request body for recording coverage from an analysis result.
*
* @param requirementId identifier for the requirement (e.g. "REQ-101")
* @param requirementText original business text
* @param scores map of nodeCode → score (0–100)
* @param minScore minimum score threshold (inclusive); 0 means use the default (50)
*/
public record RecordCoverageRequest(
String requirementId,
String requirementText,
Map<String, Integer> scores,
int minScore) {
}
/**
* Records coverage from an analysis result. Scores below {@code minScore} are discarded.
*/
@Operation(summary = "Record coverage",
description = "Stores nodeCode→score mappings above the threshold for a requirement")
@PostMapping("/record")
public ResponseEntity<Void> recordCoverage(@RequestBody RecordCoverageRequest request) {
coverageService.analyzeCoverage(
request.scores() != null ? request.scores() : Map.of(),
request.requirementId(),
request.requirementText(),
request.minScore());
return ResponseEntity.ok().build();
}
/**
* Returns all requirements that cover a given taxonomy node code.
*/
@Operation(summary = "Coverage for node",
description = "Returns all requirements covering a specific node code")
@GetMapping("/node/{code}")
public ResponseEntity<List<RequirementCoverage>> getCoverageForNode(
@Parameter(description = "Taxonomy node code") @PathVariable String code) {
return ResponseEntity.ok(coverageService.getCoverageForNode(code));
}
/**
* Returns all nodes covered by a specific requirement.
*/
@Operation(summary = "Coverage for requirement",
description = "Returns all nodes covered by a specific requirement ID")
@GetMapping("/requirement/{id}")
public ResponseEntity<List<RequirementCoverage>> getCoverageForRequirement(
@Parameter(description = "Requirement identifier") @PathVariable String id) {
return ResponseEntity.ok(coverageService.getCoverageForRequirement(id));
}
/**
* Returns overall coverage statistics for the taxonomy.
*/
@Operation(summary = "Coverage statistics",
description = "Returns overall coverage statistics: totals, percentages, top-covered, and gap candidates")
@GetMapping("/statistics")
public ResponseEntity<CoverageStatistics> getCoverageStatistics() {
return ResponseEntity.ok(coverageService.getCoverageStatistics());
}
/**
* Returns the requirement density map (nodeCode → requirementCount).
*/
@Operation(summary = "Density map",
description = "Returns a map of nodeCode → requirementCount for heatmap visualisation")
@GetMapping("/density")
public ResponseEntity<Map<String, Integer>> getDensityMap() {
return ResponseEntity.ok(coverageService.getRequirementDensityMap());
}
/**
* Removes all coverage records for the given requirement ID.
*/
@Operation(summary = "Delete coverage",
description = "Removes all coverage records for a given requirement ID")
@DeleteMapping("/requirement/{id}")
public ResponseEntity<Void> deleteCoverage(
@Parameter(description = "Requirement identifier") @PathVariable String id) {
coverageService.deleteCoverageForRequirement(id);
return ResponseEntity.noContent().build();
}
}