Skip to content

Commit 6d9a160

Browse files
authored
[#605]: feature: TSA available on the StartWorkflow stage
2 parents 5cce3d5 + 914160d commit 6d9a160

8 files changed

Lines changed: 266 additions & 49 deletions

File tree

aggregatedpool/handler.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func (wp *Workflow) handleCancel() {
9999

100100
// schedule the signal processing
101101
func (wp *Workflow) handleSignal(name string, input *commonpb.Payloads, header *commonpb.Header) error {
102+
wp.log.Debug("signal request", zap.String("RunID", wp.env.WorkflowInfo().WorkflowExecution.RunID), zap.String("name", name))
102103
wp.mq.PushCommand(
103104
internal.InvokeSignal{
104105
RunID: wp.env.WorkflowInfo().WorkflowExecution.RunID,
@@ -114,6 +115,9 @@ func (wp *Workflow) handleSignal(name string, input *commonpb.Payloads, header *
114115
// Handle query in blocking mode.
115116
func (wp *Workflow) handleQuery(queryType string, queryArgs *commonpb.Payloads, header *commonpb.Header) (*commonpb.Payloads, error) {
116117
const op = errors.Op("workflow_process_handle_query")
118+
119+
wp.log.Debug("query request", zap.String("RunID", wp.env.WorkflowInfo().WorkflowExecution.RunID), zap.String("name", queryType))
120+
117121
result, err := wp.runCommand(internal.InvokeQuery{
118122
RunID: wp.env.WorkflowInfo().WorkflowExecution.RunID,
119123
Name: queryType,
@@ -355,11 +359,30 @@ func (wp *Workflow) handleMessage(msg *internal.Message) error {
355359
continue
356360
}
357361

358-
if tt, ok := v.Value.(int); ok {
359-
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(int64(tt)))
360-
} else {
362+
switch ti := v.Value.(type) {
363+
case float64:
364+
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(int64(ti)))
365+
case int:
366+
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(int64(ti)))
367+
case int64:
368+
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(ti))
369+
case int32:
370+
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(int64(ti)))
371+
case int16:
372+
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(int64(ti)))
373+
case int8:
374+
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(int64(ti)))
375+
case string:
376+
i, err := strconv.ParseInt(ti, 10, 64)
377+
if err != nil {
378+
wp.log.Warn("failed to parse int", zap.Error(err))
379+
continue
380+
}
381+
sau = append(sau, temporal.NewSearchAttributeKeyInt64(k).ValueSet(i))
382+
default:
361383
wp.log.Warn("field value is not an int type", zap.String("key", k), zap.Any("value", v.Value))
362384
}
385+
363386
case internal.KeywordType:
364387
if v.Operation == internal.TypedSearchAttributeOperationUnset {
365388
sau = append(sau, temporal.NewSearchAttributeKeyKeyword(k).ValueUnset())
@@ -387,11 +410,21 @@ func (wp *Workflow) handleMessage(msg *internal.Message) error {
387410
continue
388411
}
389412

390-
if tt, ok := v.Value.([]string); ok {
413+
switch tt := v.Value.(type) {
414+
case []string:
391415
sau = append(sau, temporal.NewSearchAttributeKeyKeywordList(k).ValueSet(tt))
392-
} else {
416+
case []any:
417+
var res []string
418+
for _, v := range tt {
419+
if s, ok := v.(string); ok {
420+
res = append(res, s)
421+
}
422+
}
423+
sau = append(sau, temporal.NewSearchAttributeKeyKeywordList(k).ValueSet(res))
424+
default:
393425
wp.log.Warn("field value is not a []string (strings array) type", zap.String("key", k), zap.Any("value", v.Value))
394426
}
427+
395428
case internal.StringType:
396429
if v.Operation == internal.TypedSearchAttributeOperationUnset {
397430
sau = append(sau, temporal.NewSearchAttributeKeyString(k).ValueUnset())

aggregatedpool/workflow.go

Lines changed: 125 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package aggregatedpool
33
import (
44
"context"
55
"fmt"
6+
"strconv"
67
"sync"
78
"sync/atomic"
89
"time"
@@ -15,6 +16,7 @@ import (
1516
"github.com/temporalio/roadrunner-temporal/v5/queue"
1617
"github.com/temporalio/roadrunner-temporal/v5/registry"
1718
commonpb "go.temporal.io/api/common/v1"
19+
enumspb "go.temporal.io/api/enums/v1"
1820
temporalClient "go.temporal.io/sdk/client"
1921
bindings "go.temporal.io/sdk/internalbindings"
2022
"go.uber.org/zap"
@@ -131,23 +133,139 @@ func (wp *Workflow) Execute(env bindings.WorkflowEnvironment, header *commonpb.H
131133
env.RegisterQueryHandler(wp.handleQuery)
132134
env.RegisterUpdateHandler(wp.handleUpdate)
133135

134-
var lastCompletion = bindings.GetLastCompletionResult(env)
135-
var lastCompletionOffset = 0
136+
// check if we have some TSA
137+
tsa := env.TypedSearchAttributes()
138+
// start workflow command
139+
stwfcmd := internal.StartWorkflow{
140+
Info: env.WorkflowInfo(),
141+
}
142+
143+
// search attributes types are:
144+
/*
145+
INDEXED_VALUE_TYPE_TEXT IndexedValueType = 1
146+
INDEXED_VALUE_TYPE_KEYWORD IndexedValueType = 2
147+
INDEXED_VALUE_TYPE_INT IndexedValueType = 3
148+
INDEXED_VALUE_TYPE_DOUBLE IndexedValueType = 4
149+
INDEXED_VALUE_TYPE_BOOL IndexedValueType = 5
150+
INDEXED_VALUE_TYPE_DATETIME IndexedValueType = 6
151+
INDEXED_VALUE_TYPE_KEYWORD_LIST IndexedValueType = 7
152+
153+
*/
154+
// only process if there're values, obviously
155+
if tsa.Size() > 0 {
156+
untuped := tsa.GetUntypedValues()
157+
tsaParsed := make(map[string]*internal.TypedSearchAttribute, tsa.Size())
158+
for k, v := range untuped {
159+
vt := k.GetValueType()
160+
switch vt {
161+
// just for the linters, should be never reached
162+
case enumspb.INDEXED_VALUE_TYPE_UNSPECIFIED:
163+
continue
164+
case enumspb.INDEXED_VALUE_TYPE_TEXT:
165+
str, ok := v.(string)
166+
if !ok {
167+
wp.log.Warn("typed search attribute found, but it is not a string", zap.String("key", k.GetName()))
168+
continue
169+
}
170+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
171+
Type: internal.StringType,
172+
Value: str,
173+
}
174+
case enumspb.INDEXED_VALUE_TYPE_KEYWORD:
175+
str, ok := v.(string)
176+
if !ok {
177+
wp.log.Warn("typed search attribute found, but it is not a string[keyword]", zap.String("key", k.GetName()))
178+
continue
179+
}
180+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
181+
Type: internal.KeywordType,
182+
Value: str,
183+
}
184+
case enumspb.INDEXED_VALUE_TYPE_INT:
185+
switch tt := v.(type) {
186+
case int:
187+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
188+
Type: internal.IntType,
189+
Value: tt,
190+
}
191+
case int64:
192+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
193+
Type: internal.IntType,
194+
Value: tt,
195+
}
196+
case string:
197+
res, err := strconv.Atoi(tt)
198+
if err != nil {
199+
wp.log.Warn("typed search attribute found, but it is not an int", zap.Error(err), zap.String("key", k.GetName()))
200+
continue
201+
}
202+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
203+
Type: internal.IntType,
204+
Value: res,
205+
}
206+
default:
207+
wp.log.Warn("typed search attribute found, but it is not an int", zap.String("key", k.GetName()))
208+
continue
209+
}
210+
case enumspb.INDEXED_VALUE_TYPE_DOUBLE:
211+
str, ok := v.(float64)
212+
if !ok {
213+
wp.log.Warn("typed search attribute found, but it is not a float64", zap.String("key", k.GetName()))
214+
continue
215+
}
216+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
217+
Type: internal.FloatType,
218+
Value: str,
219+
}
220+
case enumspb.INDEXED_VALUE_TYPE_BOOL:
221+
str, ok := v.(bool)
222+
if !ok {
223+
wp.log.Warn("typed search attribute found, but it is not a bool", zap.String("key", k.GetName()))
224+
continue
225+
}
226+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
227+
Type: internal.BoolType,
228+
Value: str,
229+
}
230+
case enumspb.INDEXED_VALUE_TYPE_DATETIME:
231+
str, ok := v.(time.Time)
232+
if !ok {
233+
wp.log.Warn("typed search attribute found, but it is not a datetime", zap.String("key", k.GetName()))
234+
continue
235+
}
236+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
237+
Type: internal.DatetimeType,
238+
Value: str.Format(time.RFC3339),
239+
}
240+
case enumspb.INDEXED_VALUE_TYPE_KEYWORD_LIST:
241+
str, ok := v.([]string)
242+
if !ok {
243+
wp.log.Warn("typed search attribute found, but it is not a []string", zap.String("key", k.GetName()))
244+
continue
245+
}
246+
tsaParsed[k.GetName()] = &internal.TypedSearchAttribute{
247+
Type: internal.KeywordListType,
248+
Value: str,
249+
}
250+
}
251+
}
136252

253+
// set typed search attributes
254+
stwfcmd.SearchAttributes = tsaParsed
255+
}
256+
257+
var lastCompletion = bindings.GetLastCompletionResult(env)
137258
if lastCompletion != nil && len(lastCompletion.Payloads) != 0 {
138259
if input == nil {
139260
input = &commonpb.Payloads{Payloads: []*commonpb.Payload{}}
140261
}
141262

142263
input.Payloads = append(input.Payloads, lastCompletion.Payloads...)
143-
lastCompletionOffset = len(lastCompletion.Payloads)
264+
stwfcmd.LastCompletion = len(lastCompletion.Payloads)
144265
}
145266

146267
wp.mq.PushCommand(
147-
internal.StartWorkflow{
148-
Info: env.WorkflowInfo(),
149-
LastCompletion: lastCompletionOffset,
150-
},
268+
stwfcmd,
151269
input,
152270
wp.header,
153271
)

go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ toolchain go1.23.4
77
require (
88
github.com/goccy/go-json v0.10.4
99
github.com/google/uuid v1.6.0
10-
github.com/roadrunner-server/api/v4 v4.17.0
10+
github.com/roadrunner-server/api/v4 v4.18.1
1111
github.com/roadrunner-server/endure/v2 v2.6.1
1212
github.com/roadrunner-server/errors v1.4.1
1313
github.com/roadrunner-server/events v1.0.1
1414
github.com/roadrunner-server/pool v1.1.2
1515
github.com/stretchr/testify v1.10.0
1616
github.com/uber-go/tally/v4 v4.1.17-0.20240412215630-22fe011f5ff0
1717
go.temporal.io/api v1.43.1
18-
go.temporal.io/sdk v1.31.0
18+
go.temporal.io/sdk v1.32.1
1919
go.temporal.io/sdk/contrib/tally v0.2.0
2020
go.temporal.io/server v1.26.2
2121
go.uber.org/zap v1.27.0
22-
google.golang.org/protobuf v1.36.2
22+
google.golang.org/protobuf v1.36.3
2323
)
2424

2525
require (
@@ -42,7 +42,7 @@ require (
4242
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
4343
github.com/klauspost/compress v1.17.11 // indirect
4444
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
45-
github.com/nexus-rpc/sdk-go v0.1.0 // indirect
45+
github.com/nexus-rpc/sdk-go v0.1.1 // indirect
4646
github.com/pborman/uuid v1.2.1 // indirect
4747
github.com/pkg/errors v0.9.1 // indirect
4848
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
@@ -67,8 +67,8 @@ require (
6767
golang.org/x/sys v0.29.0 // indirect
6868
golang.org/x/text v0.21.0 // indirect
6969
golang.org/x/time v0.9.0 // indirect
70-
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
71-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect
70+
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
71+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
7272
google.golang.org/grpc v1.69.4
7373
gopkg.in/yaml.v3 v3.0.1 // indirect
7474
)

go.sum

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
137137
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
138138
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
139139
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
140-
github.com/nexus-rpc/sdk-go v0.1.0 h1:PUL/0vEY1//WnqyEHT5ao4LBRQ6MeNUihmnNGn0xMWY=
141-
github.com/nexus-rpc/sdk-go v0.1.0/go.mod h1:TpfkM2Cw0Rlk9drGkoiSMpFqflKTiQLWUNyKJjF8mKQ=
140+
github.com/nexus-rpc/sdk-go v0.1.1 h1:S63hhn4CpmwyoCUn8nVLfuKV+6sA/7hR57ohIQajDP0=
141+
github.com/nexus-rpc/sdk-go v0.1.1/go.mod h1:TpfkM2Cw0Rlk9drGkoiSMpFqflKTiQLWUNyKJjF8mKQ=
142142
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
143143
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
144144
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
@@ -173,8 +173,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
173173
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
174174
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
175175
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
176-
github.com/roadrunner-server/api/v4 v4.17.0 h1:SVmqWfMCKcvjrpluk+mA1o1D8PT1uXHM6AWykFe6i0I=
177-
github.com/roadrunner-server/api/v4 v4.17.0/go.mod h1:ALjb4nL64DM9Cm/vTyfWyGt1b3m1lO4fg11YUq6NrqY=
176+
github.com/roadrunner-server/api/v4 v4.18.1 h1:IA01DjK7wPXkSAUc0Bg2d3hmc/XMI0DYn0BKbBju9bI=
177+
github.com/roadrunner-server/api/v4 v4.18.1/go.mod h1:VdCLIpnjKFHNspqRlu5zfPvrDS9eLR7fYy5K9HYKNkE=
178178
github.com/roadrunner-server/endure/v2 v2.6.1 h1:vx+3ayn8HXnyeCcjKWwe+DfPrwL5sOQobugzCId4F7k=
179179
github.com/roadrunner-server/endure/v2 v2.6.1/go.mod h1:1rfvKpSzUW774T3yHm3IEHPQ3SX/lU/ZTYQykfzqMF0=
180180
github.com/roadrunner-server/errors v1.4.1 h1:LKNeaCGiwd3t8IaL840ZNF3UA9yDQlpvHnKddnh0YRQ=
@@ -243,8 +243,8 @@ go.temporal.io/api v1.5.0/go.mod h1:BqKxEJJYdxb5dqf0ODfzfMxh8UEQ5L3zKS51FiIYYkA=
243243
go.temporal.io/api v1.43.1 h1:44Q12pUczfGkcAwZtJNhfv3+L6RFzL3kNk547/r8QY8=
244244
go.temporal.io/api v1.43.1/go.mod h1:1WwYUMo6lao8yl0371xWUm13paHExN5ATYT/B7QtFis=
245245
go.temporal.io/sdk v1.12.0/go.mod h1:lSp3lH1lI0TyOsus0arnO3FYvjVXBZGi/G7DjnAnm6o=
246-
go.temporal.io/sdk v1.31.0 h1:CLYiP0R5Sdj0gq8LyYKDDz4ccGOdJPR8wNGJU0JGwj8=
247-
go.temporal.io/sdk v1.31.0/go.mod h1:8U8H7rF9u4Hyb4Ry9yiEls5716DHPNvVITPNkgWUwE8=
246+
go.temporal.io/sdk v1.32.1 h1:slA8prhdFr4lxpsTcRusWVitD/cGjELfKUh0mBj73SU=
247+
go.temporal.io/sdk v1.32.1/go.mod h1:8U8H7rF9u4Hyb4Ry9yiEls5716DHPNvVITPNkgWUwE8=
248248
go.temporal.io/sdk/contrib/tally v0.2.0 h1:XnTJIQcjOv+WuCJ1u8Ve2nq+s2H4i/fys34MnWDRrOo=
249249
go.temporal.io/sdk/contrib/tally v0.2.0/go.mod h1:1kpSuCms/tHeJQDPuuKkaBsMqfHnIIRnCtUYlPNXxuE=
250250
go.temporal.io/server v1.26.2 h1:vDW11lxslYPlGDbQklWi/tqbkVZ2ExtRO1jNjvZmUUI=
@@ -377,10 +377,10 @@ google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfG
377377
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
378378
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
379379
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
380-
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
381-
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
382-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw=
383-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
380+
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
381+
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
382+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
383+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
384384
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
385385
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
386386
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
@@ -404,8 +404,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
404404
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
405405
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
406406
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
407-
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
408-
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
407+
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
408+
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
409409
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
410410
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
411411
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

0 commit comments

Comments
 (0)