LocalDeploymentCatalog.java
package com.cloudforge.core.local;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
/**
* Resolves {@code applicationId} from per-stack catalog files under {@code deployment-contexts/}.
*/
public final class LocalDeploymentCatalog {
private static final ObjectMapper MAPPER = new ObjectMapper();
private LocalDeploymentCatalog() {
}
/**
* Logical stack name from a CFN stack name (strips {@code -ministack} / {@code -localstack}).
*/
public static String logicalStackName(String cfnStackName, DeploymentTarget target) {
if (cfnStackName == null || cfnStackName.isBlank()) {
return cfnStackName;
}
String suffix = switch (target) {
case MINISTACK -> "-ministack";
case LOCALSTACK -> "-localstack";
case AWS -> "";
};
if (!suffix.isEmpty() && cfnStackName.endsWith(suffix)) {
return cfnStackName.substring(0, cfnStackName.length() - suffix.length());
}
return cfnStackName;
}
/**
* Read {@code applicationId} from {@code catalogDirectory/{logicalStack}.json} when present.
*/
public static Optional<String> applicationIdForCfnStack(
Path catalogDirectory,
String cfnStackName,
DeploymentTarget target) {
if (catalogDirectory == null || cfnStackName == null || cfnStackName.isBlank()) {
return Optional.empty();
}
if (!Files.isDirectory(catalogDirectory)) {
return Optional.empty();
}
String logical = logicalStackName(cfnStackName, target);
Path file = catalogDirectory.resolve(logical + ".json");
if (!Files.isRegularFile(file)) {
return Optional.empty();
}
try {
@SuppressWarnings("unchecked")
Map<String, Object> map = MAPPER.readValue(file.toFile(), Map.class);
Object id = map.get("applicationId");
return id == null || id.toString().isBlank()
? Optional.empty()
: Optional.of(id.toString().trim());
} catch (IOException e) {
return Optional.empty();
}
}
}