implement PR-19 api read dto separation and model mapping

This commit is contained in:
bQUARKz 2026-03-16 06:56:17 +00:00
parent 59e1ad5ecf
commit 2d923455f5
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
24 changed files with 264 additions and 48 deletions

View File

@ -0,0 +1,28 @@
package p.packer.assets;
import p.packer.diagnostics.PackerDiagnosticDTO;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public record PackerAssetDetailsDTO(
PackerAssetSummaryDTO summary,
String outputFormat,
OutputCodecCatalog outputCodec,
List<OutputCodecCatalog> availableOutputCodecs,
Map<OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> codecConfigurationFieldsByCodec,
Map<String, List<Path>> inputsByRole,
List<PackerDiagnosticDTO> diagnostics) {
public PackerAssetDetailsDTO {
Objects.requireNonNull(summary, "summary");
outputFormat = Objects.requireNonNullElse(outputFormat, "unknown").trim();
outputCodec = Objects.requireNonNullElse(outputCodec, OutputCodecCatalog.UNKNOWN);
availableOutputCodecs = List.copyOf(Objects.requireNonNull(availableOutputCodecs, "availableOutputCodecs"));
codecConfigurationFieldsByCodec = Map.copyOf(Objects.requireNonNull(codecConfigurationFieldsByCodec, "codecConfigurationFieldsByCodec"));
inputsByRole = Map.copyOf(Objects.requireNonNull(inputsByRole, "inputsByRole"));
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
}
}

View File

@ -0,0 +1,20 @@
package p.packer.assets;
import java.nio.file.Path;
import java.util.Objects;
public record PackerAssetIdentityDTO(
Integer assetId,
String assetUuid,
String assetName,
Path assetRoot) {
public PackerAssetIdentityDTO {
assetUuid = assetUuid == null ? null : assetUuid.trim();
assetName = Objects.requireNonNull(assetName, "assetName").trim();
assetRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
if (assetName.isBlank()) {
throw new IllegalArgumentException("assetName must not be blank");
}
}
}

View File

@ -0,0 +1,27 @@
package p.packer.assets;
import java.util.Objects;
public record PackerAssetSummaryDTO(
AssetReference assetReference,
PackerAssetIdentityDTO identity,
PackerAssetState state,
PackerBuildParticipation buildParticipation,
AssetFamilyCatalog assetFamily,
boolean preloadEnabled,
boolean hasDiagnostics) {
public PackerAssetSummaryDTO {
Objects.requireNonNull(assetReference, "assetReference");
Objects.requireNonNull(identity, "identity");
Objects.requireNonNull(state, "state");
Objects.requireNonNull(buildParticipation, "buildParticipation");
assetFamily = Objects.requireNonNullElse(assetFamily, AssetFamilyCatalog.UNKNOWN);
if (state == PackerAssetState.REGISTERED && identity.assetId() == null) {
throw new IllegalArgumentException("registered asset must expose assetId");
}
if (state == PackerAssetState.UNREGISTERED && buildParticipation != PackerBuildParticipation.EXCLUDED) {
throw new IllegalArgumentException("unregistered asset must stay excluded from build participation");
}
}
}

View File

@ -0,0 +1,24 @@
package p.packer.assets;
import java.util.List;
import java.util.Objects;
public record PackerCodecConfigurationFieldDTO(
String key,
String label,
PackerCodecConfigurationFieldType fieldType,
String value,
boolean required,
List<String> options) {
public PackerCodecConfigurationFieldDTO {
key = Objects.requireNonNull(key, "key").trim();
label = Objects.requireNonNull(label, "label").trim();
Objects.requireNonNull(fieldType, "fieldType");
value = Objects.requireNonNullElse(value, "").trim();
options = List.copyOf(Objects.requireNonNull(options, "options"));
if (key.isBlank() || label.isBlank()) {
throw new IllegalArgumentException("codec configuration field key and label must not be blank");
}
}
}

View File

@ -0,0 +1,22 @@
package p.packer.diagnostics;
import java.nio.file.Path;
import java.util.Objects;
public record PackerDiagnosticDTO(
PackerDiagnosticSeverity severity,
PackerDiagnosticCategory category,
String message,
Path evidencePath,
boolean blocking) {
public PackerDiagnosticDTO {
Objects.requireNonNull(severity, "severity");
Objects.requireNonNull(category, "category");
message = Objects.requireNonNull(message, "message").trim();
evidencePath = evidencePath == null ? null : evidencePath.toAbsolutePath().normalize();
if (message.isBlank()) {
throw new IllegalArgumentException("message must not be blank");
}
}
}

View File

@ -1,8 +1,8 @@
package p.packer.messages;
import p.packer.PackerOperationStatus;
import p.packer.assets.PackerAssetDetails;
import p.packer.diagnostics.PackerDiagnostic;
import p.packer.assets.PackerAssetDetailsDTO;
import p.packer.diagnostics.PackerDiagnosticDTO;
import java.util.List;
import java.util.Objects;
@ -10,8 +10,8 @@ import java.util.Objects;
public record GetAssetDetailsResult(
PackerOperationStatus status,
String summary,
PackerAssetDetails details,
List<PackerDiagnostic> diagnostics) {
PackerAssetDetailsDTO details,
List<PackerDiagnosticDTO> diagnostics) {
public GetAssetDetailsResult {
Objects.requireNonNull(status, "status");

View File

@ -1,7 +1,7 @@
package p.packer.messages;
import p.packer.PackerOperationStatus;
import p.packer.diagnostics.PackerDiagnostic;
import p.packer.diagnostics.PackerDiagnosticDTO;
import java.nio.file.Path;
import java.util.List;
@ -11,7 +11,7 @@ public record InitWorkspaceResult(
PackerOperationStatus status,
String summary,
Path registryPath,
List<PackerDiagnostic> diagnostics) {
List<PackerDiagnosticDTO> diagnostics) {
public InitWorkspaceResult {
Objects.requireNonNull(status, "status");

View File

@ -1,8 +1,8 @@
package p.packer.messages;
import p.packer.PackerOperationStatus;
import p.packer.assets.PackerAssetSummary;
import p.packer.diagnostics.PackerDiagnostic;
import p.packer.assets.PackerAssetSummaryDTO;
import p.packer.diagnostics.PackerDiagnosticDTO;
import java.util.List;
import java.util.Objects;
@ -10,8 +10,8 @@ import java.util.Objects;
public record ListAssetsResult(
PackerOperationStatus status,
String summary,
List<PackerAssetSummary> assets,
List<PackerDiagnostic> diagnostics) {
List<PackerAssetSummaryDTO> assets,
List<PackerDiagnosticDTO> diagnostics) {
public ListAssetsResult {
Objects.requireNonNull(status, "status");

View File

@ -1,7 +1,5 @@
package p.packer.models;
import p.packer.diagnostics.PackerDiagnostic;
import java.util.List;
import java.util.Objects;

View File

@ -1,6 +1,6 @@
package p.packer.assets;
package p.packer.models;
import p.packer.diagnostics.PackerDiagnostic;
import p.packer.assets.OutputCodecCatalog;
import java.nio.file.Path;
import java.util.List;

View File

@ -1,4 +1,4 @@
package p.packer.assets;
package p.packer.models;
import java.nio.file.Path;
import java.util.Objects;

View File

@ -1,4 +1,9 @@
package p.packer.assets;
package p.packer.models;
import p.packer.assets.AssetFamilyCatalog;
import p.packer.assets.AssetReference;
import p.packer.assets.PackerAssetState;
import p.packer.assets.PackerBuildParticipation;
import java.util.Objects;

View File

@ -1,4 +1,6 @@
package p.packer.assets;
package p.packer.models;
import p.packer.assets.PackerCodecConfigurationFieldType;
import java.util.List;
import java.util.Objects;

View File

@ -1,4 +1,7 @@
package p.packer.diagnostics;
package p.packer.models;
import p.packer.diagnostics.PackerDiagnosticCategory;
import p.packer.diagnostics.PackerDiagnosticSeverity;
import java.nio.file.Path;
import java.util.Objects;

View File

@ -6,11 +6,8 @@ import p.packer.PackerProjectContext;
import p.packer.assets.AssetFamilyCatalog;
import p.packer.assets.AssetReference;
import p.packer.assets.OutputFormatCatalog;
import p.packer.assets.PackerAssetIdentity;
import p.packer.assets.PackerAssetState;
import p.packer.assets.PackerAssetSummary;
import p.packer.assets.PackerBuildParticipation;
import p.packer.diagnostics.PackerDiagnostic;
import p.packer.diagnostics.PackerDiagnosticCategory;
import p.packer.diagnostics.PackerDiagnosticSeverity;
import p.packer.events.PackerEventKind;
@ -26,6 +23,9 @@ import p.packer.messages.ListAssetsRequest;
import p.packer.messages.ListAssetsResult;
import p.packer.PackerWorkspaceService;
import p.packer.models.PackerAssetDeclarationParseResult;
import p.packer.models.PackerAssetIdentity;
import p.packer.models.PackerAssetSummary;
import p.packer.models.PackerDiagnostic;
import p.packer.models.PackerRegistryEntry;
import p.packer.models.PackerRegistryState;
import p.packer.models.PackerRuntimeAsset;
@ -124,8 +124,8 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
return new ListAssetsResult(
status,
"Packer asset snapshot ready.",
assets,
diagnostics);
PackerReadMessageMapper.toAssetSummaryDTOs(assets),
PackerReadMessageMapper.toDiagnosticDTOs(diagnostics));
}
@Override

View File

@ -4,11 +4,11 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import p.packer.assets.AssetFamilyCatalog;
import p.packer.assets.OutputCodecCatalog;
import p.packer.diagnostics.PackerDiagnostic;
import p.packer.diagnostics.PackerDiagnosticCategory;
import p.packer.diagnostics.PackerDiagnosticSeverity;
import p.packer.models.PackerAssetDeclaration;
import p.packer.models.PackerAssetDeclarationParseResult;
import p.packer.models.PackerDiagnostic;
import java.io.IOException;
import java.nio.file.Path;

View File

@ -5,18 +5,18 @@ import p.packer.PackerProjectContext;
import p.packer.assets.AssetFamilyCatalog;
import p.packer.assets.AssetReference;
import p.packer.assets.OutputCodecCatalog;
import p.packer.assets.PackerAssetDetails;
import p.packer.assets.PackerAssetIdentity;
import p.packer.assets.PackerAssetState;
import p.packer.assets.PackerAssetSummary;
import p.packer.assets.PackerBuildParticipation;
import p.packer.diagnostics.PackerDiagnostic;
import p.packer.diagnostics.PackerDiagnosticCategory;
import p.packer.diagnostics.PackerDiagnosticSeverity;
import p.packer.messages.GetAssetDetailsRequest;
import p.packer.messages.GetAssetDetailsResult;
import p.packer.models.PackerAssetDeclaration;
import p.packer.models.PackerAssetDeclarationParseResult;
import p.packer.models.PackerAssetDetails;
import p.packer.models.PackerAssetIdentity;
import p.packer.models.PackerAssetSummary;
import p.packer.models.PackerDiagnostic;
import p.packer.models.PackerRegistryEntry;
import p.packer.models.PackerRuntimeAsset;
import p.packer.models.PackerRuntimeSnapshot;
@ -93,8 +93,8 @@ public final class PackerAssetDetailsService {
return new GetAssetDetailsResult(
diagnostics.stream().anyMatch(PackerDiagnostic::blocking) ? PackerOperationStatus.PARTIAL : PackerOperationStatus.SUCCESS,
"Asset details resolved from runtime snapshot.",
details,
diagnostics);
PackerReadMessageMapper.toAssetDetailsDTO(details),
PackerReadMessageMapper.toDiagnosticDTOs(diagnostics));
}
private GetAssetDetailsResult failureResult(
@ -130,8 +130,8 @@ public final class PackerAssetDetailsService {
return new GetAssetDetailsResult(
PackerOperationStatus.FAILED,
"Asset declaration is invalid or unreadable.",
details,
diagnostics);
PackerReadMessageMapper.toAssetDetailsDTO(details),
PackerReadMessageMapper.toDiagnosticDTOs(diagnostics));
}
private Map<String, List<Path>> resolveInputs(Path assetRoot, Map<String, List<String>> inputsByRole) {

View File

@ -2,13 +2,13 @@ package p.packer.services;
import p.packer.assets.OutputCodecCatalog;
import p.packer.assets.OutputFormatCatalog;
import p.packer.models.PackerCodecConfigurationField;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import p.packer.assets.PackerCodecConfigurationField;
final class PackerOutputContractCatalog {
private PackerOutputContractCatalog() {

View File

@ -0,0 +1,87 @@
package p.packer.services;
import p.packer.assets.PackerAssetDetailsDTO;
import p.packer.assets.PackerAssetIdentityDTO;
import p.packer.assets.PackerAssetSummaryDTO;
import p.packer.assets.PackerCodecConfigurationFieldDTO;
import p.packer.diagnostics.PackerDiagnosticDTO;
import p.packer.models.PackerAssetDetails;
import p.packer.models.PackerAssetIdentity;
import p.packer.models.PackerAssetSummary;
import p.packer.models.PackerCodecConfigurationField;
import p.packer.models.PackerDiagnostic;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public final class PackerReadMessageMapper {
private PackerReadMessageMapper() {
}
public static PackerAssetSummaryDTO toAssetSummaryDTO(PackerAssetSummary summary) {
return new PackerAssetSummaryDTO(
summary.assetReference(),
toAssetIdentityDTO(summary.identity()),
summary.state(),
summary.buildParticipation(),
summary.assetFamily(),
summary.preloadEnabled(),
summary.hasDiagnostics());
}
public static PackerAssetDetailsDTO toAssetDetailsDTO(PackerAssetDetails details) {
return new PackerAssetDetailsDTO(
toAssetSummaryDTO(details.summary()),
details.outputFormat(),
details.outputCodec(),
details.availableOutputCodecs(),
toCodecConfigurationFieldsByCodecDTO(details.codecConfigurationFieldsByCodec()),
details.inputsByRole(),
toDiagnosticDTOs(details.diagnostics()));
}
public static List<PackerAssetSummaryDTO> toAssetSummaryDTOs(List<PackerAssetSummary> summaries) {
return summaries.stream().map(PackerReadMessageMapper::toAssetSummaryDTO).toList();
}
public static List<PackerDiagnosticDTO> toDiagnosticDTOs(List<PackerDiagnostic> diagnostics) {
return diagnostics.stream().map(PackerReadMessageMapper::toDiagnosticDTO).toList();
}
private static PackerAssetIdentityDTO toAssetIdentityDTO(PackerAssetIdentity identity) {
return new PackerAssetIdentityDTO(
identity.assetId(),
identity.assetUuid(),
identity.assetName(),
identity.assetRoot());
}
private static Map<p.packer.assets.OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> toCodecConfigurationFieldsByCodecDTO(
Map<p.packer.assets.OutputCodecCatalog, List<PackerCodecConfigurationField>> fieldsByCodec) {
return fieldsByCodec.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().stream().map(PackerReadMessageMapper::toCodecConfigurationFieldDTO).toList(),
(left, right) -> left,
java.util.LinkedHashMap::new));
}
private static PackerCodecConfigurationFieldDTO toCodecConfigurationFieldDTO(PackerCodecConfigurationField field) {
return new PackerCodecConfigurationFieldDTO(
field.key(),
field.label(),
field.fieldType(),
field.value(),
field.required(),
field.options());
}
private static PackerDiagnosticDTO toDiagnosticDTO(PackerDiagnostic diagnostic) {
return new PackerDiagnosticDTO(
diagnostic.severity(),
diagnostic.category(),
diagnostic.message(),
diagnostic.evidencePath(),
diagnostic.blocking());
}
}

View File

@ -7,7 +7,7 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import p.packer.assets.AssetReference;
import p.packer.assets.PackerAssetDetails;
import p.packer.assets.PackerAssetDetailsDTO;
import p.packer.messages.GetAssetDetailsRequest;
import p.studio.Container;
import p.studio.events.StudioWorkspaceEventBus;
@ -244,7 +244,7 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
workspaceSummaryLabel.setManaged(visible);
}
private AssetWorkspaceAssetDetails mapDetails(PackerAssetDetails details) {
private AssetWorkspaceAssetDetails mapDetails(PackerAssetDetailsDTO details) {
return new AssetWorkspaceAssetDetails(
AssetListPackerMappings.mapSummary(details.summary()),
details.outputFormat(),

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.details;
import p.packer.assets.PackerAssetSummary;
import p.packer.assets.PackerAssetSummaryDTO;
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetSummary;
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetState;
import p.studio.workspaces.assets.messages.AssetWorkspaceBuildParticipation;
@ -9,7 +9,7 @@ public final class AssetListPackerMappings {
private AssetListPackerMappings() {
}
public static AssetWorkspaceAssetSummary mapSummary(PackerAssetSummary summary) {
public static AssetWorkspaceAssetSummary mapSummary(PackerAssetSummaryDTO summary) {
final AssetWorkspaceAssetState state = switch (summary.state()) {
case REGISTERED -> AssetWorkspaceAssetState.REGISTERED;
case UNREGISTERED -> AssetWorkspaceAssetState.UNREGISTERED;

View File

@ -11,7 +11,7 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import p.packer.assets.OutputCodecCatalog;
import p.packer.assets.PackerCodecConfigurationField;
import p.packer.assets.PackerCodecConfigurationFieldDTO;
import p.studio.Container;
import p.studio.controls.forms.StudioFormActionBar;
import p.studio.controls.forms.StudioFormMode;
@ -211,7 +211,7 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
return createCodecMetadataPane(title, content);
}
for (PackerCodecConfigurationField field : fields) {
for (PackerCodecConfigurationFieldDTO field : fields) {
content.getChildren().add(createCodecFieldRow(field, draft, editing));
}
return createCodecMetadataPane(title, content);
@ -230,7 +230,7 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
}
private Node createCodecFieldRow(
PackerCodecConfigurationField field,
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final Node valueNode = switch (field.fieldType()) {
@ -242,7 +242,7 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
}
private Node createBooleanField(
PackerCodecConfigurationField field,
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final CheckBox checkBox = new CheckBox();
@ -265,7 +265,7 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
}
private Node createEnumField(
PackerCodecConfigurationField field,
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final ComboBox<String> comboBox = new ComboBox<>(FXCollections.observableArrayList(field.options()));
@ -289,7 +289,7 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
}
private Node createTextField(
PackerCodecConfigurationField field,
PackerCodecConfigurationFieldDTO field,
AssetContractDraft draft,
boolean editing) {
final TextField textField = new TextField(currentValue(field, draft));
@ -313,7 +313,7 @@ public final class AssetDetailsContractControl extends VBox implements StudioCon
return textField;
}
private String currentValue(PackerCodecConfigurationField field, AssetContractDraft draft) {
private String currentValue(PackerCodecConfigurationFieldDTO field, AssetContractDraft draft) {
return draft.codecFieldValue(draft.selectedCodec(), field.key(), field.value());
}

View File

@ -11,7 +11,7 @@ import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import p.packer.assets.AssetReference;
import p.packer.assets.PackerAssetSummary;
import p.packer.assets.PackerAssetSummaryDTO;
import p.packer.messages.ListAssetsRequest;
import p.studio.Container;
import p.studio.events.StudioWorkspaceEventBus;
@ -233,7 +233,7 @@ public final class AssetListControl extends VBox implements StudioEventAware {
workspaceBus.publish(new StudioAssetsNavigatorViewStateChangedEvent(nextViewState));
}
private AssetWorkspaceAssetSummary mapAsset(PackerAssetSummary summary) {
private AssetWorkspaceAssetSummary mapAsset(PackerAssetSummaryDTO summary) {
return AssetListPackerMappings.mapSummary(summary);
}

View File

@ -1,6 +1,6 @@
package p.studio.workspaces.assets.messages;
import p.packer.assets.PackerCodecConfigurationField;
import p.packer.assets.PackerCodecConfigurationFieldDTO;
import p.packer.assets.OutputCodecCatalog;
import java.nio.file.Path;
@ -13,7 +13,7 @@ public record AssetWorkspaceAssetDetails(
String outputFormat,
OutputCodecCatalog outputCodec,
List<OutputCodecCatalog> availableOutputCodecs,
Map<OutputCodecCatalog, List<PackerCodecConfigurationField>> codecConfigurationFieldsByCodec,
Map<OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> codecConfigurationFieldsByCodec,
Map<String, List<Path>> inputsByRole) {
public AssetWorkspaceAssetDetails {