Skip to content

Commit

Permalink
Add filters to SIP list APIs. (#913)
Browse files Browse the repository at this point in the history
  • Loading branch information
dennwc authored Dec 9, 2024
1 parent 47d9881 commit f6b5078
Show file tree
Hide file tree
Showing 6 changed files with 864 additions and 486 deletions.
6 changes: 6 additions & 0 deletions .changeset/lemon-otters-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@livekit/protocol": minor
"github.com/livekit/protocol": minor
---

Add filters to SIP list APIs.
743 changes: 409 additions & 334 deletions livekit/livekit_sip.pb.go

Large diffs are not rendered by default.

305 changes: 153 additions & 152 deletions livekit/livekit_sip.twirp.go

Large diffs are not rendered by default.

114 changes: 114 additions & 0 deletions livekit/sip.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package livekit
import (
"errors"
"fmt"
"slices"
"strings"
)

Expand Down Expand Up @@ -240,3 +241,116 @@ func (p *TransferSIPParticipantRequest) Validate() error {

return nil
}

func filterSlice[T any](arr []T, fnc func(v T) bool) []T {
var out []T
for _, v := range arr {
if fnc(v) {
out = append(out, v)
}
}
return out
}

func filterIDs[T any, ID comparable](arr []T, ids []ID, get func(v T) ID) []T {
if len(ids) == 0 {
return arr
}
out := make([]T, len(ids))
for i, id := range ids {
j := slices.IndexFunc(arr, func(v T) bool {
return get(v) == id
})
if j >= 0 {
out[i] = arr[j]
}
}
return out
}

func (p *ListSIPInboundTrunkRequest) Filter(info *SIPInboundTrunkInfo) bool {
if info == nil {
return true // for FilterSlice to work correctly with missing IDs
}
if len(p.TrunkIds) != 0 && !slices.Contains(p.TrunkIds, info.SipTrunkId) {
return false
}
if len(p.Numbers) != 0 && len(info.Numbers) != 0 {
ok := false
for _, num := range info.Numbers {
if slices.Contains(p.Numbers, num) {
ok = true
break
}
}
if !ok {
return false
}
}
return true
}

func (p *ListSIPInboundTrunkRequest) FilterSlice(arr []*SIPInboundTrunkInfo) []*SIPInboundTrunkInfo {
arr = filterIDs(arr, p.TrunkIds, func(v *SIPInboundTrunkInfo) string {
return v.SipTrunkId
})
return filterSlice(arr, p.Filter)
}

func (p *ListSIPOutboundTrunkRequest) Filter(info *SIPOutboundTrunkInfo) bool {
if info == nil {
return true // for FilterSlice to work correctly with missing IDs
}
if len(p.TrunkIds) != 0 && !slices.Contains(p.TrunkIds, info.SipTrunkId) {
return false
}
if len(p.Numbers) != 0 && len(info.Numbers) != 0 {
ok := false
for _, num := range info.Numbers {
if slices.Contains(p.Numbers, num) {
ok = true
break
}
}
if !ok {
return false
}
}
return true
}

func (p *ListSIPOutboundTrunkRequest) FilterSlice(arr []*SIPOutboundTrunkInfo) []*SIPOutboundTrunkInfo {
arr = filterIDs(arr, p.TrunkIds, func(v *SIPOutboundTrunkInfo) string {
return v.SipTrunkId
})
return filterSlice(arr, p.Filter)
}

func (p *ListSIPDispatchRuleRequest) Filter(info *SIPDispatchRuleInfo) bool {
if info == nil {
return true // for FilterSlice to work correctly with missing IDs
}
if len(p.DispatchRuleIds) != 0 && !slices.Contains(p.DispatchRuleIds, info.SipDispatchRuleId) {
return false
}
if len(p.TrunkIds) != 0 && len(info.TrunkIds) != 0 {
ok := false
for _, id := range info.TrunkIds {
if slices.Contains(p.TrunkIds, id) {
ok = true
break
}
}
if !ok {
return false
}
}
return true
}

func (p *ListSIPDispatchRuleRequest) FilterSlice(arr []*SIPDispatchRuleInfo) []*SIPDispatchRuleInfo {
arr = filterIDs(arr, p.DispatchRuleIds, func(v *SIPDispatchRuleInfo) string {
return v.SipDispatchRuleId
})
return filterSlice(arr, p.Filter)
}
163 changes: 163 additions & 0 deletions livekit/sip_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package livekit

import (
"slices"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -174,3 +175,165 @@ func TestSIPValidate(t *testing.T) {
})
}
}

func TestSIPInboundTrunkFilter(t *testing.T) {
list := []*SIPInboundTrunkInfo{
0: {SipTrunkId: "A"},
1: {SipTrunkId: "B", Numbers: []string{"+111", "+222"}},
2: {SipTrunkId: "C", Numbers: []string{"+333"}},
}
cases := []struct {
name string
req *ListSIPInboundTrunkRequest
exp []*SIPInboundTrunkInfo
}{
{
name: "all",
req: &ListSIPInboundTrunkRequest{},
exp: list,
},
{
name: "id",
req: &ListSIPInboundTrunkRequest{
TrunkIds: []string{"B"},
},
exp: []*SIPInboundTrunkInfo{list[1]},
},
{
name: "ids",
req: &ListSIPInboundTrunkRequest{
TrunkIds: []string{"B", "C"},
},
exp: []*SIPInboundTrunkInfo{list[1], list[2]},
},
{
name: "ids order",
req: &ListSIPInboundTrunkRequest{
TrunkIds: []string{"C", "missing", "B"},
},
exp: []*SIPInboundTrunkInfo{list[2], nil, list[1]},
},
{
name: "numbers",
req: &ListSIPInboundTrunkRequest{
Numbers: []string{"+222"},
},
exp: []*SIPInboundTrunkInfo{list[0], list[1]},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
list := slices.Clone(list)
got := c.req.FilterSlice(list)
require.Equal(t, c.exp, got)
})
}
}

func TestSIPOutboundTrunkFilter(t *testing.T) {
list := []*SIPOutboundTrunkInfo{
0: {SipTrunkId: "A"},
1: {SipTrunkId: "B", Numbers: []string{"+111", "+222"}},
2: {SipTrunkId: "C", Numbers: []string{"+333"}},
}
cases := []struct {
name string
req *ListSIPOutboundTrunkRequest
exp []*SIPOutboundTrunkInfo
}{
{
name: "all",
req: &ListSIPOutboundTrunkRequest{},
exp: list,
},
{
name: "id",
req: &ListSIPOutboundTrunkRequest{
TrunkIds: []string{"B"},
},
exp: []*SIPOutboundTrunkInfo{list[1]},
},
{
name: "ids",
req: &ListSIPOutboundTrunkRequest{
TrunkIds: []string{"B", "C"},
},
exp: []*SIPOutboundTrunkInfo{list[1], list[2]},
},
{
name: "ids order",
req: &ListSIPOutboundTrunkRequest{
TrunkIds: []string{"C", "missing", "B"},
},
exp: []*SIPOutboundTrunkInfo{list[2], nil, list[1]},
},
{
name: "numbers",
req: &ListSIPOutboundTrunkRequest{
Numbers: []string{"+222"},
},
exp: []*SIPOutboundTrunkInfo{list[0], list[1]},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
list := slices.Clone(list)
got := c.req.FilterSlice(list)
require.Equal(t, c.exp, got)
})
}
}

func TestSIPDispatchRuleFilter(t *testing.T) {
list := []*SIPDispatchRuleInfo{
0: {SipDispatchRuleId: "A"},
1: {SipDispatchRuleId: "B", TrunkIds: []string{"T1", "T2"}},
2: {SipDispatchRuleId: "C", TrunkIds: []string{"T3"}},
}
cases := []struct {
name string
req *ListSIPDispatchRuleRequest
exp []*SIPDispatchRuleInfo
}{
{
name: "all",
req: &ListSIPDispatchRuleRequest{},
exp: list,
},
{
name: "id",
req: &ListSIPDispatchRuleRequest{
DispatchRuleIds: []string{"B"},
},
exp: []*SIPDispatchRuleInfo{list[1]},
},
{
name: "ids",
req: &ListSIPDispatchRuleRequest{
DispatchRuleIds: []string{"B", "C"},
},
exp: []*SIPDispatchRuleInfo{list[1], list[2]},
},
{
name: "ids order",
req: &ListSIPDispatchRuleRequest{
DispatchRuleIds: []string{"C", "missing", "B"},
},
exp: []*SIPDispatchRuleInfo{list[2], nil, list[1]},
},
{
name: "numbers",
req: &ListSIPDispatchRuleRequest{
TrunkIds: []string{"T2"},
},
exp: []*SIPDispatchRuleInfo{list[0], list[1]},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
list := slices.Clone(list)
got := c.req.FilterSlice(list)
require.Equal(t, c.exp, got)
})
}
}
19 changes: 19 additions & 0 deletions protobufs/livekit_sip.proto
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import "livekit_models.proto";

service SIP {
// rpc CreateSIPTrunk(CreateSIPTrunkRequest) returns (SIPTrunkInfo) { option deprecated = true; }; DELETED

rpc ListSIPTrunk(ListSIPTrunkRequest) returns (ListSIPTrunkResponse) { option deprecated = true; };

rpc CreateSIPInboundTrunk(CreateSIPInboundTrunkRequest) returns (SIPInboundTrunkInfo);
Expand Down Expand Up @@ -269,14 +270,26 @@ message ListSIPTrunkResponse {
repeated SIPTrunkInfo items = 1;
}

// ListSIPInboundTrunkRequest lists inbound trunks for given filters. If no filters are set, all trunks are listed.
message ListSIPInboundTrunkRequest {
// Trunk IDs to list. If this option is set, the response will contains trunks in the same order.
// If any of the trunks is missing, a nil item in that position will be sent in the response.
repeated string trunk_ids = 1;
// Only list trunks that contain one of the numbers, including wildcard trunks.
repeated string numbers = 2;
}

message ListSIPInboundTrunkResponse {
repeated SIPInboundTrunkInfo items = 1;
}

// ListSIPOutboundTrunkRequest lists outbound trunks for given filters. If no filters are set, all trunks are listed.
message ListSIPOutboundTrunkRequest {
// Trunk IDs to list. If this option is set, the response will contains trunks in the same order.
// If any of the trunks is missing, a nil item in that position will be sent in the response.
repeated string trunk_ids = 1;
// Only list trunks that contain one of the numbers, including wildcard trunks.
repeated string numbers = 2;
}

message ListSIPOutboundTrunkResponse {
Expand Down Expand Up @@ -375,7 +388,13 @@ message SIPDispatchRuleInfo {
// NEXT ID: 9
}

// ListSIPDispatchRuleRequest lists dispatch rules for given filters. If no filters are set, all rules are listed.
message ListSIPDispatchRuleRequest {
// Rule IDs to list. If this option is set, the response will contains rules in the same order.
// If any of the rules is missing, a nil item in that position will be sent in the response.
repeated string dispatch_rule_ids = 1;
// Only list rules that contain one of the Trunk IDs, including wildcard rules.
repeated string trunk_ids = 2;
}

message ListSIPDispatchRuleResponse {
Expand Down

0 comments on commit f6b5078

Please sign in to comment.