From 4b37358ded04a6db69f3030fdabfc2062702af85 Mon Sep 17 00:00:00 2001 From: Nishad Musthafa Date: Wed, 7 Jan 2026 09:30:16 -0800 Subject: [PATCH 1/2] Incorporating changes in the list phone number api --- cmd/lk/phone_number.go | 40 ++++++++++++++++++++++++++++++++++------ go.mod | 4 ++-- go.sum | 8 ++++---- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/cmd/lk/phone_number.go b/cmd/lk/phone_number.go index ade65126..e625235f 100644 --- a/cmd/lk/phone_number.go +++ b/cmd/lk/phone_number.go @@ -76,12 +76,17 @@ var ( Flags: []cli.Flag{ &cli.IntFlag{ Name: "limit", - Usage: "Maximum number of results (default: 50)", + Usage: "Maximum number of results per page (default: 50)", Value: 50, }, + &cli.IntFlag{ + Name: "offset", + Usage: "Offset for pagination (default: 0)", + Value: 0, + }, &cli.StringSliceFlag{ Name: "status", - Usage: "Filter by status(es) (active, pending, released). Multiple values can be specified.", + Usage: "Filter by status(es) (active, pending, released, offline). Multiple values can be specified.", }, &cli.StringFlag{ Name: "sip-dispatch-rule-id", @@ -351,10 +356,17 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error { } req := &livekit.ListPhoneNumbersRequest{} - if val := cmd.Int("limit"); val != 0 { - limit := int32(val) - req.Limit = &limit + limit := int32(cmd.Int("limit")) + offset := int32(cmd.Int("offset")) + + // Encode offset and limit into a page token for pagination + // Even if offset is 0, we encode it to include the limit in the token + pageToken, err := livekit.EncodeTokenPagination(offset, limit) + if err != nil { + return fmt.Errorf("failed to encode pagination token: %w", err) } + req.PageToken = pageToken + if statuses := cmd.StringSlice("status"); len(statuses) > 0 { var phoneNumberStatuses []livekit.PhoneNumberStatus for _, status := range statuses { @@ -418,7 +430,23 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error { return nil } - fmt.Printf("Total phone numbers: %d\n", resp.TotalCount) + fmt.Printf("Total phone numbers: %d", resp.TotalCount) + if resp.OfflineCount > 0 { + fmt.Printf(" (%d offline)", resp.OfflineCount) + } + fmt.Printf("\n") + + // Show pagination info + if offset > 0 { + fmt.Printf("Showing results from offset %d\n", offset) + } + if resp.NextPageToken != nil { + nextOffset, _, err := livekit.DecodeTokenPagination(resp.NextPageToken) + if err == nil { + fmt.Printf("More results available. Use --offset %d to see the next page.\n", nextOffset) + } + } + return listAndPrint(ctx, cmd, func(ctx context.Context, req *livekit.ListPhoneNumbersRequest) (*livekit.ListPhoneNumbersResponse, error) { return client.ListPhoneNumbers(ctx, req) }, req, []string{ diff --git a/go.mod b/go.mod index 055832f6..380d0e59 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/go-logr/logr v1.4.3 github.com/go-task/task/v3 v3.44.1 github.com/joho/godotenv v1.5.1 - github.com/livekit/protocol v1.43.1-0.20251112192040-8bdcb749c402 + github.com/livekit/protocol v1.43.3-0.20251127030154-5d65f30ee664 github.com/livekit/server-sdk-go/v2 v2.12.9-0.20251112193538-ed23dded48f9 github.com/moby/patternmatcher v0.6.0 github.com/pelletier/go-toml v1.9.5 @@ -113,7 +113,7 @@ require ( github.com/lithammer/shortuuid/v4 v4.2.0 // indirect github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 // indirect github.com/livekit/mediatransportutil v0.0.0-20250922175932-f537f0880397 // indirect - github.com/livekit/psrpc v0.7.1-0.20251105165826-1016ad610a7e // indirect + github.com/livekit/psrpc v0.7.1 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magefile/mage v1.15.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect diff --git a/go.sum b/go.sum index 0d866eb2..d984f6a5 100644 --- a/go.sum +++ b/go.sum @@ -267,10 +267,10 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20250922175932-f537f0880397 h1:Z7j2mY+bvG05UC80MpnJkitlJju8sSDWsr0Bb4dPceo= github.com/livekit/mediatransportutil v0.0.0-20250922175932-f537f0880397/go.mod h1:mSNtYzSf6iY9xM3UX42VEI+STHvMgHmrYzEHPcdhB8A= -github.com/livekit/protocol v1.43.1-0.20251112192040-8bdcb749c402 h1:mMIddIE1wlvgwIDYZ48M4eptU4dzA2DqzWzeT7OTkcY= -github.com/livekit/protocol v1.43.1-0.20251112192040-8bdcb749c402/go.mod h1:TpqU2qCI1ES4Lk7PAWSgYO4RaexfVXb54ZO2hXv0Bmc= -github.com/livekit/psrpc v0.7.1-0.20251105165826-1016ad610a7e h1:K6GKMFGJW8U2RoEyi8Zar7pPwUB7RMsn3AEJXf/Mgfc= -github.com/livekit/psrpc v0.7.1-0.20251105165826-1016ad610a7e/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= +github.com/livekit/protocol v1.43.3-0.20251127030154-5d65f30ee664 h1:CfKrVfzkGDvN9XeKqjt/Ap4sEbeUiLGSo3hgMikBDXg= +github.com/livekit/protocol v1.43.3-0.20251127030154-5d65f30ee664/go.mod h1:yjkL2/HcaCRyHykP9rLgKST2099AGd8laaU8EuHMnfw= +github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw= +github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= github.com/livekit/server-sdk-go/v2 v2.12.9-0.20251112193538-ed23dded48f9 h1:NjL66OFpkQJMhdvi0WCGWaPUr6XE1JGQwyKpNXWj4hg= github.com/livekit/server-sdk-go/v2 v2.12.9-0.20251112193538-ed23dded48f9/go.mod h1:YGjcmx4bSoEWGFqd1nqfVw8a+KQuOHXpr/prKtWRZaQ= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= From fb81a739bb53e05fa048c32976432da0894166a0 Mon Sep 17 00:00:00 2001 From: Nishad Musthafa Date: Wed, 7 Jan 2026 12:54:00 -0800 Subject: [PATCH 2/2] Doing away with the now deprecated SipDispatchRuleId field and also directly using SipDispatchRuleIds instead of constructing it locally --- cmd/lk/phone_number.go | 265 ++++------------------------------------- 1 file changed, 20 insertions(+), 245 deletions(-) diff --git a/cmd/lk/phone_number.go b/cmd/lk/phone_number.go index e625235f..124421e5 100644 --- a/cmd/lk/phone_number.go +++ b/cmd/lk/phone_number.go @@ -166,36 +166,6 @@ func createPhoneNumberClient(ctx context.Context, cmd *cli.Command) (*lksdk.Phon return lksdk.NewPhoneNumberClient(project.URL, project.APIKey, project.APISecret, withDefaultClientOpts(project)...), nil } -// getPhoneNumberToDispatchRulesMap fetches all dispatch rules and maps phone number IDs to their associated dispatch rule IDs -// Returns a map where key is phone number ID and value is a slice of dispatch rule IDs -func getPhoneNumberToDispatchRulesMap(ctx context.Context, cmd *cli.Command) (map[string][]string, error) { - _, err := requireProject(ctx, cmd) - if err != nil { - return nil, fmt.Errorf("failed to get project: %w", err) - } - - sipClient := lksdk.NewSIPClient(project.URL, project.APIKey, project.APISecret, withDefaultClientOpts(project)...) - - // List all dispatch rules - resp, err := sipClient.ListSIPDispatchRule(ctx, &livekit.ListSIPDispatchRuleRequest{}) - if err != nil { - return nil, fmt.Errorf("failed to list dispatch rules: %w", err) - } - - // Build map: phone number ID -> []dispatch rule IDs - phoneNumberToRules := make(map[string][]string) - for _, rule := range resp.Items { - for _, trunkID := range rule.TrunkIds { - // Check if trunkID is a phone number ID (starts with PN_PPN_) - if strings.HasPrefix(trunkID, "PN_PPN_") { - phoneNumberToRules[trunkID] = append(phoneNumberToRules[trunkID], rule.SipDispatchRuleId) - } - } - } - - return phoneNumberToRules, nil -} - func searchPhoneNumbers(ctx context.Context, cmd *cli.Command) error { client, err := createPhoneNumberClient(ctx, cmd) if err != nil { @@ -270,65 +240,9 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { req.SipDispatchRuleId = &dispatchRuleID } - // Call purchase and get dispatch rules in parallel - type purchaseResult struct { - resp *livekit.PurchasePhoneNumberResponse - err error - } - type dispatchRulesResult struct { - rules map[string][]string - err error - } - - purchaseChan := make(chan purchaseResult, 1) - dispatchRulesChan := make(chan dispatchRulesResult, 1) - - // Purchase phone numbers - go func() { - resp, err := client.PurchasePhoneNumber(ctx, req) - purchaseChan <- purchaseResult{resp: resp, err: err} - }() - - // Get dispatch rules mapping in parallel - go func() { - rules, err := getPhoneNumberToDispatchRulesMap(ctx, cmd) - dispatchRulesChan <- dispatchRulesResult{rules: rules, err: err} - }() - - // Wait for purchase to complete - purchaseRes := <-purchaseChan - if purchaseRes.err != nil { - return purchaseRes.err - } - resp := purchaseRes.resp - - // Wait for dispatch rules (ignore errors, we'll just not show them) - dispatchRulesRes := <-dispatchRulesChan - phoneNumberToRules := dispatchRulesRes.rules - if dispatchRulesRes.err != nil { - // Log but don't fail - if cmd.Bool("verbose") { - fmt.Fprintf(cmd.ErrWriter, "Warning: failed to get dispatch rules: %v\n", dispatchRulesRes.err) - } - phoneNumberToRules = make(map[string][]string) - } - - // If dispatch rule ID was provided, add it to the mapping for display - // (The actual update is now handled by cloud-io) - if dispatchRuleID != "" { - for _, phoneNumber := range resp.PhoneNumbers { - // Check if dispatchRuleID is already in the list - found := false - for _, ruleID := range phoneNumberToRules[phoneNumber.Id] { - if ruleID == dispatchRuleID { - found = true - break - } - } - if !found { - phoneNumberToRules[phoneNumber.Id] = append(phoneNumberToRules[phoneNumber.Id], dispatchRuleID) - } - } + resp, err := client.PurchasePhoneNumber(ctx, req) + if err != nil { + return err } if cmd.Bool("json") { @@ -339,9 +253,8 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { fmt.Printf("Successfully purchased %d phone numbers:\n", len(resp.PhoneNumbers)) for _, phoneNumber := range resp.PhoneNumbers { ruleInfo := "" - rules := phoneNumberToRules[phoneNumber.Id] - if len(rules) > 0 { - ruleInfo = fmt.Sprintf(" (SIP Dispatch Rules: %s)", strings.Join(rules, ", ")) + if len(phoneNumber.SipDispatchRuleIds) > 0 { + ruleInfo = fmt.Sprintf(" (SIP Dispatch Rules: %s)", strings.Join(phoneNumber.SipDispatchRuleIds, ", ")) } fmt.Printf(" %s (%s) - %s%s\n", phoneNumber.E164Format, phoneNumber.Id, strings.TrimPrefix(phoneNumber.Status.String(), "PHONE_NUMBER_STATUS_"), ruleInfo) } @@ -382,47 +295,9 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error { req.SipDispatchRuleId = &val } - // Call list and get dispatch rules in parallel - type listResult struct { - resp *livekit.ListPhoneNumbersResponse - err error - } - type dispatchRulesResult struct { - rules map[string][]string - err error - } - - listChan := make(chan listResult, 1) - dispatchRulesChan := make(chan dispatchRulesResult, 1) - - // List phone numbers - go func() { - resp, err := client.ListPhoneNumbers(ctx, req) - listChan <- listResult{resp: resp, err: err} - }() - - // Get dispatch rules mapping in parallel - go func() { - rules, err := getPhoneNumberToDispatchRulesMap(ctx, cmd) - dispatchRulesChan <- dispatchRulesResult{rules: rules, err: err} - }() - - // Wait for list to complete - listRes := <-listChan - if listRes.err != nil { - return listRes.err - } - resp := listRes.resp - - // Wait for dispatch rules (ignore errors, we'll just not show them) - dispatchRulesRes := <-dispatchRulesChan - phoneNumberToRules := dispatchRulesRes.rules - if dispatchRulesRes.err != nil { - // Log but don't fail - if cmd.Bool("verbose") { - fmt.Fprintf(cmd.ErrWriter, "Warning: failed to get dispatch rules: %v\n", dispatchRulesRes.err) - } - phoneNumberToRules = make(map[string][]string) + resp, err := client.ListPhoneNumbers(ctx, req) + if err != nil { + return err } if cmd.Bool("json") { @@ -452,12 +327,9 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error { }, req, []string{ "ID", "E164", "Country", "Area Code", "Type", "Locality", "Region", "Capabilities", "Status", "SIP Dispatch Rules", }, func(item *livekit.PhoneNumber) []string { - rules := phoneNumberToRules[item.Id] dispatchRulesStr := "" - if len(rules) > 0 { - dispatchRulesStr = strings.Join(rules, ", ") - } else if item.SipDispatchRuleId != "" { - dispatchRulesStr = item.SipDispatchRuleId + if len(item.SipDispatchRuleIds) > 0 { + dispatchRulesStr = strings.Join(item.SipDispatchRuleIds, ", ") } else { dispatchRulesStr = "-" } @@ -499,47 +371,9 @@ func getPhoneNumber(ctx context.Context, cmd *cli.Command) error { req.PhoneNumber = &phoneNumber } - // Call get and get dispatch rules in parallel - type getResult struct { - resp *livekit.GetPhoneNumberResponse - err error - } - type dispatchRulesResult struct { - rules map[string][]string - err error - } - - getChan := make(chan getResult, 1) - dispatchRulesChan := make(chan dispatchRulesResult, 1) - - // Get phone number - go func() { - resp, err := client.GetPhoneNumber(ctx, req) - getChan <- getResult{resp: resp, err: err} - }() - - // Get dispatch rules mapping in parallel - go func() { - rules, err := getPhoneNumberToDispatchRulesMap(ctx, cmd) - dispatchRulesChan <- dispatchRulesResult{rules: rules, err: err} - }() - - // Wait for get to complete - getRes := <-getChan - if getRes.err != nil { - return getRes.err - } - resp := getRes.resp - - // Wait for dispatch rules (ignore errors, we'll just not show them) - dispatchRulesRes := <-dispatchRulesChan - phoneNumberToRules := dispatchRulesRes.rules - if dispatchRulesRes.err != nil { - // Log but don't fail - if cmd.Bool("verbose") { - fmt.Fprintf(cmd.ErrWriter, "Warning: failed to get dispatch rules: %v\n", dispatchRulesRes.err) - } - phoneNumberToRules = make(map[string][]string) + resp, err := client.GetPhoneNumber(ctx, req) + if err != nil { + return err } if cmd.Bool("json") { @@ -548,12 +382,9 @@ func getPhoneNumber(ctx context.Context, cmd *cli.Command) error { } item := resp.PhoneNumber - rules := phoneNumberToRules[item.Id] dispatchRulesStr := "" - if len(rules) > 0 { - dispatchRulesStr = strings.Join(rules, ", ") - } else if item.SipDispatchRuleId != "" { - dispatchRulesStr = item.SipDispatchRuleId + if len(item.SipDispatchRuleIds) > 0 { + dispatchRulesStr = strings.Join(item.SipDispatchRuleIds, ", ") } else { dispatchRulesStr = "-" } @@ -604,64 +435,9 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { req.SipDispatchRuleId = &dispatchRuleID } - // Call update and get dispatch rules in parallel - type updateResult struct { - resp *livekit.UpdatePhoneNumberResponse - err error - } - type dispatchRulesResult struct { - rules map[string][]string - err error - } - - updateChan := make(chan updateResult, 1) - dispatchRulesChan := make(chan dispatchRulesResult, 1) - - // Update phone number - go func() { - resp, err := client.UpdatePhoneNumber(ctx, req) - updateChan <- updateResult{resp: resp, err: err} - }() - - // Get dispatch rules mapping in parallel - go func() { - rules, err := getPhoneNumberToDispatchRulesMap(ctx, cmd) - dispatchRulesChan <- dispatchRulesResult{rules: rules, err: err} - }() - - // Wait for update to complete - updateRes := <-updateChan - if updateRes.err != nil { - return updateRes.err - } - resp := updateRes.resp - - // Wait for dispatch rules (ignore errors, we'll just not show them) - dispatchRulesRes := <-dispatchRulesChan - phoneNumberToRules := dispatchRulesRes.rules - if dispatchRulesRes.err != nil { - // Log but don't fail - if cmd.Bool("verbose") { - fmt.Fprintf(cmd.ErrWriter, "Warning: failed to get dispatch rules: %v\n", dispatchRulesRes.err) - } - phoneNumberToRules = make(map[string][]string) - } - - // If dispatch rule ID was provided, add it to the mapping for display - // (The actual update is now handled by cloud-io) - if dispatchRuleID != "" { - phoneNumberID := resp.PhoneNumber.Id - // Check if dispatchRuleID is already in the list - found := false - for _, ruleID := range phoneNumberToRules[phoneNumberID] { - if ruleID == dispatchRuleID { - found = true - break - } - } - if !found { - phoneNumberToRules[phoneNumberID] = append(phoneNumberToRules[phoneNumberID], dispatchRuleID) - } + resp, err := client.UpdatePhoneNumber(ctx, req) + if err != nil { + return err } if cmd.Bool("json") { @@ -670,10 +446,9 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { } item := resp.PhoneNumber - rules := phoneNumberToRules[item.Id] dispatchRulesStr := "" - if len(rules) > 0 { - dispatchRulesStr = strings.Join(rules, ", ") + if len(item.SipDispatchRuleIds) > 0 { + dispatchRulesStr = strings.Join(item.SipDispatchRuleIds, ", ") } else { dispatchRulesStr = "-" }