Skip to content

Commit 2d53a10

Browse files
authored
Fix workflow list MongoDB fallback (#1608)
* Fix workflow list MongoDB fallback The CLI's direct MongoDB fallback (used when gRPC ListInstanceIDs is unavailable) searched for a "key" field, but the Dapr MongoDB state store stores document keys in the "_id" field. This caused `dapr workflow list -c 'mongodb://...'` to always return empty results. Signed-off-by: joshvanl <me@joshvanl.dev> * Review comments Signed-off-by: joshvanl <me@joshvanl.dev> * Review comments Signed-off-by: joshvanl <me@joshvanl.dev> --------- Signed-off-by: joshvanl <me@joshvanl.dev>
1 parent 27de98f commit 2d53a10

2 files changed

Lines changed: 94 additions & 3 deletions

File tree

pkg/workflow/db/mongo.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ func ListMongo(ctx context.Context, db *mongo.Database, collection string, opts
4747
regex := fmt.Sprintf("^%s.*%s$", prefix, suffix)
4848

4949
filter := bson.M{
50-
"key": bson.M{
50+
"_id": bson.M{
5151
"$regex": regex,
5252
"$options": "",
5353
},
5454
}
5555

56-
findOpts := options.Find().SetProjection(bson.M{"_id": 0, "key": 1})
56+
findOpts := options.Find().SetProjection(bson.M{"_id": 1})
5757

5858
cur, err := coll.Find(ctx, filter, findOpts)
5959
if err != nil {
@@ -64,7 +64,7 @@ func ListMongo(ctx context.Context, db *mongo.Database, collection string, opts
6464
var keys []string
6565
for cur.Next(ctx) {
6666
var doc struct {
67-
Key string `bson:"key"`
67+
Key string `bson:"_id"`
6868
}
6969
if err := cur.Decode(&doc); err != nil {
7070
return nil, err

pkg/workflow/db/mongo_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
Copyright 2026 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package db
15+
16+
import (
17+
"testing"
18+
19+
"github.com/stretchr/testify/assert"
20+
"github.com/stretchr/testify/require"
21+
"go.mongodb.org/mongo-driver/bson"
22+
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
23+
)
24+
25+
const testCollection = "daprCollection"
26+
27+
func TestListMongo(t *testing.T) {
28+
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
29+
30+
mt.Run("returns keys from _id field", func(mt *mtest.T) {
31+
key1 := "myapp||dapr.internal.default.myapp.workflow||instance-1||metadata"
32+
key2 := "myapp||dapr.internal.default.myapp.workflow||instance-2||metadata"
33+
34+
ns := mt.DB.Name() + "." + testCollection
35+
mt.AddMockResponses(mtest.CreateCursorResponse(1, ns, mtest.FirstBatch,
36+
bson.D{{Key: "_id", Value: key1}},
37+
), mtest.CreateCursorResponse(0, ns, mtest.NextBatch,
38+
bson.D{{Key: "_id", Value: key2}},
39+
))
40+
41+
keys, err := ListMongo(mt.Context(), mt.DB, testCollection, ListOptions{
42+
Namespace: "default",
43+
AppID: "myapp",
44+
})
45+
require.NoError(mt, err)
46+
assert.Equal(mt, []string{key1, key2}, keys)
47+
})
48+
49+
mt.Run("returns nil when no documents match", func(mt *mtest.T) {
50+
ns := mt.DB.Name() + "." + testCollection
51+
mt.AddMockResponses(mtest.CreateCursorResponse(0, ns, mtest.FirstBatch))
52+
53+
keys, err := ListMongo(mt.Context(), mt.DB, testCollection, ListOptions{
54+
Namespace: "default",
55+
AppID: "myapp",
56+
})
57+
require.NoError(mt, err)
58+
assert.Nil(mt, keys)
59+
})
60+
61+
mt.Run("filter uses _id field with correct regex", func(mt *mtest.T) {
62+
ns := mt.DB.Name() + "." + testCollection
63+
mt.AddMockResponses(mtest.CreateCursorResponse(0, ns, mtest.FirstBatch))
64+
65+
_, err := ListMongo(mt.Context(), mt.DB, testCollection, ListOptions{
66+
Namespace: "default",
67+
AppID: "myapp",
68+
})
69+
require.NoError(mt, err)
70+
71+
// Verify the find command was sent with _id filter.
72+
cmd := mt.GetStartedEvent().Command
73+
filterVal := cmd.Lookup("filter")
74+
filterDoc := filterVal.Document()
75+
76+
// The filter should contain an "_id" field, not "key".
77+
idVal, err := filterDoc.LookupErr("_id")
78+
assert.NoError(mt, err, "filter should query on _id field")
79+
_, err = filterDoc.LookupErr("key")
80+
assert.Error(mt, err, "filter should not query on key field")
81+
82+
// Verify the $regex value matches the expected workflow metadata pattern.
83+
idDoc := idVal.Document()
84+
regexVal, err := idDoc.LookupErr("$regex")
85+
require.NoError(mt, err, "filter on _id should use $regex")
86+
assert.Equal(mt,
87+
`^myapp\|\|dapr\.internal\.default\.myapp\.workflow\|\|.*\|\|metadata$`,
88+
regexVal.StringValue(),
89+
)
90+
})
91+
}

0 commit comments

Comments
 (0)