From 8f4ce6521691fb90f92f7e4f56d85f20a3eea330 Mon Sep 17 00:00:00 2001 From: Nishad Musthafa Date: Fri, 7 Nov 2025 15:39:00 +0530 Subject: [PATCH 1/4] Changing up how dispatch rules are assigned to numbers --- cmd/lk/phone_number.go | 98 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/cmd/lk/phone_number.go b/cmd/lk/phone_number.go index 51083214..66f6e5be 100644 --- a/cmd/lk/phone_number.go +++ b/cmd/lk/phone_number.go @@ -161,6 +161,53 @@ func createPhoneNumberClient(ctx context.Context, cmd *cli.Command) (*lksdk.Phon return lksdk.NewPhoneNumberClient(project.URL, project.APIKey, project.APISecret, withDefaultClientOpts(project)...), nil } +// appendPhoneNumberToDispatchRule appends a phone number ID to the trunk_ids of a dispatch rule +func appendPhoneNumberToDispatchRule(ctx context.Context, cmd *cli.Command, dispatchRuleID, phoneNumberID string) error { + _, err := requireProject(ctx, cmd) + if err != nil { + return fmt.Errorf("failed to get project: %w", err) + } + + sipClient := lksdk.NewSIPClient(project.URL, project.APIKey, project.APISecret, withDefaultClientOpts(project)...) + + // Get the current dispatch rule to check if phone number ID is already in trunk_ids + rules, err := sipClient.GetSIPDispatchRulesByIDs(ctx, []string{dispatchRuleID}) + if err != nil { + return fmt.Errorf("failed to get dispatch rule: %w", err) + } + if len(rules) == 0 { + return fmt.Errorf("dispatch rule %s not found", dispatchRuleID) + } + currentRule := rules[0] + + // Check if phone number ID is already in trunk_ids + for _, trunkID := range currentRule.TrunkIds { + if trunkID == phoneNumberID { + // Already in the list, no need to update + return nil + } + } + + // Append phone number ID to trunk_ids using Update action + updateReq := &livekit.UpdateSIPDispatchRuleRequest{ + SipDispatchRuleId: dispatchRuleID, + Action: &livekit.UpdateSIPDispatchRuleRequest_Update{ + Update: &livekit.SIPDispatchRuleUpdate{ + TrunkIds: &livekit.ListUpdate{ + Add: []string{phoneNumberID}, + }, + }, + }, + } + + _, err = sipClient.UpdateSIPDispatchRule(ctx, updateReq) + if err != nil { + return fmt.Errorf("failed to update dispatch rule: %w", err) + } + + return nil +} + func searchPhoneNumbers(ctx context.Context, cmd *cli.Command) error { client, err := createPhoneNumberClient(ctx, cmd) if err != nil { @@ -226,18 +273,30 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("at least one phone number must be provided") } + dispatchRuleID := cmd.String("sip-dispatch-rule-id") + req := &livekit.PurchasePhoneNumberRequest{ PhoneNumbers: phoneNumbers, } - if val := cmd.String("sip-dispatch-rule-id"); val != "" { - req.SipDispatchRuleId = &val - } resp, err := client.PurchasePhoneNumber(ctx, req) if err != nil { return err } + // If dispatch rule ID was provided, append each purchased phone number ID to the dispatch rule's trunk_ids + dispatchRuleAdded := make(map[string]bool) + if dispatchRuleID != "" { + for _, phoneNumber := range resp.PhoneNumbers { + if err := appendPhoneNumberToDispatchRule(ctx, cmd, dispatchRuleID, phoneNumber.Id); err != nil { + // Log error but don't fail the purchase operation + fmt.Fprintf(cmd.ErrWriter, "Warning: failed to add phone number %s to dispatch rule %s: %v\n", phoneNumber.Id, dispatchRuleID, err) + } else { + dispatchRuleAdded[phoneNumber.Id] = true + } + } + } + if cmd.Bool("json") { util.PrintJSON(resp) return nil @@ -245,7 +304,13 @@ 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 { - fmt.Printf(" %s (%s) - %s\n", phoneNumber.E164Format, phoneNumber.Id, strings.TrimPrefix(phoneNumber.Status.String(), "PHONE_NUMBER_STATUS_")) + ruleInfo := "" + if dispatchRuleAdded[phoneNumber.Id] && dispatchRuleID != "" { + ruleInfo = fmt.Sprintf(" (SIP Dispatch Rule: %s)", dispatchRuleID) + } else if phoneNumber.SipDispatchRuleId != "" { + ruleInfo = fmt.Sprintf(" (SIP Dispatch Rule: %s)", phoneNumber.SipDispatchRuleId) + } + fmt.Printf(" %s (%s) - %s%s\n", phoneNumber.E164Format, phoneNumber.Id, strings.TrimPrefix(phoneNumber.Status.String(), "PHONE_NUMBER_STATUS_"), ruleInfo) } return nil @@ -376,21 +441,32 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("only one of --id or --number can be provided") } + dispatchRuleID := cmd.String("sip-dispatch-rule-id") + req := &livekit.UpdatePhoneNumberRequest{} if id != "" { req.Id = &id } else { req.PhoneNumber = &phoneNumber } - if val := cmd.String("sip-dispatch-rule-id"); val != "" { - req.SipDispatchRuleId = &val - } resp, err := client.UpdatePhoneNumber(ctx, req) if err != nil { return err } + // If dispatch rule ID was provided, append the phone number ID to the dispatch rule's trunk_ids + dispatchRuleAdded := false + if dispatchRuleID != "" { + phoneNumberID := resp.PhoneNumber.Id + if err := appendPhoneNumberToDispatchRule(ctx, cmd, dispatchRuleID, phoneNumberID); err != nil { + // Log error but don't fail the update operation + fmt.Fprintf(cmd.ErrWriter, "Warning: failed to add phone number %s to dispatch rule %s: %v\n", phoneNumberID, dispatchRuleID, err) + } else { + dispatchRuleAdded = true + } + } + if cmd.Bool("json") { util.PrintJSON(resp) return nil @@ -401,7 +477,13 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { fmt.Printf(" ID: %s\n", item.Id) fmt.Printf(" E164 Format: %s\n", item.E164Format) fmt.Printf(" Status: %s\n", strings.TrimPrefix(item.Status.String(), "PHONE_NUMBER_STATUS_")) - fmt.Printf(" SIP Dispatch Rule: %s\n", item.SipDispatchRuleId) + + // Show dispatch rule ID if it was provided and successfully added, or if it's in the response + displayRuleID := item.SipDispatchRuleId + if dispatchRuleAdded && dispatchRuleID != "" { + displayRuleID = dispatchRuleID + } + fmt.Printf(" SIP Dispatch Rule: %s\n", displayRuleID) return nil } From b047b9545b092f9984edd85556381facd181ec41 Mon Sep 17 00:00:00 2001 From: Nishad Musthafa Date: Sun, 9 Nov 2025 00:15:01 +0530 Subject: [PATCH 2/4] handling the case of multiple dispatch rules correctly --- cmd/lk/phone_number.go | 294 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 266 insertions(+), 28 deletions(-) diff --git a/cmd/lk/phone_number.go b/cmd/lk/phone_number.go index 66f6e5be..218bc083 100644 --- a/cmd/lk/phone_number.go +++ b/cmd/lk/phone_number.go @@ -161,6 +161,36 @@ 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 +} + // appendPhoneNumberToDispatchRule appends a phone number ID to the trunk_ids of a dispatch rule func appendPhoneNumberToDispatchRule(ctx context.Context, cmd *cli.Command, dispatchRuleID, phoneNumberID string) error { _, err := requireProject(ctx, cmd) @@ -181,10 +211,13 @@ func appendPhoneNumberToDispatchRule(ctx context.Context, cmd *cli.Command, disp currentRule := rules[0] // Check if phone number ID is already in trunk_ids - for _, trunkID := range currentRule.TrunkIds { - if trunkID == phoneNumberID { - // Already in the list, no need to update - return nil + // Handle nil or empty trunk_ids explicitly + if currentRule.TrunkIds != nil && len(currentRule.TrunkIds) > 0 { + for _, trunkID := range currentRule.TrunkIds { + if trunkID == phoneNumberID { + // Already in the list, no need to update + return nil + } } } @@ -279,11 +312,38 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { PhoneNumbers: phoneNumbers, } - resp, err := client.PurchasePhoneNumber(ctx, req) - if err != nil { - return err + // 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 + // If dispatch rule ID was provided, append each purchased phone number ID to the dispatch rule's trunk_ids dispatchRuleAdded := make(map[string]bool) if dispatchRuleID != "" { @@ -297,6 +357,26 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { } } + // 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) + } + + // Update the mapping with newly added dispatch rules + if dispatchRuleID != "" { + for _, phoneNumber := range resp.PhoneNumbers { + if dispatchRuleAdded[phoneNumber.Id] { + phoneNumberToRules[phoneNumber.Id] = append(phoneNumberToRules[phoneNumber.Id], dispatchRuleID) + } + } + } + if cmd.Bool("json") { util.PrintJSON(resp) return nil @@ -305,8 +385,9 @@ 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 := "" - if dispatchRuleAdded[phoneNumber.Id] && dispatchRuleID != "" { - ruleInfo = fmt.Sprintf(" (SIP Dispatch Rule: %s)", dispatchRuleID) + rules := phoneNumberToRules[phoneNumber.Id] + if len(rules) > 0 { + ruleInfo = fmt.Sprintf(" (SIP Dispatch Rules: %s)", strings.Join(rules, ", ")) } else if phoneNumber.SipDispatchRuleId != "" { ruleInfo = fmt.Sprintf(" (SIP Dispatch Rule: %s)", phoneNumber.SipDispatchRuleId) } @@ -342,9 +423,47 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error { req.SipDispatchRuleId = &val } - resp, err := client.ListPhoneNumbers(ctx, req) - if err != nil { - return err + // 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) } if cmd.Bool("json") { @@ -356,8 +475,17 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error { return listAndPrint(ctx, cmd, func(ctx context.Context, req *livekit.ListPhoneNumbersRequest) (*livekit.ListPhoneNumbersResponse, error) { return client.ListPhoneNumbers(ctx, req) }, req, []string{ - "ID", "E164", "Country", "Area Code", "Type", "Locality", "Region", "Capabilities", "Status", "SIP Dispatch Rule", + "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 + } else { + dispatchRulesStr = "-" + } return []string{ item.Id, item.E164Format, @@ -368,7 +496,7 @@ func listPhoneNumbers(ctx context.Context, cmd *cli.Command) error { item.Region, strings.Join(item.Capabilities, ","), strings.TrimPrefix(item.Status.String(), "PHONE_NUMBER_STATUS_"), - item.SipDispatchRuleId, + dispatchRulesStr, } }) } @@ -396,9 +524,47 @@ func getPhoneNumber(ctx context.Context, cmd *cli.Command) error { req.PhoneNumber = &phoneNumber } - resp, err := client.GetPhoneNumber(ctx, req) - if err != nil { - return err + // 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) } if cmd.Bool("json") { @@ -407,6 +573,16 @@ 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 + } else { + dispatchRulesStr = "-" + } + fmt.Printf("Phone Number Details:\n") fmt.Printf(" ID: %s\n", item.Id) fmt.Printf(" E164 Format: %s\n", item.E164Format) @@ -417,7 +593,7 @@ func getPhoneNumber(ctx context.Context, cmd *cli.Command) error { fmt.Printf(" Region: %s\n", item.Region) fmt.Printf(" Capabilities: %s\n", strings.Join(item.Capabilities, ",")) fmt.Printf(" Status: %s\n", strings.TrimPrefix(item.Status.String(), "PHONE_NUMBER_STATUS_")) - fmt.Printf(" SIP Dispatch Rule: %s\n", item.SipDispatchRuleId) + fmt.Printf(" SIP Dispatch Rules: %s\n", dispatchRulesStr) if item.ReleasedAt != nil { fmt.Printf(" Released At: %s\n", item.ReleasedAt.AsTime().Format("2006-01-02 15:04:05")) } @@ -450,10 +626,37 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { req.PhoneNumber = &phoneNumber } - resp, err := client.UpdatePhoneNumber(ctx, req) - if err != nil { - return err + // 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 // If dispatch rule ID was provided, append the phone number ID to the dispatch rule's trunk_ids dispatchRuleAdded := false @@ -467,23 +670,58 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { } } + // 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) + } + + // Update the mapping with newly added dispatch rule if it was successfully added + if dispatchRuleAdded && dispatchRuleID != "" { + phoneNumberID := resp.PhoneNumber.Id + // Check if it's already in the map (from the parallel fetch) + if _, exists := phoneNumberToRules[phoneNumberID]; !exists { + phoneNumberToRules[phoneNumberID] = []string{} + } + // 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) + } + } + if cmd.Bool("json") { util.PrintJSON(resp) return nil } item := resp.PhoneNumber + rules := phoneNumberToRules[item.Id] + dispatchRulesStr := "" + if len(rules) > 0 { + dispatchRulesStr = strings.Join(rules, ", ") + } else if item.SipDispatchRuleId != "" { + dispatchRulesStr = item.SipDispatchRuleId + } else { + dispatchRulesStr = "-" + } + fmt.Printf("Successfully updated phone number:\n") fmt.Printf(" ID: %s\n", item.Id) fmt.Printf(" E164 Format: %s\n", item.E164Format) fmt.Printf(" Status: %s\n", strings.TrimPrefix(item.Status.String(), "PHONE_NUMBER_STATUS_")) - - // Show dispatch rule ID if it was provided and successfully added, or if it's in the response - displayRuleID := item.SipDispatchRuleId - if dispatchRuleAdded && dispatchRuleID != "" { - displayRuleID = dispatchRuleID - } - fmt.Printf(" SIP Dispatch Rule: %s\n", displayRuleID) + fmt.Printf(" SIP Dispatch Rules: %s\n", dispatchRulesStr) return nil } From ed5da928f39930efa70c37fcc06a6ca8e78fa263 Mon Sep 17 00:00:00 2001 From: Nishad Musthafa Date: Sun, 9 Nov 2025 00:36:31 +0530 Subject: [PATCH 3/4] Fix static check issue --- cmd/lk/phone_number.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cmd/lk/phone_number.go b/cmd/lk/phone_number.go index 218bc083..e771e941 100644 --- a/cmd/lk/phone_number.go +++ b/cmd/lk/phone_number.go @@ -211,13 +211,10 @@ func appendPhoneNumberToDispatchRule(ctx context.Context, cmd *cli.Command, disp currentRule := rules[0] // Check if phone number ID is already in trunk_ids - // Handle nil or empty trunk_ids explicitly - if currentRule.TrunkIds != nil && len(currentRule.TrunkIds) > 0 { - for _, trunkID := range currentRule.TrunkIds { - if trunkID == phoneNumberID { - // Already in the list, no need to update - return nil - } + for _, trunkID := range currentRule.TrunkIds { + if trunkID == phoneNumberID { + // Already in the list, no need to update + return nil } } From 406f61ac63508e7dfcb5b450034423fa397a47d9 Mon Sep 17 00:00:00 2001 From: Nishad Musthafa Date: Mon, 10 Nov 2025 14:33:59 +0530 Subject: [PATCH 4/4] Moving the editing for trunk_ids to server --- cmd/lk/phone_number.go | 104 ++++++++--------------------------------- 1 file changed, 20 insertions(+), 84 deletions(-) diff --git a/cmd/lk/phone_number.go b/cmd/lk/phone_number.go index e771e941..18a35be6 100644 --- a/cmd/lk/phone_number.go +++ b/cmd/lk/phone_number.go @@ -191,53 +191,6 @@ func getPhoneNumberToDispatchRulesMap(ctx context.Context, cmd *cli.Command) (ma return phoneNumberToRules, nil } -// appendPhoneNumberToDispatchRule appends a phone number ID to the trunk_ids of a dispatch rule -func appendPhoneNumberToDispatchRule(ctx context.Context, cmd *cli.Command, dispatchRuleID, phoneNumberID string) error { - _, err := requireProject(ctx, cmd) - if err != nil { - return fmt.Errorf("failed to get project: %w", err) - } - - sipClient := lksdk.NewSIPClient(project.URL, project.APIKey, project.APISecret, withDefaultClientOpts(project)...) - - // Get the current dispatch rule to check if phone number ID is already in trunk_ids - rules, err := sipClient.GetSIPDispatchRulesByIDs(ctx, []string{dispatchRuleID}) - if err != nil { - return fmt.Errorf("failed to get dispatch rule: %w", err) - } - if len(rules) == 0 { - return fmt.Errorf("dispatch rule %s not found", dispatchRuleID) - } - currentRule := rules[0] - - // Check if phone number ID is already in trunk_ids - for _, trunkID := range currentRule.TrunkIds { - if trunkID == phoneNumberID { - // Already in the list, no need to update - return nil - } - } - - // Append phone number ID to trunk_ids using Update action - updateReq := &livekit.UpdateSIPDispatchRuleRequest{ - SipDispatchRuleId: dispatchRuleID, - Action: &livekit.UpdateSIPDispatchRuleRequest_Update{ - Update: &livekit.SIPDispatchRuleUpdate{ - TrunkIds: &livekit.ListUpdate{ - Add: []string{phoneNumberID}, - }, - }, - }, - } - - _, err = sipClient.UpdateSIPDispatchRule(ctx, updateReq) - if err != nil { - return fmt.Errorf("failed to update dispatch rule: %w", err) - } - - return nil -} - func searchPhoneNumbers(ctx context.Context, cmd *cli.Command) error { client, err := createPhoneNumberClient(ctx, cmd) if err != nil { @@ -308,6 +261,9 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { req := &livekit.PurchasePhoneNumberRequest{ PhoneNumbers: phoneNumbers, } + if dispatchRuleID != "" { + req.SipDispatchRuleId = &dispatchRuleID + } // Call purchase and get dispatch rules in parallel type purchaseResult struct { @@ -341,19 +297,6 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { } resp := purchaseRes.resp - // If dispatch rule ID was provided, append each purchased phone number ID to the dispatch rule's trunk_ids - dispatchRuleAdded := make(map[string]bool) - if dispatchRuleID != "" { - for _, phoneNumber := range resp.PhoneNumbers { - if err := appendPhoneNumberToDispatchRule(ctx, cmd, dispatchRuleID, phoneNumber.Id); err != nil { - // Log error but don't fail the purchase operation - fmt.Fprintf(cmd.ErrWriter, "Warning: failed to add phone number %s to dispatch rule %s: %v\n", phoneNumber.Id, dispatchRuleID, err) - } else { - dispatchRuleAdded[phoneNumber.Id] = true - } - } - } - // Wait for dispatch rules (ignore errors, we'll just not show them) dispatchRulesRes := <-dispatchRulesChan phoneNumberToRules := dispatchRulesRes.rules @@ -365,10 +308,19 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { phoneNumberToRules = make(map[string][]string) } - // Update the mapping with newly added dispatch rules + // 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 { - if dispatchRuleAdded[phoneNumber.Id] { + // 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) } } @@ -385,8 +337,6 @@ func purchasePhoneNumbers(ctx context.Context, cmd *cli.Command) error { rules := phoneNumberToRules[phoneNumber.Id] if len(rules) > 0 { ruleInfo = fmt.Sprintf(" (SIP Dispatch Rules: %s)", strings.Join(rules, ", ")) - } else if phoneNumber.SipDispatchRuleId != "" { - ruleInfo = fmt.Sprintf(" (SIP Dispatch Rule: %s)", phoneNumber.SipDispatchRuleId) } fmt.Printf(" %s (%s) - %s%s\n", phoneNumber.E164Format, phoneNumber.Id, strings.TrimPrefix(phoneNumber.Status.String(), "PHONE_NUMBER_STATUS_"), ruleInfo) } @@ -622,6 +572,9 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { } else { req.PhoneNumber = &phoneNumber } + if dispatchRuleID != "" { + req.SipDispatchRuleId = &dispatchRuleID + } // Call update and get dispatch rules in parallel type updateResult struct { @@ -655,18 +608,6 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { } resp := updateRes.resp - // If dispatch rule ID was provided, append the phone number ID to the dispatch rule's trunk_ids - dispatchRuleAdded := false - if dispatchRuleID != "" { - phoneNumberID := resp.PhoneNumber.Id - if err := appendPhoneNumberToDispatchRule(ctx, cmd, dispatchRuleID, phoneNumberID); err != nil { - // Log error but don't fail the update operation - fmt.Fprintf(cmd.ErrWriter, "Warning: failed to add phone number %s to dispatch rule %s: %v\n", phoneNumberID, dispatchRuleID, err) - } else { - dispatchRuleAdded = true - } - } - // Wait for dispatch rules (ignore errors, we'll just not show them) dispatchRulesRes := <-dispatchRulesChan phoneNumberToRules := dispatchRulesRes.rules @@ -678,13 +619,10 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { phoneNumberToRules = make(map[string][]string) } - // Update the mapping with newly added dispatch rule if it was successfully added - if dispatchRuleAdded && dispatchRuleID != "" { + // 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 it's already in the map (from the parallel fetch) - if _, exists := phoneNumberToRules[phoneNumberID]; !exists { - phoneNumberToRules[phoneNumberID] = []string{} - } // Check if dispatchRuleID is already in the list found := false for _, ruleID := range phoneNumberToRules[phoneNumberID] { @@ -708,8 +646,6 @@ func updatePhoneNumber(ctx context.Context, cmd *cli.Command) error { dispatchRulesStr := "" if len(rules) > 0 { dispatchRulesStr = strings.Join(rules, ", ") - } else if item.SipDispatchRuleId != "" { - dispatchRulesStr = item.SipDispatchRuleId } else { dispatchRulesStr = "-" }