@@ -2,6 +2,7 @@ package keeper
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67
78 "google.golang.org/grpc/codes"
@@ -12,7 +13,7 @@ import (
1213 sdk "github.com/cosmos/cosmos-sdk/types"
1314 sdkquery "github.com/cosmos/cosmos-sdk/types/query"
1415
15- "pkg.akt.dev/go/node/deployment/v1"
16+ v1 "pkg.akt.dev/go/node/deployment/v1"
1617 types "pkg.akt.dev/go/node/deployment/v1beta4"
1718
1819 "pkg.akt.dev/node/util/query"
@@ -44,14 +45,17 @@ func (k Querier) Deployments(c context.Context, req *types.QueryDeploymentsReque
4445
4546 ctx := sdk .UnwrapSDKContext (c )
4647
48+ // Step 1: Resolve states, resumePK, and iteration path
4749 states := make ([]byte , 0 , 2 )
4850 var resumePK * keys.DeploymentPrimaryKey
51+ var ownerPath bool
52+ var owner string
4953
50- // nolint: gocritic
5154 if len (req .Pagination .Key ) > 0 {
52- var pkBytes []byte
55+ // RESUME — all filters ignored, key provides everything
56+ var pkBytes , unsolicited []byte
5357 var err error
54- states , _ , pkBytes , _ , err = query .DecodePaginationKey (req .Pagination .Key )
58+ states , _ , pkBytes , unsolicited , err = query .DecodePaginationKey (req .Pagination .Key )
5559 if err != nil {
5660 return nil , status .Error (codes .InvalidArgument , err .Error ())
5761 }
@@ -61,24 +65,208 @@ func (k Querier) Deployments(c context.Context, req *types.QueryDeploymentsReque
6165 return nil , status .Error (codes .Internal , err .Error ())
6266 }
6367 resumePK = & pk
64- } else if req .Filters .State != "" {
65- stateVal := v1 .Deployment_State (v1 .Deployment_State_value [req .Filters .State ])
6668
67- if stateVal == v1 .DeploymentStateInvalid {
68- return nil , status .Error (codes .InvalidArgument , "invalid state value" )
69+ if len (unsolicited ) > 0 && unsolicited [0 ] == 1 {
70+ ownerPath = true
71+ owner = resumePK .K1 ()
6972 }
70-
71- states = append (states , byte (stateVal ))
7273 } else {
73- states = append (states , byte (v1 .DeploymentActive ), byte (v1 .DeploymentClosed ))
74+ // INITIAL — resolve states from Filters.State
75+ if req .Filters .State != "" {
76+ stateVal := v1 .Deployment_State (v1 .Deployment_State_value [req .Filters .State ])
77+ if stateVal == v1 .DeploymentStateInvalid {
78+ return nil , status .Error (codes .InvalidArgument , "invalid state value" )
79+ }
80+ states = append (states , byte (stateVal ))
81+ } else {
82+ states = append (states , byte (v1 .DeploymentActive ), byte (v1 .DeploymentClosed ))
83+ }
84+
85+ // Resolve iteration path from Filters.Owner
86+ if req .Filters .Owner != "" {
87+ ownerPath = true
88+ owner = req .Filters .Owner
89+ }
90+ }
91+
92+ // Step 2: Direct Get path — Owner + DSeq = full PK known
93+ if ownerPath && resumePK == nil && req .Filters .DSeq != 0 {
94+ return k .deploymentsDirectGet (ctx , req , states )
95+ }
96+
97+ // Step 3: Owner path — iterate primary map with owner prefix
98+ if ownerPath {
99+ return k .deploymentsOwnerPath (ctx , req , states , owner , resumePK )
74100 }
75101
102+ // Step 4: State-index path — iterate by state index
76103 if len (req .Pagination .Key ) == 0 && req .Pagination .Reverse {
77104 for i , j := 0 , len (states )- 1 ; i < j ; i , j = i + 1 , j - 1 {
78105 states [i ], states [j ] = states [j ], states [i ]
79106 }
80107 }
81108
109+ return k .deploymentsStatePath (ctx , req , states , resumePK )
110+ }
111+
112+ // deploymentsDirectGet handles the case where Owner + DSeq are both set, giving a full primary key.
113+ func (k Querier ) deploymentsDirectGet (
114+ ctx sdk.Context ,
115+ req * types.QueryDeploymentsRequest ,
116+ states []byte ,
117+ ) (* types.QueryDeploymentsResponse , error ) {
118+ pk := collections .Join (req .Filters .Owner , req .Filters .DSeq )
119+ deployment , err := k .deployments .Get (ctx , pk )
120+ if err != nil {
121+ if errors .Is (err , collections .ErrNotFound ) {
122+ return & types.QueryDeploymentsResponse {
123+ Pagination : & sdkquery.PageResponse {},
124+ }, nil
125+ }
126+ return nil , status .Error (codes .Internal , err .Error ())
127+ }
128+
129+ // Check state filter
130+ stateMatch := false
131+ for _ , s := range states {
132+ if v1 .Deployment_State (s ) == deployment .State {
133+ stateMatch = true
134+ break
135+ }
136+ }
137+ if ! stateMatch {
138+ return & types.QueryDeploymentsResponse {
139+ Pagination : & sdkquery.PageResponse {},
140+ }, nil
141+ }
142+
143+ account , err := k .ekeeper .GetAccount (ctx , deployment .ID .ToEscrowAccountID ())
144+ if err != nil {
145+ return nil , status .Error (codes .Internal , fmt .Sprintf ("fetching escrow account for DeploymentID=%s: %v" , deployment .ID , err ))
146+ }
147+
148+ groups , err := k .GetGroups (ctx , deployment .ID )
149+ if err != nil {
150+ return nil , status .Error (codes .Internal , fmt .Sprintf ("fetching groups for DeploymentID=%s: %v" , deployment .ID , err ))
151+ }
152+
153+ return & types.QueryDeploymentsResponse {
154+ Deployments : types.DeploymentResponses {
155+ {
156+ Deployment : deployment ,
157+ Groups : groups ,
158+ EscrowAccount : account ,
159+ },
160+ },
161+ Pagination : & sdkquery.PageResponse {
162+ Total : 1 ,
163+ },
164+ }, nil
165+ }
166+
167+ // deploymentsOwnerPath iterates the primary map with an owner prefix.
168+ // States are filtered in the Walk callback.
169+ func (k Querier ) deploymentsOwnerPath (
170+ ctx sdk.Context ,
171+ req * types.QueryDeploymentsRequest ,
172+ states []byte ,
173+ owner string ,
174+ resumePK * keys.DeploymentPrimaryKey ,
175+ ) (* types.QueryDeploymentsResponse , error ) {
176+ // Build state set for callback filtering
177+ stateSet := make (map [v1.Deployment_State ]bool , len (states ))
178+ for _ , s := range states {
179+ stateSet [v1 .Deployment_State (s )] = true
180+ }
181+
182+ // Build range on primary map
183+ ownerRange := collections.NewPrefixedPairRange [string , uint64 ](owner )
184+
185+ var r * collections.PairRange [string , uint64 ]
186+ if resumePK != nil {
187+ if req .Pagination .Reverse {
188+ r = collections.NewPrefixedPairRange [string , uint64 ](owner ).EndInclusive (resumePK .K2 ()).Descending ()
189+ } else {
190+ r = collections.NewPrefixedPairRange [string , uint64 ](owner ).StartInclusive (resumePK .K2 ())
191+ }
192+ } else if req .Pagination .Reverse {
193+ r = ownerRange .Descending ()
194+ } else {
195+ r = ownerRange
196+ }
197+
198+ var deployments types.DeploymentResponses
199+ var nextKey []byte
200+ total := uint64 (0 )
201+ offset := req .Pagination .Offset
202+
203+ walkErr := k .deployments .Walk (ctx , r , func (_ keys.DeploymentPrimaryKey , deployment v1.Deployment ) (bool , error ) {
204+ // State filter
205+ if ! stateSet [deployment .State ] {
206+ return false , nil
207+ }
208+
209+ // Offset
210+ if offset > 0 {
211+ offset --
212+ return false , nil
213+ }
214+
215+ // Page full — encode NextKey
216+ if req .Pagination .Limit == 0 {
217+ npk := keys .DeploymentIDToKey (deployment .ID )
218+ pkBuf := make ([]byte , k .deployments .KeyCodec ().Size (npk ))
219+ if _ , err := k .deployments .KeyCodec ().Encode (pkBuf , npk ); err != nil {
220+ return true , err
221+ }
222+ var err error
223+ nextKey , err = query .EncodePaginationKey (states , []byte {states [0 ]}, pkBuf , []byte {1 })
224+ if err != nil {
225+ return true , err
226+ }
227+ return true , nil
228+ }
229+
230+ // Collect result
231+ account , err := k .ekeeper .GetAccount (ctx , deployment .ID .ToEscrowAccountID ())
232+ if err != nil {
233+ return true , fmt .Errorf ("%w: fetching escrow account for DeploymentID=%s" , err , deployment .ID )
234+ }
235+
236+ groups , err := k .GetGroups (ctx , deployment .ID )
237+ if err != nil {
238+ return true , fmt .Errorf ("%w: fetching groups for DeploymentID=%s" , err , deployment .ID )
239+ }
240+
241+ deployments = append (deployments , types.QueryDeploymentResponse {
242+ Deployment : deployment ,
243+ Groups : groups ,
244+ EscrowAccount : account ,
245+ })
246+ req .Pagination .Limit --
247+ total ++
248+ return false , nil
249+ })
250+ if walkErr != nil {
251+ return nil , status .Error (codes .Internal , walkErr .Error ())
252+ }
253+
254+ return & types.QueryDeploymentsResponse {
255+ Deployments : deployments ,
256+ Pagination : & sdkquery.PageResponse {
257+ Total : total ,
258+ NextKey : nextKey ,
259+ },
260+ }, nil
261+ }
262+
263+ // deploymentsStatePath iterates deployments via the State index.
264+ func (k Querier ) deploymentsStatePath (
265+ ctx sdk.Context ,
266+ req * types.QueryDeploymentsRequest ,
267+ states []byte ,
268+ resumePK * keys.DeploymentPrimaryKey ,
269+ ) (* types.QueryDeploymentsResponse , error ) {
82270 var deployments types.DeploymentResponses
83271 var nextKey []byte
84272 total := uint64 (0 )
0 commit comments