@@ -76,12 +76,17 @@ var (
7676 Flags : []cli.Flag {
7777 & cli.IntFlag {
7878 Name : "limit" ,
79- Usage : "Maximum number of results (default: 50)" ,
79+ Usage : "Maximum number of results per page (default: 50)" ,
8080 Value : 50 ,
8181 },
82+ & cli.IntFlag {
83+ Name : "offset" ,
84+ Usage : "Offset for pagination (default: 0)" ,
85+ Value : 0 ,
86+ },
8287 & cli.StringSliceFlag {
8388 Name : "status" ,
84- Usage : "Filter by status(es) (active, pending, released). Multiple values can be specified." ,
89+ Usage : "Filter by status(es) (active, pending, released, offline ). Multiple values can be specified." ,
8590 },
8691 & cli.StringFlag {
8792 Name : "sip-dispatch-rule-id" ,
@@ -161,36 +166,6 @@ func createPhoneNumberClient(ctx context.Context, cmd *cli.Command) (*lksdk.Phon
161166 return lksdk .NewPhoneNumberClient (project .URL , project .APIKey , project .APISecret , withDefaultClientOpts (project )... ), nil
162167}
163168
164- // getPhoneNumberToDispatchRulesMap fetches all dispatch rules and maps phone number IDs to their associated dispatch rule IDs
165- // Returns a map where key is phone number ID and value is a slice of dispatch rule IDs
166- func getPhoneNumberToDispatchRulesMap (ctx context.Context , cmd * cli.Command ) (map [string ][]string , error ) {
167- _ , err := requireProject (ctx , cmd )
168- if err != nil {
169- return nil , fmt .Errorf ("failed to get project: %w" , err )
170- }
171-
172- sipClient := lksdk .NewSIPClient (project .URL , project .APIKey , project .APISecret , withDefaultClientOpts (project )... )
173-
174- // List all dispatch rules
175- resp , err := sipClient .ListSIPDispatchRule (ctx , & livekit.ListSIPDispatchRuleRequest {})
176- if err != nil {
177- return nil , fmt .Errorf ("failed to list dispatch rules: %w" , err )
178- }
179-
180- // Build map: phone number ID -> []dispatch rule IDs
181- phoneNumberToRules := make (map [string ][]string )
182- for _ , rule := range resp .Items {
183- for _ , trunkID := range rule .TrunkIds {
184- // Check if trunkID is a phone number ID (starts with PN_PPN_)
185- if strings .HasPrefix (trunkID , "PN_PPN_" ) {
186- phoneNumberToRules [trunkID ] = append (phoneNumberToRules [trunkID ], rule .SipDispatchRuleId )
187- }
188- }
189- }
190-
191- return phoneNumberToRules , nil
192- }
193-
194169func searchPhoneNumbers (ctx context.Context , cmd * cli.Command ) error {
195170 client , err := createPhoneNumberClient (ctx , cmd )
196171 if err != nil {
@@ -265,65 +240,9 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error {
265240 req .SipDispatchRuleId = & dispatchRuleID
266241 }
267242
268- // Call purchase and get dispatch rules in parallel
269- type purchaseResult struct {
270- resp * livekit.PurchasePhoneNumberResponse
271- err error
272- }
273- type dispatchRulesResult struct {
274- rules map [string ][]string
275- err error
276- }
277-
278- purchaseChan := make (chan purchaseResult , 1 )
279- dispatchRulesChan := make (chan dispatchRulesResult , 1 )
280-
281- // Purchase phone numbers
282- go func () {
283- resp , err := client .PurchasePhoneNumber (ctx , req )
284- purchaseChan <- purchaseResult {resp : resp , err : err }
285- }()
286-
287- // Get dispatch rules mapping in parallel
288- go func () {
289- rules , err := getPhoneNumberToDispatchRulesMap (ctx , cmd )
290- dispatchRulesChan <- dispatchRulesResult {rules : rules , err : err }
291- }()
292-
293- // Wait for purchase to complete
294- purchaseRes := <- purchaseChan
295- if purchaseRes .err != nil {
296- return purchaseRes .err
297- }
298- resp := purchaseRes .resp
299-
300- // Wait for dispatch rules (ignore errors, we'll just not show them)
301- dispatchRulesRes := <- dispatchRulesChan
302- phoneNumberToRules := dispatchRulesRes .rules
303- if dispatchRulesRes .err != nil {
304- // Log but don't fail
305- if cmd .Bool ("verbose" ) {
306- fmt .Fprintf (cmd .ErrWriter , "Warning: failed to get dispatch rules: %v\n " , dispatchRulesRes .err )
307- }
308- phoneNumberToRules = make (map [string ][]string )
309- }
310-
311- // If dispatch rule ID was provided, add it to the mapping for display
312- // (The actual update is now handled by cloud-io)
313- if dispatchRuleID != "" {
314- for _ , phoneNumber := range resp .PhoneNumbers {
315- // Check if dispatchRuleID is already in the list
316- found := false
317- for _ , ruleID := range phoneNumberToRules [phoneNumber .Id ] {
318- if ruleID == dispatchRuleID {
319- found = true
320- break
321- }
322- }
323- if ! found {
324- phoneNumberToRules [phoneNumber .Id ] = append (phoneNumberToRules [phoneNumber .Id ], dispatchRuleID )
325- }
326- }
243+ resp , err := client .PurchasePhoneNumber (ctx , req )
244+ if err != nil {
245+ return err
327246 }
328247
329248 if cmd .Bool ("json" ) {
@@ -334,9 +253,8 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error {
334253 fmt .Printf ("Successfully purchased %d phone numbers:\n " , len (resp .PhoneNumbers ))
335254 for _ , phoneNumber := range resp .PhoneNumbers {
336255 ruleInfo := ""
337- rules := phoneNumberToRules [phoneNumber .Id ]
338- if len (rules ) > 0 {
339- ruleInfo = fmt .Sprintf (" (SIP Dispatch Rules: %s)" , strings .Join (rules , ", " ))
256+ if len (phoneNumber .SipDispatchRuleIds ) > 0 {
257+ ruleInfo = fmt .Sprintf (" (SIP Dispatch Rules: %s)" , strings .Join (phoneNumber .SipDispatchRuleIds , ", " ))
340258 }
341259 fmt .Printf (" %s (%s) - %s%s\n " , phoneNumber .E164Format , phoneNumber .Id , strings .TrimPrefix (phoneNumber .Status .String (), "PHONE_NUMBER_STATUS_" ), ruleInfo )
342260 }
@@ -351,10 +269,17 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error {
351269 }
352270
353271 req := & livekit.ListPhoneNumbersRequest {}
354- if val := cmd .Int ("limit" ); val != 0 {
355- limit := int32 (val )
356- req .Limit = & limit
272+ limit := int32 (cmd .Int ("limit" ))
273+ offset := int32 (cmd .Int ("offset" ))
274+
275+ // Encode offset and limit into a page token for pagination
276+ // Even if offset is 0, we encode it to include the limit in the token
277+ pageToken , err := livekit .EncodeTokenPagination (offset , limit )
278+ if err != nil {
279+ return fmt .Errorf ("failed to encode pagination token: %w" , err )
357280 }
281+ req .PageToken = pageToken
282+
358283 if statuses := cmd .StringSlice ("status" ); len (statuses ) > 0 {
359284 var phoneNumberStatuses []livekit.PhoneNumberStatus
360285 for _ , status := range statuses {
@@ -370,66 +295,41 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error {
370295 req .SipDispatchRuleId = & val
371296 }
372297
373- // Call list and get dispatch rules in parallel
374- type listResult struct {
375- resp * livekit.ListPhoneNumbersResponse
376- err error
377- }
378- type dispatchRulesResult struct {
379- rules map [string ][]string
380- err error
298+ resp , err := client .ListPhoneNumbers (ctx , req )
299+ if err != nil {
300+ return err
381301 }
382302
383- listChan := make (chan listResult , 1 )
384- dispatchRulesChan := make (chan dispatchRulesResult , 1 )
385-
386- // List phone numbers
387- go func () {
388- resp , err := client .ListPhoneNumbers (ctx , req )
389- listChan <- listResult {resp : resp , err : err }
390- }()
391-
392- // Get dispatch rules mapping in parallel
393- go func () {
394- rules , err := getPhoneNumberToDispatchRulesMap (ctx , cmd )
395- dispatchRulesChan <- dispatchRulesResult {rules : rules , err : err }
396- }()
397-
398- // Wait for list to complete
399- listRes := <- listChan
400- if listRes .err != nil {
401- return listRes .err
303+ if cmd .Bool ("json" ) {
304+ util .PrintJSON (resp )
305+ return nil
402306 }
403- resp := listRes .resp
404307
405- // Wait for dispatch rules (ignore errors, we'll just not show them)
406- dispatchRulesRes := <- dispatchRulesChan
407- phoneNumberToRules := dispatchRulesRes .rules
408- if dispatchRulesRes .err != nil {
409- // Log but don't fail
410- if cmd .Bool ("verbose" ) {
411- fmt .Fprintf (cmd .ErrWriter , "Warning: failed to get dispatch rules: %v\n " , dispatchRulesRes .err )
412- }
413- phoneNumberToRules = make (map [string ][]string )
308+ fmt .Printf ("Total phone numbers: %d" , resp .TotalCount )
309+ if resp .OfflineCount > 0 {
310+ fmt .Printf (" (%d offline)" , resp .OfflineCount )
414311 }
312+ fmt .Printf ("\n " )
415313
416- if cmd .Bool ("json" ) {
417- util .PrintJSON (resp )
418- return nil
314+ // Show pagination info
315+ if offset > 0 {
316+ fmt .Printf ("Showing results from offset %d\n " , offset )
317+ }
318+ if resp .NextPageToken != nil {
319+ nextOffset , _ , err := livekit .DecodeTokenPagination (resp .NextPageToken )
320+ if err == nil {
321+ fmt .Printf ("More results available. Use --offset %d to see the next page.\n " , nextOffset )
322+ }
419323 }
420324
421- fmt .Printf ("Total phone numbers: %d\n " , resp .TotalCount )
422325 return listAndPrint (ctx , cmd , func (ctx context.Context , req * livekit.ListPhoneNumbersRequest ) (* livekit.ListPhoneNumbersResponse , error ) {
423326 return client .ListPhoneNumbers (ctx , req )
424327 }, req , []string {
425328 "ID" , "E164" , "Country" , "Area Code" , "Type" , "Locality" , "Region" , "Capabilities" , "Status" , "SIP Dispatch Rules" ,
426329 }, func (item * livekit.PhoneNumber ) []string {
427- rules := phoneNumberToRules [item .Id ]
428330 dispatchRulesStr := ""
429- if len (rules ) > 0 {
430- dispatchRulesStr = strings .Join (rules , ", " )
431- } else if item .SipDispatchRuleId != "" {
432- dispatchRulesStr = item .SipDispatchRuleId
331+ if len (item .SipDispatchRuleIds ) > 0 {
332+ dispatchRulesStr = strings .Join (item .SipDispatchRuleIds , ", " )
433333 } else {
434334 dispatchRulesStr = "-"
435335 }
@@ -471,47 +371,9 @@ func getPhoneNumber(ctx context.Context, cmd *cli.Command) error {
471371 req .PhoneNumber = & phoneNumber
472372 }
473373
474- // Call get and get dispatch rules in parallel
475- type getResult struct {
476- resp * livekit.GetPhoneNumberResponse
477- err error
478- }
479- type dispatchRulesResult struct {
480- rules map [string ][]string
481- err error
482- }
483-
484- getChan := make (chan getResult , 1 )
485- dispatchRulesChan := make (chan dispatchRulesResult , 1 )
486-
487- // Get phone number
488- go func () {
489- resp , err := client .GetPhoneNumber (ctx , req )
490- getChan <- getResult {resp : resp , err : err }
491- }()
492-
493- // Get dispatch rules mapping in parallel
494- go func () {
495- rules , err := getPhoneNumberToDispatchRulesMap (ctx , cmd )
496- dispatchRulesChan <- dispatchRulesResult {rules : rules , err : err }
497- }()
498-
499- // Wait for get to complete
500- getRes := <- getChan
501- if getRes .err != nil {
502- return getRes .err
503- }
504- resp := getRes .resp
505-
506- // Wait for dispatch rules (ignore errors, we'll just not show them)
507- dispatchRulesRes := <- dispatchRulesChan
508- phoneNumberToRules := dispatchRulesRes .rules
509- if dispatchRulesRes .err != nil {
510- // Log but don't fail
511- if cmd .Bool ("verbose" ) {
512- fmt .Fprintf (cmd .ErrWriter , "Warning: failed to get dispatch rules: %v\n " , dispatchRulesRes .err )
513- }
514- phoneNumberToRules = make (map [string ][]string )
374+ resp , err := client .GetPhoneNumber (ctx , req )
375+ if err != nil {
376+ return err
515377 }
516378
517379 if cmd .Bool ("json" ) {
@@ -520,12 +382,9 @@ func getPhoneNumber(ctx context.Context, cmd *cli.Command) error {
520382 }
521383
522384 item := resp .PhoneNumber
523- rules := phoneNumberToRules [item .Id ]
524385 dispatchRulesStr := ""
525- if len (rules ) > 0 {
526- dispatchRulesStr = strings .Join (rules , ", " )
527- } else if item .SipDispatchRuleId != "" {
528- dispatchRulesStr = item .SipDispatchRuleId
386+ if len (item .SipDispatchRuleIds ) > 0 {
387+ dispatchRulesStr = strings .Join (item .SipDispatchRuleIds , ", " )
529388 } else {
530389 dispatchRulesStr = "-"
531390 }
@@ -576,64 +435,9 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error {
576435 req .SipDispatchRuleId = & dispatchRuleID
577436 }
578437
579- // Call update and get dispatch rules in parallel
580- type updateResult struct {
581- resp * livekit.UpdatePhoneNumberResponse
582- err error
583- }
584- type dispatchRulesResult struct {
585- rules map [string ][]string
586- err error
587- }
588-
589- updateChan := make (chan updateResult , 1 )
590- dispatchRulesChan := make (chan dispatchRulesResult , 1 )
591-
592- // Update phone number
593- go func () {
594- resp , err := client .UpdatePhoneNumber (ctx , req )
595- updateChan <- updateResult {resp : resp , err : err }
596- }()
597-
598- // Get dispatch rules mapping in parallel
599- go func () {
600- rules , err := getPhoneNumberToDispatchRulesMap (ctx , cmd )
601- dispatchRulesChan <- dispatchRulesResult {rules : rules , err : err }
602- }()
603-
604- // Wait for update to complete
605- updateRes := <- updateChan
606- if updateRes .err != nil {
607- return updateRes .err
608- }
609- resp := updateRes .resp
610-
611- // Wait for dispatch rules (ignore errors, we'll just not show them)
612- dispatchRulesRes := <- dispatchRulesChan
613- phoneNumberToRules := dispatchRulesRes .rules
614- if dispatchRulesRes .err != nil {
615- // Log but don't fail
616- if cmd .Bool ("verbose" ) {
617- fmt .Fprintf (cmd .ErrWriter , "Warning: failed to get dispatch rules: %v\n " , dispatchRulesRes .err )
618- }
619- phoneNumberToRules = make (map [string ][]string )
620- }
621-
622- // If dispatch rule ID was provided, add it to the mapping for display
623- // (The actual update is now handled by cloud-io)
624- if dispatchRuleID != "" {
625- phoneNumberID := resp .PhoneNumber .Id
626- // Check if dispatchRuleID is already in the list
627- found := false
628- for _ , ruleID := range phoneNumberToRules [phoneNumberID ] {
629- if ruleID == dispatchRuleID {
630- found = true
631- break
632- }
633- }
634- if ! found {
635- phoneNumberToRules [phoneNumberID ] = append (phoneNumberToRules [phoneNumberID ], dispatchRuleID )
636- }
438+ resp , err := client .UpdatePhoneNumber (ctx , req )
439+ if err != nil {
440+ return err
637441 }
638442
639443 if cmd .Bool ("json" ) {
@@ -642,10 +446,9 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error {
642446 }
643447
644448 item := resp .PhoneNumber
645- rules := phoneNumberToRules [item .Id ]
646449 dispatchRulesStr := ""
647- if len (rules ) > 0 {
648- dispatchRulesStr = strings .Join (rules , ", " )
450+ if len (item . SipDispatchRuleIds ) > 0 {
451+ dispatchRulesStr = strings .Join (item . SipDispatchRuleIds , ", " )
649452 } else {
650453 dispatchRulesStr = "-"
651454 }
0 commit comments