Skip to content

Annotations Audit — Developer Reference

Developer-focused reference. For module overview, pages, and Drush commands see README.md.


CoverageService

CoverageService (annotations_audit.coverage_service) is the public API for coverage data. Any module that needs to act on coverage — workflow conditions, CI checks, export enrichment — injects this service directly.

$coverage = $coverageService->getCoverage();
// Returns: array<string, array{
//   target: AnnotationTargetInterface,
//   status: 'complete'|'partial'|'empty',
//   missing: array<string, string[]>  // [type_id => ['overview', 'field_name', ...]]
// }>

$entry = $coverageService->getCoverageForTarget($target);

$score = $coverageService->getScore($coverage);
// Returns: array{
//   complete: int, total: int, percent: int,
//   filled_tracked: int, total_tracked: int,
//   filled_optional: int, total_optional: int,
// }

$affects = $coverageService->affectsCoverage($annotationType);

Coverage statuses:

Status Meaning
complete All status-affecting annotation types filled at target level and for all included fields
partial Target-level primary type filled, but gaps remain at field or secondary type level
empty Primary status-affecting type is blank at target level

When annotations_workflows is installed, only published annotations count as filled. Without it, the published filter is a no-op.


ScanService

ScanService (annotations_audit.scan_service) handles snapshot persistence and diff logic. Inject it directly to build on the audit scan data.

// Run discovery — returns array<target_id, data>
$result = $scanService->scan();

// Snapshot
$scanService->saveSnapshot($result);
$stored = $scanService->loadSnapshot();

// Diff
$diff = $scanService->computeDiff($current, $stored);
// Returns: ['added' => ..., 'removed' => ..., 'changed' => ...]

$hasChanges = $scanService->diffHasChanges($diff);
$timestamp  = $scanService->getLastScanTimestamp(); // ?int

// Accumulated changes (since last waypoint)
$changes = $scanService->getAccumulatedChanges();
$scanService->clearAccumulatedChanges();
$scanService->mergeNewChanges($diff); // returns only newly added entries

// Scope drift — fields in Drupal but not in any target's scope
$drift = $scanService->getScopeDrift();
// Returns: array<target_id, string[]>

Each scan entry is keyed by {entity_type}__{bundle}:

[
  'entity_type' => 'node',
  'label'       => 'Article',
  'bundle'      => 'article',
  'fields'      => [
    'field_body' => ['label' => 'Body', 'type' => 'text_long', 'required' => false, 'cardinality' => 1, 'description' => ''],
  ],
]

Performance note

getCoverage() executes one DB query per target. At 100+ targets this becomes significant on every page load. The intended fix at scale is cron-driven snapshot storage with cached results. Deferred until target counts warrant it.