Skip to content

DbBackupClient#getInfo(): repoint to /api/v1/info and make lastSuccessfulBackupAgeSeconds nullable #23

Description

@herbie-bot

Summary

DbBackupClient#getInfo() reads the backup service's Spring Boot Actuator /info endpoint and binds it directly onto the BackupServiceInfo record. That's the wrong contract: Actuator /info is an ops/monitoring endpoint with a Spring-Boot-conventional shape (app, build, custom InfoContributors), not a stable API for programmatic consumers. The fields the record expects (version, pgDumpVersion, retention, backupInterval) don't exist at the top level of Actuator /info, so Jackson leaves them null.

Counterpart issue (service side, adds the dedicated endpoint): OpenElementsLabs/db-backup-service#1

Two changes

1. Repoint getInfo() to the dedicated programmatic endpoint

Once db-backup-service ships GET /api/v1/info (see linked issue), change DbBackupClient#getInfo():

  • path: GET /infoGET /api/v1/info
  • send the Bearer token (the new endpoint is authenticated, like /api/v1/backups*; the current /info actuator call is unauthenticated)

The /api/v1/info contract the record binds to:

{
  "version": "0.1.1",
  "pgDumpVersion": "17.10",
  "retention":      { "days": 7 },
  "backupInterval": { "iso8601": "PT24H", "seconds": 86400 },
  "backup":         { "lastSuccessfulBackupAgeSeconds": null }
}

2. Make lastSuccessfulBackupAgeSeconds nullable

BackupServiceInfo.Backup#lastSuccessfulBackupAgeSeconds is currently a primitive long. The service legitimately returns null for it when no successful backup has run yet — a primitive can't represent that and forces a misleading 0 (or a deserialization failure depending on Jackson config). Change it to a nullable type (Long, or expose via OptionalLong/Optional<Duration>), and update consumers accordingly.

Impact

A downstream consumer (Open CRM admin "Backup" page) crashed with:

TypeError: null is not an object (evaluating 'info.retention.days')

because getInfo() returned a BackupServiceInfo with retention == null, backupInterval == null, version == null. The consumer UI has been hardened to degrade gracefully, but the panel stays empty until this client is fixed and repointed.

Acceptance criteria

  • DbBackupClient#getInfo() calls GET /api/v1/info with Bearer auth
  • BackupServiceInfo.Backup#lastSuccessfulBackupAgeSeconds is nullable / represents "no backup yet"
  • Test coverage for the "no successful backup yet" (null) case
  • Released so consumers can bump the spring-services pin

Ordering

This depends on OpenElementsLabs/db-backup-service#1 shipping /api/v1/info first. The nullable-type change (2) can land independently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions