Skip to content

Commit 083c189

Browse files
authored
refactor: cloud api reporting (#928)
1 parent 43ac7d1 commit 083c189

16 files changed

Lines changed: 767 additions & 34 deletions

File tree

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ plugins {
1919

2020
allprojects {
2121
group = "io.flamingock"
22-
val declaredVersion = "1.4.1-SNAPSHOT"
22+
val declaredVersion = "1.5.0-SNAPSHOT"
2323
version = VersionManager.resolveVersion(declaredVersion, project.hasProperty("release"))
2424

2525
extra["generalUtilVersion"] = "1.5.3"

cloud/flamingock-cloud-api/src/main/java/io/flamingock/cloud/api/request/ChangeRequest.java

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package io.flamingock.cloud.api.request;
1717

1818

19+
import com.fasterxml.jackson.annotation.JsonInclude;
20+
import io.flamingock.cloud.api.vo.CloudChangeStatus;
1921
import io.flamingock.cloud.api.vo.CloudTargetSystemAuditMarkType;
2022

2123
//TODO add recoveryStrategy, so we can determin the acction in the server
@@ -25,26 +27,51 @@ public class ChangeRequest {
2527

2628
private CloudTargetSystemAuditMarkType ongoingStatus;
2729

30+
/**
31+
* Per-change status reported by the client — mirrors the operation-side
32+
* {@code ChangeResult.status} currently held on the client's {@code PipelineRun}. The
33+
* server uses this as informational input when synthesising the response: it never
34+
* contradicts the client's positive report (e.g. {@code APPLIED} stays {@code APPLIED},
35+
* not downgraded to {@code ALREADY_APPLIED}), and it respects {@code FAILED} /
36+
* {@code ROLLED_BACK} reports so it doesn't ask the client to retry indefinitely.
37+
*
38+
* <p>{@code null} on the wire means the operation has nothing to report yet
39+
* (equivalent to {@code NOT_REACHED}). Serialised as field-absence so the wire shape is
40+
* forward-compatible with older mocks/expectations that don't set this field.
41+
*/
42+
@JsonInclude(JsonInclude.Include.NON_NULL)
43+
private CloudChangeStatus currentStatus;
44+
2845
private boolean transactional;
2946

3047
public ChangeRequest() {
3148
}
3249

3350
public static ChangeRequest change(String id, boolean transactional) {
34-
return new ChangeRequest(id, CloudTargetSystemAuditMarkType.NONE, transactional);
51+
return new ChangeRequest(id, CloudTargetSystemAuditMarkType.NONE, null, transactional);
3552
}
3653

3754
public static ChangeRequest ongoingExecution(String id, boolean transactional) {
38-
return new ChangeRequest(id, CloudTargetSystemAuditMarkType.APPLIED, transactional);
55+
return new ChangeRequest(id, CloudTargetSystemAuditMarkType.APPLIED, null, transactional);
3956
}
4057

4158
public static ChangeRequest ongoingRollback(String id, boolean transactional) {
42-
return new ChangeRequest(id, CloudTargetSystemAuditMarkType.ROLLED_BACK, transactional);
59+
return new ChangeRequest(id, CloudTargetSystemAuditMarkType.ROLLED_BACK, null, transactional);
60+
}
61+
62+
public ChangeRequest(String id,
63+
CloudTargetSystemAuditMarkType ongoingStatus,
64+
boolean transactional) {
65+
this(id, ongoingStatus, null, transactional);
4366
}
4467

45-
public ChangeRequest(String id, CloudTargetSystemAuditMarkType ongoingStatus, boolean transactional) {
68+
public ChangeRequest(String id,
69+
CloudTargetSystemAuditMarkType ongoingStatus,
70+
CloudChangeStatus currentStatus,
71+
boolean transactional) {
4672
this.id = id;
4773
this.ongoingStatus = ongoingStatus;
74+
this.currentStatus = currentStatus;
4875
this.transactional = transactional;
4976
}
5077

@@ -56,6 +83,10 @@ public CloudTargetSystemAuditMarkType getOngoingStatus() {
5683
return ongoingStatus;
5784
}
5885

86+
public CloudChangeStatus getCurrentStatus() {
87+
return currentStatus;
88+
}
89+
5990
public boolean isTransactional() {
6091
return transactional;
6192
}
@@ -68,6 +99,10 @@ public void setOngoingStatus(CloudTargetSystemAuditMarkType ongoingStatus) {
6899
this.ongoingStatus = ongoingStatus;
69100
}
70101

102+
public void setCurrentStatus(CloudChangeStatus currentStatus) {
103+
this.currentStatus = currentStatus;
104+
}
105+
71106
public void setTransactional(boolean transactional) {
72107
this.transactional = transactional;
73108
}
@@ -79,11 +114,12 @@ public boolean equals(Object o) {
79114
ChangeRequest that = (ChangeRequest) o;
80115
return transactional == that.transactional
81116
&& java.util.Objects.equals(id, that.id)
82-
&& java.util.Objects.equals(ongoingStatus, that.ongoingStatus);
117+
&& java.util.Objects.equals(ongoingStatus, that.ongoingStatus)
118+
&& java.util.Objects.equals(currentStatus, that.currentStatus);
83119
}
84120

85121
@Override
86122
public int hashCode() {
87-
return java.util.Objects.hash(id, ongoingStatus, transactional);
123+
return java.util.Objects.hash(id, ongoingStatus, currentStatus, transactional);
88124
}
89125
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2026 Flamingock (https://www.flamingock.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.flamingock.cloud.api.response;
17+
18+
import io.flamingock.cloud.api.vo.CloudChangeStatus;
19+
20+
import java.util.Objects;
21+
22+
/**
23+
* Result-side per-change payload. Sibling of the operation-side {@link ChangeResponse} (which
24+
* carries {@code action}); this class carries the server's synthesised {@code status} so the
25+
* client can write rich per-change records into {@code PipelineRun} via
26+
* {@code markStageAlreadyAppliedFromAudit} (and downstream renderers).
27+
*/
28+
public class ChangeResultResponse {
29+
30+
private String id;
31+
32+
private CloudChangeStatus status;
33+
34+
public ChangeResultResponse() {
35+
}
36+
37+
public ChangeResultResponse(String id, CloudChangeStatus status) {
38+
this.id = id;
39+
this.status = status;
40+
}
41+
42+
public String getId() {
43+
return id;
44+
}
45+
46+
public void setId(String id) {
47+
this.id = id;
48+
}
49+
50+
public CloudChangeStatus getStatus() {
51+
return status;
52+
}
53+
54+
public void setStatus(CloudChangeStatus status) {
55+
this.status = status;
56+
}
57+
58+
@Override
59+
public boolean equals(Object o) {
60+
if (this == o) return true;
61+
if (o == null || getClass() != o.getClass()) return false;
62+
ChangeResultResponse that = (ChangeResultResponse) o;
63+
return Objects.equals(id, that.id) && status == that.status;
64+
}
65+
66+
@Override
67+
public int hashCode() {
68+
return Objects.hash(id, status);
69+
}
70+
}

cloud/flamingock-cloud-api/src/main/java/io/flamingock/cloud/api/response/ExecutionPlanResponse.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,21 @@ public class ExecutionPlanResponse {
2828

2929
private LockInfoResponse lock;
3030

31+
/**
32+
* Operation side: the stages the executor should act on this round. Sparse — only
33+
* stages with actionable changes appear here.
34+
*/
3135
private List<StageResponse> stages;
3236

37+
/**
38+
* Result side: server's synthesised per-stage verdict + per-change status for the
39+
* <strong>entire</strong> submitted pipeline (not just stages with work). The client
40+
* iterates this uniformly to feed {@code PipelineRun.markStageVerdict} and
41+
* {@code PipelineRun.markStageAlreadyAppliedFromAudit}. Required on {@code EXECUTE}
42+
* and {@code CONTINUE} responses; absent on {@code AWAIT} / {@code ABORT}.
43+
*/
44+
private PipelineResultResponse pipelineResult;
45+
3346
private boolean synchronizedMarks;
3447

3548

@@ -54,10 +67,20 @@ public ExecutionPlanResponse(CloudExecutionAction action,
5467
LockInfoResponse lock,
5568
List<StageResponse> stages,
5669
boolean synchronizedMarks) {
70+
this(action, executionId, lock, stages, null, synchronizedMarks);
71+
}
72+
73+
public ExecutionPlanResponse(CloudExecutionAction action,
74+
String executionId,
75+
LockInfoResponse lock,
76+
List<StageResponse> stages,
77+
PipelineResultResponse pipelineResult,
78+
boolean synchronizedMarks) {
5779
this.action = action;
5880
this.executionId = executionId;
5981
this.lock = lock;
6082
this.stages = stages;
83+
this.pipelineResult = pipelineResult;
6184
this.synchronizedMarks = synchronizedMarks;
6285
}
6386

@@ -89,6 +112,14 @@ public void setStages(List<StageResponse> stages) {
89112
this.stages = stages;
90113
}
91114

115+
public PipelineResultResponse getPipelineResult() {
116+
return pipelineResult;
117+
}
118+
119+
public void setPipelineResult(PipelineResultResponse pipelineResult) {
120+
this.pipelineResult = pipelineResult;
121+
}
122+
92123
public boolean isContinue() {
93124
return action == CloudExecutionAction.CONTINUE;
94125
}
@@ -128,6 +159,15 @@ public void validate() {
128159
if (isAwait() && getLock() == null) {
129160
throw new RuntimeException("ExecutionPlan is await, but not lock information returned");
130161
}
162+
163+
// pipelineResult is required on EXECUTE and CONTINUE so the client can write
164+
// per-stage verdict and per-change records uniformly. AWAIT / ABORT carry none.
165+
if ((isExecute() || isContinue()) && pipelineResult == null) {
166+
throw new RuntimeException(
167+
"ExecutionPlan is " + action
168+
+ ", but no pipelineResult returned — the server must populate the result side"
169+
+ " so the client can write verdict and per-change status into PipelineRun.");
170+
}
131171
}
132172

133173
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2026 Flamingock (https://www.flamingock.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.flamingock.cloud.api.response;
17+
18+
import java.util.List;
19+
import java.util.Objects;
20+
21+
/**
22+
* Result-side sibling of {@code ExecutionPlanResponse.stages}. Carries the server's
23+
* synthesised per-stage {@code verdict} and per-change {@code status} for the entire
24+
* submitted pipeline, in a shape the client iterates uniformly to feed
25+
* {@code PipelineRun.markStageVerdict} and
26+
* {@code PipelineRun.markStageAlreadyAppliedFromAudit}.
27+
*
28+
* <p>The two halves of the response mirror the two halves of the core-side
29+
* {@code PipelineRun} two-writer model: the operation side ({@code stages[]}) tells the
30+
* executor what to do; the result side (this class) tells the client's planner what facts
31+
* to record.
32+
*/
33+
public class PipelineResultResponse {
34+
35+
private List<StageResultResponse> stages;
36+
37+
public PipelineResultResponse() {
38+
}
39+
40+
public PipelineResultResponse(List<StageResultResponse> stages) {
41+
this.stages = stages;
42+
}
43+
44+
public List<StageResultResponse> getStages() {
45+
return stages;
46+
}
47+
48+
public void setStages(List<StageResultResponse> stages) {
49+
this.stages = stages;
50+
}
51+
52+
@Override
53+
public boolean equals(Object o) {
54+
if (this == o) return true;
55+
if (o == null || getClass() != o.getClass()) return false;
56+
PipelineResultResponse that = (PipelineResultResponse) o;
57+
return Objects.equals(stages, that.stages);
58+
}
59+
60+
@Override
61+
public int hashCode() {
62+
return Objects.hash(stages);
63+
}
64+
}

0 commit comments

Comments
 (0)