AboutController.java

package com.taxonomy.shared.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * REST API for application version, build info, license, and third-party notices.
 */
@RestController
@RequestMapping("/api/about")
@Tag(name = "About")
public class AboutController {

    private static final Logger log = LoggerFactory.getLogger(AboutController.class);

    private final BuildProperties buildProperties;
    private final GitProperties gitProperties;

    @Autowired(required = false)
    public AboutController(BuildProperties buildProperties, GitProperties gitProperties) {
        this.buildProperties = buildProperties;
        this.gitProperties = gitProperties;
    }

    public AboutController() {
        this.buildProperties = null;
        this.gitProperties = null;
    }

    @GetMapping
    @Operation(summary = "Application info", description = "Returns version, build time, git commit, license, and copyright information.")
    public ResponseEntity<Map<String, Object>> about() {
        Map<String, Object> info = new LinkedHashMap<>();
        info.put("product", "Taxonomy Architecture Analyzer");
        info.put("version", buildProperties != null ? buildProperties.getVersion() : "unknown");
        info.put("buildTime", buildProperties != null ? buildProperties.getTime() : null);
        info.put("commit", gitProperties != null ? gitProperties.getShortCommitId() : "unknown");
        info.put("commitTime", gitProperties != null ? gitProperties.getCommitTime() : null);
        info.put("branch", gitProperties != null ? gitProperties.getBranch() : "unknown");
        info.put("license", "MIT");
        info.put("copyright", "Copyright 2026 Carsten Hammer");
        info.put("sourceUrl", "https://github.com/carstenartur/Taxonomy");
        info.put("apiDocsUrl", "/swagger-ui.html");
        info.put("thirdPartyNoticesUrl", "/THIRD-PARTY-NOTICES.md");
        return ResponseEntity.ok(info);
    }

    @GetMapping("/third-party")
    @Operation(summary = "Third-party notices", description = "Returns the content of THIRD-PARTY-NOTICES.md.")
    public ResponseEntity<String> thirdParty() {
        try {
            ClassPathResource resource = new ClassPathResource("static/THIRD-PARTY-NOTICES.md");
            try (InputStream is = resource.getInputStream()) {
                String content = new String(is.readAllBytes(), StandardCharsets.UTF_8);
                return ResponseEntity.ok()
                        .contentType(MediaType.parseMediaType("text/markdown;charset=UTF-8"))
                        .body(content);
            }
        } catch (IOException e) {
            log.warn("THIRD-PARTY-NOTICES.md not found on classpath: {}", e.getMessage());
            return ResponseEntity.ok()
                    .contentType(MediaType.TEXT_PLAIN)
                    .body("Third-party notices file not available.");
        }
    }
}