implements packer PR-09 event lane and studio adapter
This commit is contained in:
parent
682a0e72b5
commit
1c418a454b
@ -6,6 +6,10 @@ import p.packer.api.building.PackerBuildRequest;
|
||||
import p.packer.api.building.PackerBuildResult;
|
||||
import p.packer.api.building.PackerBuildService;
|
||||
import p.packer.api.diagnostics.PackerDiagnostic;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.packer.api.events.PackerEventSink;
|
||||
import p.packer.api.events.PackerProgress;
|
||||
import p.packer.events.PackerOperationEventEmitter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -27,13 +31,19 @@ public final class FileSystemPackerBuildService implements PackerBuildService {
|
||||
private static final int PRELUDE_SIZE = 24;
|
||||
|
||||
private final PackerBuildPlanner buildPlanner;
|
||||
private final PackerEventSink eventSink;
|
||||
|
||||
public FileSystemPackerBuildService() {
|
||||
this(new PackerBuildPlanner());
|
||||
this(new PackerBuildPlanner(), PackerEventSink.noop());
|
||||
}
|
||||
|
||||
public FileSystemPackerBuildService(PackerBuildPlanner buildPlanner) {
|
||||
this(buildPlanner, PackerEventSink.noop());
|
||||
}
|
||||
|
||||
public FileSystemPackerBuildService(PackerBuildPlanner buildPlanner, PackerEventSink eventSink) {
|
||||
this.buildPlanner = Objects.requireNonNull(buildPlanner, "buildPlanner");
|
||||
this.eventSink = Objects.requireNonNull(eventSink, "eventSink");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -43,14 +53,17 @@ public final class FileSystemPackerBuildService implements PackerBuildService {
|
||||
|
||||
@Override
|
||||
public PackerBuildResult build(PackerBuildRequest request) {
|
||||
final Path buildDirectory = Objects.requireNonNull(request, "request").project().rootPath().resolve("build");
|
||||
final PackerBuildRequest buildRequest = Objects.requireNonNull(request, "request");
|
||||
final PackerOperationEventEmitter events = new PackerOperationEventEmitter(buildRequest.project(), eventSink);
|
||||
final Path buildDirectory = buildRequest.project().rootPath().resolve("build");
|
||||
final Path assetsArchive = buildDirectory.resolve("assets.pa").toAbsolutePath().normalize();
|
||||
final Path assetTableJson = buildDirectory.resolve("asset_table.json").toAbsolutePath().normalize();
|
||||
final Path preloadJson = buildDirectory.resolve("preload.json").toAbsolutePath().normalize();
|
||||
final Path metadataJson = buildDirectory.resolve("asset_table_metadata.json").toAbsolutePath().normalize();
|
||||
|
||||
final PackerBuildPlanResult planResult = buildPlanner.plan(request.project());
|
||||
events.emit(PackerEventKind.BUILD_STARTED, "Build started.", new PackerProgress(0.0d, false), List.of());
|
||||
final PackerBuildPlanResult planResult = buildPlanner.plan(buildRequest.project());
|
||||
if (planResult.plan() == null) {
|
||||
events.emit(PackerEventKind.BUILD_FINISHED, planResult.summary(), new PackerProgress(1.0d, false), List.of());
|
||||
return new PackerBuildResult(
|
||||
PackerOperationStatus.FAILED,
|
||||
planResult.summary(),
|
||||
@ -61,11 +74,18 @@ public final class FileSystemPackerBuildService implements PackerBuildService {
|
||||
|
||||
try {
|
||||
Files.createDirectories(buildDirectory);
|
||||
final String previousCacheKey = loadPreviousCacheKey(metadataJson);
|
||||
events.emit(
|
||||
previousCacheKey != null && previousCacheKey.equals(planResult.plan().cacheKey()) ? PackerEventKind.CACHE_HIT : PackerEventKind.CACHE_MISS,
|
||||
previousCacheKey != null && previousCacheKey.equals(planResult.plan().cacheKey()) ? "Build cache hit." : "Build cache miss.",
|
||||
List.of());
|
||||
final EmittedArchive archive = emitArchive(planResult.plan());
|
||||
events.emit(PackerEventKind.PROGRESS_UPDATED, "Build archive prepared.", new PackerProgress(0.5d, false), List.of());
|
||||
Files.write(assetsArchive, archive.bytes());
|
||||
Files.writeString(assetTableJson, archive.assetTableJson(), StandardCharsets.UTF_8);
|
||||
Files.writeString(preloadJson, archive.preloadJson(), StandardCharsets.UTF_8);
|
||||
Files.writeString(metadataJson, archive.metadataJson(), StandardCharsets.UTF_8);
|
||||
events.emit(PackerEventKind.BUILD_FINISHED, "Build finished.", new PackerProgress(1.0d, false), List.of());
|
||||
return new PackerBuildResult(
|
||||
planResult.status(),
|
||||
"Build emitted " + planResult.plan().assets().size() + " assets.",
|
||||
@ -149,6 +169,20 @@ public final class FileSystemPackerBuildService implements PackerBuildService {
|
||||
return new EmittedArchive(archiveBytes, assetTableJson, preloadJson, metadataJson);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String loadPreviousCacheKey(Path metadataJson) {
|
||||
if (!Files.isRegularFile(metadataJson)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return ((Map<String, Object>) new com.fasterxml.jackson.databind.ObjectMapper().readValue(Files.readString(metadataJson), Map.class))
|
||||
.getOrDefault("cache_key", "")
|
||||
.toString();
|
||||
} catch (IOException exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private record EmittedArchive(
|
||||
byte[] bytes,
|
||||
String assetTableJson,
|
||||
|
||||
@ -11,9 +11,13 @@ import p.packer.api.doctor.PackerDoctorMode;
|
||||
import p.packer.api.doctor.PackerDoctorRequest;
|
||||
import p.packer.api.doctor.PackerDoctorResult;
|
||||
import p.packer.api.doctor.PackerDoctorService;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.packer.api.events.PackerEventSink;
|
||||
import p.packer.api.events.PackerProgress;
|
||||
import p.packer.api.workspace.GetAssetDetailsRequest;
|
||||
import p.packer.api.workspace.ListAssetsRequest;
|
||||
import p.packer.declarations.PackerAssetDetailsService;
|
||||
import p.packer.events.PackerOperationEventEmitter;
|
||||
import p.packer.workspace.FileSystemPackerWorkspaceService;
|
||||
|
||||
import java.nio.file.Files;
|
||||
@ -27,16 +31,25 @@ import java.util.Set;
|
||||
public final class FileSystemPackerDoctorService implements PackerDoctorService {
|
||||
private final FileSystemPackerWorkspaceService workspaceService;
|
||||
private final PackerAssetDetailsService detailsService;
|
||||
private final PackerEventSink eventSink;
|
||||
|
||||
public FileSystemPackerDoctorService() {
|
||||
this(new FileSystemPackerWorkspaceService(), new PackerAssetDetailsService());
|
||||
this(new FileSystemPackerWorkspaceService(), new PackerAssetDetailsService(), PackerEventSink.noop());
|
||||
}
|
||||
|
||||
public FileSystemPackerDoctorService(
|
||||
FileSystemPackerWorkspaceService workspaceService,
|
||||
PackerAssetDetailsService detailsService) {
|
||||
this(workspaceService, detailsService, PackerEventSink.noop());
|
||||
}
|
||||
|
||||
public FileSystemPackerDoctorService(
|
||||
FileSystemPackerWorkspaceService workspaceService,
|
||||
PackerAssetDetailsService detailsService,
|
||||
PackerEventSink eventSink) {
|
||||
this.workspaceService = Objects.requireNonNull(workspaceService, "workspaceService");
|
||||
this.detailsService = Objects.requireNonNull(detailsService, "detailsService");
|
||||
this.eventSink = Objects.requireNonNull(eventSink, "eventSink");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -48,6 +61,7 @@ public final class FileSystemPackerDoctorService implements PackerDoctorService
|
||||
public PackerDoctorResult doctor(PackerDoctorRequest request) {
|
||||
final PackerDoctorRequest doctorRequest = Objects.requireNonNull(request, "request");
|
||||
final PackerProjectContext project = doctorRequest.project();
|
||||
final PackerOperationEventEmitter events = new PackerOperationEventEmitter(project, eventSink);
|
||||
final var snapshot = workspaceService.listAssets(new ListAssetsRequest(project));
|
||||
final List<PackerDiagnostic> diagnostics = new ArrayList<>();
|
||||
final Set<String> safeFixes = new LinkedHashSet<>();
|
||||
@ -57,10 +71,13 @@ public final class FileSystemPackerDoctorService implements PackerDoctorService
|
||||
addDiagnostic(diagnostics, seenDiagnostics, diagnostic);
|
||||
}
|
||||
|
||||
final int totalAssets = snapshot.assets().size();
|
||||
int inspected = 0;
|
||||
for (var asset : snapshot.assets()) {
|
||||
if (!includeAsset(doctorRequest.mode(), asset.state())) {
|
||||
continue;
|
||||
}
|
||||
inspected += 1;
|
||||
final String assetReference = asset.identity().assetId() == null
|
||||
? relativeAssetRoot(project, asset.identity().assetRoot())
|
||||
: Integer.toString(asset.identity().assetId());
|
||||
@ -102,6 +119,11 @@ public final class FileSystemPackerDoctorService implements PackerDoctorService
|
||||
input,
|
||||
managed));
|
||||
}));
|
||||
events.emit(
|
||||
PackerEventKind.PROGRESS_UPDATED,
|
||||
"Doctor inspected asset: " + asset.identity().assetName(),
|
||||
new PackerProgress(totalAssets == 0 ? 1.0d : inspected / (double) totalAssets, false),
|
||||
List.of(asset.identity().assetName()));
|
||||
}
|
||||
|
||||
final long blockingCount = diagnostics.stream().filter(PackerDiagnostic::blocking).count();
|
||||
@ -113,6 +135,7 @@ public final class FileSystemPackerDoctorService implements PackerDoctorService
|
||||
: diagnostics.isEmpty()
|
||||
? "Doctor found no diagnostics."
|
||||
: "Doctor found " + diagnostics.size() + " diagnostics with no blockers.";
|
||||
events.emit(PackerEventKind.DIAGNOSTICS_UPDATED, summary, List.of());
|
||||
return new PackerDoctorResult(status, summary, diagnostics, List.copyOf(safeFixes));
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
package p.packer.events;
|
||||
|
||||
import p.packer.api.PackerProjectContext;
|
||||
import p.packer.api.events.PackerEvent;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.packer.api.events.PackerEventSink;
|
||||
import p.packer.api.events.PackerProgress;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class PackerOperationEventEmitter {
|
||||
private final PackerProjectContext project;
|
||||
private final PackerEventSink sink;
|
||||
private final String operationId;
|
||||
private long sequence;
|
||||
|
||||
public PackerOperationEventEmitter(PackerProjectContext project, PackerEventSink sink) {
|
||||
this(project, sink, UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
public PackerOperationEventEmitter(PackerProjectContext project, PackerEventSink sink, String operationId) {
|
||||
this.project = Objects.requireNonNull(project, "project");
|
||||
this.sink = Objects.requireNonNull(sink, "sink");
|
||||
this.operationId = Objects.requireNonNull(operationId, "operationId").trim();
|
||||
}
|
||||
|
||||
public String operationId() {
|
||||
return operationId;
|
||||
}
|
||||
|
||||
public void emit(PackerEventKind kind, String summary, List<String> affectedAssets) {
|
||||
emit(kind, summary, null, affectedAssets);
|
||||
}
|
||||
|
||||
public void emit(PackerEventKind kind, String summary, PackerProgress progress, List<String> affectedAssets) {
|
||||
sink.publish(new PackerEvent(
|
||||
project.projectId(),
|
||||
operationId,
|
||||
sequence++,
|
||||
Objects.requireNonNull(kind, "kind"),
|
||||
Instant.now(),
|
||||
Objects.requireNonNull(summary, "summary"),
|
||||
progress,
|
||||
affectedAssets == null ? List.of() : List.copyOf(affectedAssets)));
|
||||
}
|
||||
}
|
||||
@ -81,10 +81,11 @@ public final class FileSystemPackerMutationService implements PackerMutationServ
|
||||
throw new PackerMutationException("Cannot apply mutation preview with blockers");
|
||||
}
|
||||
final PackerMutationResult result = writeCoordinator.withWriteLock(project, () -> applyLocked(preview));
|
||||
emit(project, result.operationId(), 1L, PackerEventKind.ACTION_APPLIED, result.summary(), affectedAssets(preview));
|
||||
emit(project, result.operationId(), 1L, PackerEventKind.ASSET_CHANGED, "Asset state changed.", affectedAssets(preview));
|
||||
emit(project, result.operationId(), 2L, PackerEventKind.ACTION_APPLIED, result.summary(), affectedAssets(preview));
|
||||
return result;
|
||||
} catch (RuntimeException exception) {
|
||||
emit(project, preview.operationId(), 1L, PackerEventKind.ACTION_FAILED, rootCauseMessage(exception), affectedAssets(preview));
|
||||
emit(project, preview.operationId(), 2L, PackerEventKind.ACTION_FAILED, rootCauseMessage(exception), affectedAssets(preview));
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,9 @@ import p.packer.api.assets.PackerAssetSummary;
|
||||
import p.packer.api.diagnostics.PackerDiagnostic;
|
||||
import p.packer.api.diagnostics.PackerDiagnosticCategory;
|
||||
import p.packer.api.diagnostics.PackerDiagnosticSeverity;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.packer.api.events.PackerEventSink;
|
||||
import p.packer.api.events.PackerProgress;
|
||||
import p.packer.api.workspace.GetAssetDetailsRequest;
|
||||
import p.packer.api.workspace.GetAssetDetailsResult;
|
||||
import p.packer.api.workspace.InitWorkspaceRequest;
|
||||
@ -23,6 +26,7 @@ import p.packer.foundation.PackerRegistryEntry;
|
||||
import p.packer.foundation.PackerRegistryState;
|
||||
import p.packer.foundation.PackerWorkspaceFoundation;
|
||||
import p.packer.foundation.PackerWorkspacePaths;
|
||||
import p.packer.events.PackerOperationEventEmitter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -34,17 +38,26 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
private final PackerWorkspaceFoundation workspaceFoundation;
|
||||
private final PackerAssetDeclarationParser parser;
|
||||
private final PackerAssetDetailsService detailsService;
|
||||
private final PackerEventSink eventSink;
|
||||
|
||||
public FileSystemPackerWorkspaceService() {
|
||||
this(new PackerWorkspaceFoundation(), new PackerAssetDeclarationParser());
|
||||
this(new PackerWorkspaceFoundation(), new PackerAssetDeclarationParser(), PackerEventSink.noop());
|
||||
}
|
||||
|
||||
public FileSystemPackerWorkspaceService(
|
||||
PackerWorkspaceFoundation workspaceFoundation,
|
||||
PackerAssetDeclarationParser parser) {
|
||||
this(workspaceFoundation, parser, PackerEventSink.noop());
|
||||
}
|
||||
|
||||
public FileSystemPackerWorkspaceService(
|
||||
PackerWorkspaceFoundation workspaceFoundation,
|
||||
PackerAssetDeclarationParser parser,
|
||||
PackerEventSink eventSink) {
|
||||
this.workspaceFoundation = Objects.requireNonNull(workspaceFoundation, "workspaceFoundation");
|
||||
this.parser = Objects.requireNonNull(parser, "parser");
|
||||
this.detailsService = new PackerAssetDetailsService(workspaceFoundation, parser);
|
||||
this.eventSink = Objects.requireNonNull(eventSink, "eventSink");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -60,6 +73,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
@Override
|
||||
public ListAssetsResult listAssets(ListAssetsRequest request) {
|
||||
final PackerProjectContext project = Objects.requireNonNull(request, "request").project();
|
||||
final PackerOperationEventEmitter events = new PackerOperationEventEmitter(project, eventSink);
|
||||
final Path assetsRoot = PackerWorkspacePaths.assetsRoot(project);
|
||||
final PackerRegistryState registry = workspaceFoundation.loadRegistry(project);
|
||||
final Map<Path, PackerRegistryEntry> registryByRoot = new HashMap<>();
|
||||
@ -74,14 +88,23 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
if (Files.isDirectory(assetsRoot)) {
|
||||
try (Stream<Path> paths = Files.find(assetsRoot, Integer.MAX_VALUE, (path, attrs) ->
|
||||
attrs.isRegularFile() && path.getFileName().toString().equals("asset.json"))) {
|
||||
paths.forEach(assetManifestPath -> {
|
||||
final List<Path> manifests = paths.toList();
|
||||
final int total = manifests.size();
|
||||
for (int index = 0; index < manifests.size(); index += 1) {
|
||||
final Path assetManifestPath = manifests.get(index);
|
||||
final Path assetRoot = assetManifestPath.getParent().toAbsolutePath().normalize();
|
||||
discoveredRoots.add(assetRoot);
|
||||
final PackerRegistryEntry registryEntry = registryByRoot.get(assetRoot);
|
||||
final PackerAssetDeclarationParseResult parsed = parser.parse(assetManifestPath);
|
||||
diagnostics.addAll(parsed.diagnostics());
|
||||
assets.add(buildSummary(assetRoot, registryEntry, parsed));
|
||||
});
|
||||
final PackerAssetSummary summary = buildSummary(assetRoot, registryEntry, parsed);
|
||||
assets.add(summary);
|
||||
events.emit(
|
||||
PackerEventKind.ASSET_DISCOVERED,
|
||||
"Discovered asset: " + summary.identity().assetName(),
|
||||
new PackerProgress(total == 0 ? 1.0d : (index + 1) / (double) total, false),
|
||||
List.of(summary.identity().assetName()));
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
@ -110,6 +133,9 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
final PackerOperationStatus status = diagnostics.stream().anyMatch(PackerDiagnostic::blocking)
|
||||
? PackerOperationStatus.PARTIAL
|
||||
: PackerOperationStatus.SUCCESS;
|
||||
if (!diagnostics.isEmpty()) {
|
||||
events.emit(PackerEventKind.DIAGNOSTICS_UPDATED, "Asset scan diagnostics updated.", List.of());
|
||||
}
|
||||
return new ListAssetsResult(
|
||||
status,
|
||||
"Packer asset snapshot ready.",
|
||||
|
||||
@ -6,6 +6,8 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.api.PackerOperationStatus;
|
||||
import p.packer.api.PackerProjectContext;
|
||||
import p.packer.api.building.PackerBuildRequest;
|
||||
import p.packer.api.events.PackerEvent;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@ -14,6 +16,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -72,6 +75,21 @@ final class FileSystemPackerBuildServiceTest {
|
||||
assertTrue(result.companionArtifacts().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void emitsBuildLifecycleAndCacheEvents() throws Exception {
|
||||
final Path projectRoot = createProject(tempDir.resolve("events"));
|
||||
final List<PackerEvent> events = new CopyOnWriteArrayList<>();
|
||||
final FileSystemPackerBuildService service = new FileSystemPackerBuildService(new PackerBuildPlanner(), events::add);
|
||||
|
||||
service.build(new PackerBuildRequest(new PackerProjectContext("events", projectRoot), false));
|
||||
service.build(new PackerBuildRequest(new PackerProjectContext("events", projectRoot), false));
|
||||
|
||||
assertEquals(PackerEventKind.BUILD_STARTED, events.getFirst().kind());
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.CACHE_MISS));
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.CACHE_HIT));
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.BUILD_FINISHED));
|
||||
}
|
||||
|
||||
private ParsedArchive parseArchive(Path archivePath) throws Exception {
|
||||
final byte[] bytes = Files.readAllBytes(archivePath);
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
@ -7,9 +7,13 @@ import p.packer.api.PackerProjectContext;
|
||||
import p.packer.api.diagnostics.PackerDiagnosticCategory;
|
||||
import p.packer.api.doctor.PackerDoctorMode;
|
||||
import p.packer.api.doctor.PackerDoctorRequest;
|
||||
import p.packer.api.events.PackerEvent;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -81,6 +85,24 @@ final class FileSystemPackerDoctorServiceTest {
|
||||
assertTrue(result.safeFixes().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void emitsDiagnosticsLifecycleEvents() throws Exception {
|
||||
final Path projectRoot = createManagedProjectWithMissingInput();
|
||||
final List<PackerEvent> events = new CopyOnWriteArrayList<>();
|
||||
final FileSystemPackerDoctorService service = new FileSystemPackerDoctorService(
|
||||
new p.packer.workspace.FileSystemPackerWorkspaceService(),
|
||||
new p.packer.declarations.PackerAssetDetailsService(),
|
||||
events::add);
|
||||
|
||||
service.doctor(new PackerDoctorRequest(
|
||||
new PackerProjectContext("main", projectRoot),
|
||||
PackerDoctorMode.MANAGED_WORLD,
|
||||
false));
|
||||
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.PROGRESS_UPDATED));
|
||||
assertEquals(PackerEventKind.DIAGNOSTICS_UPDATED, events.getLast().kind());
|
||||
}
|
||||
|
||||
private Path createManagedProjectWithMissingInput() throws Exception {
|
||||
final Path projectRoot = tempDir.resolve("managed-missing-input");
|
||||
final Path assetRoot = projectRoot.resolve("assets/ui/atlas");
|
||||
|
||||
@ -50,9 +50,10 @@ final class FileSystemPackerMutationServiceTest {
|
||||
assertTrue(Files.isDirectory(preview.targetAssetRoot()));
|
||||
final String registryJson = Files.readString(projectRoot.resolve("assets/.prometeu/index.json"));
|
||||
assertFalse(registryJson.contains("\"root\" : \"ui/atlas\""));
|
||||
assertEquals(List.of(PackerEventKind.PREVIEW_READY, PackerEventKind.ACTION_APPLIED), events.stream().map(PackerEvent::kind).toList());
|
||||
assertEquals(List.of(PackerEventKind.PREVIEW_READY, PackerEventKind.ASSET_CHANGED, PackerEventKind.ACTION_APPLIED), events.stream().map(PackerEvent::kind).toList());
|
||||
assertEquals(preview.operationId(), events.getFirst().operationId());
|
||||
assertEquals(preview.operationId(), events.get(1).operationId());
|
||||
assertEquals(preview.operationId(), events.get(2).operationId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -5,12 +5,16 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.api.PackerOperationStatus;
|
||||
import p.packer.api.PackerProjectContext;
|
||||
import p.packer.api.assets.PackerAssetState;
|
||||
import p.packer.api.events.PackerEvent;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.packer.api.workspace.ListAssetsRequest;
|
||||
import p.packer.testing.PackerFixtureLocator;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -54,6 +58,22 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertTrue(result.assets().getFirst().hasDiagnostics());
|
||||
}
|
||||
|
||||
@Test
|
||||
void emitsDiscoveryAndDiagnosticsEventsDuringScan() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/read-invalid", tempDir.resolve("events"));
|
||||
final List<PackerEvent> events = new CopyOnWriteArrayList<>();
|
||||
final FileSystemPackerWorkspaceService service = new FileSystemPackerWorkspaceService(
|
||||
new p.packer.foundation.PackerWorkspaceFoundation(),
|
||||
new p.packer.declarations.PackerAssetDeclarationParser(),
|
||||
events::add);
|
||||
|
||||
service.listAssets(new ListAssetsRequest(project(projectRoot)));
|
||||
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.ASSET_DISCOVERED));
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.DIAGNOSTICS_UPDATED));
|
||||
assertTrue(events.stream().allMatch(event -> event.sequence() >= 0L));
|
||||
}
|
||||
|
||||
private PackerProjectContext project(Path root) {
|
||||
return new PackerProjectContext("main", root);
|
||||
}
|
||||
|
||||
@ -30,7 +30,18 @@ public final class StudioActivityEventMapper {
|
||||
Optional.of(new StudioActivityEntry("Assets", "Action applied: " + applied.action().name().toLowerCase(), StudioActivityEntrySeverity.SUCCESS, false));
|
||||
case StudioAssetsMutationFailedEvent failed ->
|
||||
Optional.of(new StudioActivityEntry("Assets", failed.message(), StudioActivityEntrySeverity.ERROR, true));
|
||||
case StudioPackerOperationEvent packerEvent ->
|
||||
Optional.of(new StudioActivityEntry("Assets", packerEvent.summary(), severity(packerEvent), packerEvent.kind() == p.packer.api.events.PackerEventKind.ACTION_FAILED));
|
||||
default -> Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
private static StudioActivityEntrySeverity severity(StudioPackerOperationEvent event) {
|
||||
return switch (event.kind()) {
|
||||
case BUILD_FINISHED, CACHE_HIT -> StudioActivityEntrySeverity.SUCCESS;
|
||||
case DIAGNOSTICS_UPDATED, CACHE_MISS, BUILD_STARTED, ASSET_DISCOVERED, ASSET_CHANGED, PROGRESS_UPDATED -> StudioActivityEntrySeverity.INFO;
|
||||
case ACTION_FAILED -> StudioActivityEntrySeverity.ERROR;
|
||||
default -> StudioActivityEntrySeverity.INFO;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@ public final class StudioActivityFeedControl extends VBox implements StudioContr
|
||||
subscriptions.add(eventBus.subscribe(StudioAssetsMutationPreviewReadyEvent.class, this::onEvent));
|
||||
subscriptions.add(eventBus.subscribe(StudioAssetsMutationAppliedEvent.class, this::onEvent));
|
||||
subscriptions.add(eventBus.subscribe(StudioAssetsMutationFailedEvent.class, this::onEvent));
|
||||
subscriptions.add(eventBus.subscribe(StudioPackerOperationEvent.class, this::onPackerOperation));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,6 +115,23 @@ public final class StudioActivityFeedControl extends VBox implements StudioContr
|
||||
clearProgress();
|
||||
}
|
||||
|
||||
private void onPackerOperation(StudioPackerOperationEvent event) {
|
||||
onEvent(event);
|
||||
if (event.progress() != null) {
|
||||
Platform.runLater(() -> {
|
||||
progressLabel.setText(event.summary());
|
||||
progressBar.setVisible(true);
|
||||
progressBar.setManaged(true);
|
||||
progressBar.setProgress(event.indeterminate() ? ProgressBar.INDETERMINATE_PROGRESS : event.progress());
|
||||
});
|
||||
if (event.kind() == p.packer.api.events.PackerEventKind.BUILD_FINISHED) {
|
||||
clearProgress();
|
||||
}
|
||||
} else if (event.kind() == p.packer.api.events.PackerEventKind.BUILD_FINISHED) {
|
||||
clearProgress();
|
||||
}
|
||||
}
|
||||
|
||||
private void clearProgress() {
|
||||
Platform.runLater(() -> {
|
||||
progressLabel.setText(Container.i18n().text(I18n.ACTIVITY_PROGRESS_IDLE));
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
package p.studio.events;
|
||||
|
||||
import p.packer.api.events.PackerEvent;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.packer.api.events.PackerEventSink;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class StudioPackerEventAdapter implements PackerEventSink {
|
||||
private final StudioWorkspaceEventBus eventBus;
|
||||
private final ProjectReference projectReference;
|
||||
|
||||
public StudioPackerEventAdapter(StudioWorkspaceEventBus eventBus, ProjectReference projectReference) {
|
||||
this.eventBus = Objects.requireNonNull(eventBus, "eventBus");
|
||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(PackerEvent event) {
|
||||
final PackerEvent packerEvent = Objects.requireNonNull(event, "event");
|
||||
if (ignore(packerEvent.kind())) {
|
||||
return;
|
||||
}
|
||||
eventBus.publish(new StudioPackerOperationEvent(
|
||||
projectReference,
|
||||
packerEvent.operationId(),
|
||||
packerEvent.kind(),
|
||||
packerEvent.summary(),
|
||||
packerEvent.progress() == null ? null : packerEvent.progress().value(),
|
||||
packerEvent.progress() == null || packerEvent.progress().indeterminate()));
|
||||
}
|
||||
|
||||
private boolean ignore(PackerEventKind kind) {
|
||||
return kind == PackerEventKind.PREVIEW_READY
|
||||
|| kind == PackerEventKind.ACTION_APPLIED
|
||||
|| kind == PackerEventKind.ACTION_FAILED;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package p.studio.events;
|
||||
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record StudioPackerOperationEvent(
|
||||
ProjectReference project,
|
||||
String operationId,
|
||||
PackerEventKind kind,
|
||||
String summary,
|
||||
Double progress,
|
||||
boolean indeterminate) implements StudioEvent {
|
||||
|
||||
public StudioPackerOperationEvent {
|
||||
Objects.requireNonNull(project, "project");
|
||||
operationId = Objects.requireNonNull(operationId, "operationId").trim();
|
||||
Objects.requireNonNull(kind, "kind");
|
||||
summary = Objects.requireNonNull(summary, "summary").trim();
|
||||
if (operationId.isBlank() || summary.isBlank()) {
|
||||
throw new IllegalArgumentException("operationId and summary must not be blank");
|
||||
}
|
||||
if (progress != null && !indeterminate && (progress < 0.0d || progress > 1.0d)) {
|
||||
throw new IllegalArgumentException("progress must be between 0.0 and 1.0 when determinate");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,9 @@ import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.Workspace;
|
||||
import p.studio.workspaces.WorkspaceId;
|
||||
import p.packer.declarations.PackerAssetDeclarationParser;
|
||||
import p.packer.foundation.PackerWorkspaceFoundation;
|
||||
import p.packer.workspace.FileSystemPackerWorkspaceService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -58,7 +61,7 @@ public final class AssetWorkspace implements Workspace {
|
||||
public AssetWorkspace(ProjectReference projectReference) {
|
||||
this(
|
||||
projectReference,
|
||||
new PackerBackedAssetWorkspaceService(),
|
||||
null,
|
||||
defaultWorkspaceBus(),
|
||||
null);
|
||||
}
|
||||
@ -76,8 +79,13 @@ public final class AssetWorkspace implements Workspace {
|
||||
StudioWorkspaceEventBus workspaceBus,
|
||||
AssetWorkspaceMutationService mutationService) {
|
||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||
this.assetWorkspaceService = Objects.requireNonNull(assetWorkspaceService, "assetWorkspaceService");
|
||||
this.workspaceBus = Objects.requireNonNull(workspaceBus, "workspaceBus");
|
||||
this.assetWorkspaceService = assetWorkspaceService == null
|
||||
? new PackerBackedAssetWorkspaceService(new FileSystemPackerWorkspaceService(
|
||||
new PackerWorkspaceFoundation(),
|
||||
new PackerAssetDeclarationParser(),
|
||||
new StudioPackerEventAdapter(this.workspaceBus, this.projectReference)))
|
||||
: Objects.requireNonNull(assetWorkspaceService, "assetWorkspaceService");
|
||||
this.mutationService = mutationService == null
|
||||
? new PackerBackedAssetWorkspaceMutationService(this.workspaceBus)
|
||||
: Objects.requireNonNull(mutationService, "mutationService");
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
package p.studio.controls.shell;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.studio.events.StudioAssetsMutationAppliedEvent;
|
||||
import p.studio.events.StudioAssetsMutationFailedEvent;
|
||||
import p.studio.events.StudioAssetsMutationPreviewReadyEvent;
|
||||
import p.studio.events.StudioPackerOperationEvent;
|
||||
import p.studio.events.StudioAssetsWorkspaceRefreshFailedEvent;
|
||||
import p.studio.events.StudioAssetsWorkspaceRefreshedEvent;
|
||||
import p.studio.events.StudioProjectOpenedEvent;
|
||||
@ -83,6 +85,17 @@ final class StudioActivityEventMapperTest {
|
||||
assertEquals("Apply failed", entry.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapsPackerBuildEventToActivityEntry() {
|
||||
final StudioActivityEntry entry = StudioActivityEventMapper
|
||||
.map(new StudioPackerOperationEvent(project(), "op-1", PackerEventKind.BUILD_FINISHED, "Build finished.", 1.0d, false))
|
||||
.orElseThrow();
|
||||
|
||||
assertEquals("Assets", entry.source());
|
||||
assertEquals(StudioActivityEntrySeverity.SUCCESS, entry.severity());
|
||||
assertEquals("Build finished.", entry.message());
|
||||
}
|
||||
|
||||
private ProjectReference project() {
|
||||
return new ProjectReference("Main", "1.0.0", "pbs", 1, Path.of("/tmp/main"));
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
package p.studio.events;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.packer.api.events.PackerEvent;
|
||||
import p.packer.api.events.PackerEventKind;
|
||||
import p.packer.api.events.PackerProgress;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.workspaces.WorkspaceId;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
final class StudioPackerEventAdapterTest {
|
||||
@Test
|
||||
void forwardsBuildEventsIntoStudioOperationEvents() {
|
||||
final StudioEventBus globalBus = new StudioEventBus();
|
||||
final List<StudioPackerOperationEvent> events = new ArrayList<>();
|
||||
globalBus.subscribe(StudioPackerOperationEvent.class, events::add);
|
||||
final StudioPackerEventAdapter adapter = new StudioPackerEventAdapter(
|
||||
new StudioWorkspaceEventBus(WorkspaceId.ASSETS, globalBus),
|
||||
project());
|
||||
|
||||
adapter.publish(new PackerEvent(
|
||||
"main",
|
||||
"op-1",
|
||||
0L,
|
||||
PackerEventKind.BUILD_STARTED,
|
||||
Instant.now(),
|
||||
"Build started.",
|
||||
new PackerProgress(0.25d, false),
|
||||
List.of("ui_atlas")));
|
||||
|
||||
assertEquals(1, events.size());
|
||||
assertEquals(PackerEventKind.BUILD_STARTED, events.getFirst().kind());
|
||||
assertEquals(0.25d, events.getFirst().progress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoresMutationLifecycleEventsAlreadyHandledByTypedStudioEvents() {
|
||||
final StudioEventBus globalBus = new StudioEventBus();
|
||||
final List<StudioPackerOperationEvent> events = new ArrayList<>();
|
||||
globalBus.subscribe(StudioPackerOperationEvent.class, events::add);
|
||||
final StudioPackerEventAdapter adapter = new StudioPackerEventAdapter(
|
||||
new StudioWorkspaceEventBus(WorkspaceId.ASSETS, globalBus),
|
||||
project());
|
||||
|
||||
adapter.publish(new PackerEvent(
|
||||
"main",
|
||||
"op-1",
|
||||
0L,
|
||||
PackerEventKind.PREVIEW_READY,
|
||||
Instant.now(),
|
||||
"Preview ready.",
|
||||
null,
|
||||
List.of("ui_atlas")));
|
||||
|
||||
assertTrue(events.isEmpty());
|
||||
}
|
||||
|
||||
private ProjectReference project() {
|
||||
return new ProjectReference("Main", "1.0.0", "pbs", 1, Path.of("/tmp/main"));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user