Skip to content

Commit 6fe311c

Browse files
committed
fix admin server responses, fix zero values handling
1 parent 927cc87 commit 6fe311c

File tree

6 files changed

+43
-41
lines changed

6 files changed

+43
-41
lines changed

dbos/admin_server.go

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -152,20 +152,23 @@ func toListWorkflowResponse(ws WorkflowStatus) (map[string]any, error) {
152152
result["StartedAt"] = nil
153153
}
154154

155-
if ws.Input != nil && ws.Input != "" {
156-
bytes, err := json.Marshal(ws.Input)
157-
if err != nil {
158-
return nil, fmt.Errorf("failed to marshal input: %w", err)
155+
if ws.Input != nil {
156+
// If there is a value, it should be a JSON string
157+
jsonInput, ok := ws.Input.(string)
158+
if ok {
159+
result["Input"] = jsonInput
160+
} else {
161+
result["Input"] = ""
159162
}
160-
result["Input"] = string(bytes)
161163
}
162164

163-
if ws.Output != nil && ws.Output != "" {
164-
bytes, err := json.Marshal(ws.Output)
165-
if err != nil {
166-
return nil, fmt.Errorf("failed to marshal output: %w", err)
165+
if ws.Output != nil {
166+
jsonOutput, ok := ws.Output.(string)
167+
if ok {
168+
result["Output"] = jsonOutput
169+
} else {
170+
result["Output"] = ""
167171
}
168-
result["Output"] = string(bytes)
169172
}
170173

171174
if ws.Error != nil {
@@ -439,15 +442,16 @@ func newAdminServer(ctx *dbosContext, port int) *adminServer {
439442
"child_workflow_id": step.ChildWorkflowID,
440443
}
441444

442-
// Marshal Output as JSON string if present
443-
if step.Output != nil && step.Output != "" {
444-
bytes, err := json.Marshal(step.Output)
445-
if err != nil {
446-
ctx.logger.Error("Failed to marshal step output", "error", err)
447-
http.Error(w, fmt.Sprintf("Failed to format step output: %v", err), http.StatusInternalServerError)
448-
return
445+
if step.Output != nil {
446+
// If there is a value, it should be a JSON string
447+
jsonOutput, ok := step.Output.(string)
448+
if ok {
449+
formattedStep["output"] = jsonOutput
450+
} else {
451+
formattedStep["output"] = ""
449452
}
450-
formattedStep["output"] = string(bytes)
453+
} else {
454+
formattedStep["output"] = ""
451455
}
452456

453457
// Marshal Error as JSON string if present

dbos/admin_server_test.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -874,22 +874,9 @@ func TestAdminServer(t *testing.T) {
874874
assert.Contains(t, unmarshaledError, "deliberate error for testing", "Error message should be preserved")
875875

876876
case "emptyStep":
877-
// Empty string might be returned as nil or as an empty JSON string
877+
// Empty string is returned as an empty JSON string
878878
output := step["output"]
879-
if output == nil {
880-
// Empty string was not included in response (which is fine)
881-
t.Logf("Empty step output was nil (not included)")
882-
} else {
883-
// If it was included, it should be marshaled as JSON string `""`
884-
outputStr, ok := output.(string)
885-
require.True(t, ok, "If present, empty step output should be a JSON string")
886-
887-
var unmarshaledOutput string
888-
err = json.Unmarshal([]byte(outputStr), &unmarshaledOutput)
889-
require.NoError(t, err, "Failed to unmarshal empty step output")
890-
assert.Equal(t, "", unmarshaledOutput, "Empty step output should be empty string")
891-
}
892-
879+
require.Equal(t, "", output, "Empty step output should be an empty string")
893880
assert.Nil(t, step["error"], "Empty step should have no error")
894881
}
895882
}

dbos/queues_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,9 +541,13 @@ func TestQueueRecovery(t *testing.T) {
541541
// Root workflow case
542542
resultAny, err := h.GetResult()
543543
require.NoError(t, err, "failed to get result from recovered root workflow handle")
544-
// Decode the result from any (which may be []interface{} after JSON decode) to []int
544+
// re-encode and decode the result from []interface{} to []int
545+
encodedResult, ok := resultAny.([]any)
546+
require.True(t, ok, "expected result to be a []any")
547+
jsonBytes, err := json.Marshal(encodedResult)
548+
require.NoError(t, err, "failed to marshal result to JSON")
545549
var castedResult []int
546-
err = json.Unmarshal([]byte(resultAny.(string)), &castedResult)
550+
err = json.Unmarshal(jsonBytes, &castedResult)
547551
require.NoError(t, err, "failed to decode result to []int")
548552
expectedResult := []int{0, 1, 2, 3, 4}
549553
assert.Equal(t, expectedResult, castedResult, "expected result %v, got %v", expectedResult, castedResult)

dbos/serialization.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func newJSONSerializer[T any]() serializer[T] {
1919
}
2020

2121
func (j *jsonSerializer[T]) Encode(data T) (string, error) {
22-
if isNilValue(data) {
22+
if isNilOrZeroValue(data) {
2323
// For nil values, encode an empty byte slice directly to base64
2424
return base64.StdEncoding.EncodeToString([]byte{}), nil
2525
}
@@ -57,8 +57,8 @@ func (j *jsonSerializer[T]) Decode(data *string) (T, error) {
5757
return result, nil
5858
}
5959

60-
// isNilValue checks if a value is nil (for pointer types, slice, map, etc.)
61-
func isNilValue(v any) bool {
60+
// isNilOrZeroValue checks if a value is nil (for pointer types, slice, map, etc.) or a zero value.
61+
func isNilOrZeroValue(v any) bool {
6262
val := reflect.ValueOf(v)
6363
if !val.IsValid() {
6464
return true
@@ -67,7 +67,8 @@ func isNilValue(v any) bool {
6767
case reflect.Pointer, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func:
6868
return val.IsNil()
6969
}
70-
return false
70+
// For other types, check if it's the zero value
71+
return val.IsZero()
7172
}
7273

7374
// IsNestedPointer checks if a type is a nested pointer (e.g., **int, ***int).

dbos/serialization_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func testAllSerializationPaths[T any](
2626
) {
2727
t.Helper()
2828

29-
isNilExpected := isNilValue(input)
29+
isNilExpected := isNilOrZeroValue(input)
3030

3131
// Setup events for recovery
3232
startEvent := NewEvent()

dbos/workflow.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2070,6 +2070,9 @@ func (c *dbosContext) ListWorkflows(_ DBOSContext, opts ...ListWorkflowsOption)
20702070
if !ok {
20712071
return nil, fmt.Errorf("workflow input must be encoded string, got %T", workflows[i].Input)
20722072
}
2073+
if encodedInput == nil {
2074+
continue
2075+
}
20732076
decodedBytes, err := base64.StdEncoding.DecodeString(*encodedInput)
20742077
if err != nil {
20752078
return nil, fmt.Errorf("failed to decode base64 workflow input for %s: %w", workflows[i].ID, err)
@@ -2079,7 +2082,10 @@ func (c *dbosContext) ListWorkflows(_ DBOSContext, opts ...ListWorkflowsOption)
20792082
if params.loadOutput && workflows[i].Output != nil {
20802083
encodedOutput, ok := workflows[i].Output.(*string)
20812084
if !ok {
2082-
return nil, fmt.Errorf("workflow output must be encoded string, got %T", workflows[i].Output)
2085+
return nil, fmt.Errorf("workflow output must be encoded *string, got %T", workflows[i].Output)
2086+
}
2087+
if encodedOutput == nil {
2088+
continue
20832089
}
20842090
decodedBytes, err := base64.StdEncoding.DecodeString(*encodedOutput)
20852091
if err != nil {

0 commit comments

Comments
 (0)