Skip to content

Commit

Permalink
VT-4642 (Audio Streaming) (#151)
Browse files Browse the repository at this point in the history
* Initial commit to save boilerplate code

* Initial commit to save boilerplate code

* Coded everything except extraHeaders logic

* Typo in method name

* Initial commit to save boilerplate code

* Initial commit to save boilerplate code

* Coded everything except extraHeaders logic

* Typo in method name

* SetExtraHeaders logic

* Unit tests for 3 API calls and XML creation

* Fixed logic for extraHeaders processing and added a unit test to confirm its working

* Changed the SDK version and added changelog comments

* Removed logic for appending 'X-PH' to extraHeaders parameter in Stream XML

* Fixed CallStreamParams

* Fixed extraHeaders data type in CallStreamParams

* Changed extraHeaders data type in CallStreamParams from map to string and also fixed its XML implementation

* Added extra parameters to GetAll and GetSpecific stream response

* Fixed data types

* Fixed data types

* Fixed data types

* Added rounded bill duration param
  • Loading branch information
anindya-plivo authored Jun 29, 2023
1 parent 5a78df7 commit 989f83c
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## [7.32.0](https://github.com/plivo/plivo-go/tree/v7.32.0) (2023-06-28)
**Audio Streaming**
- API support for starting, deleting, getting streams on a live call
- XML creation support for stream element

## [7.31.0](https://github.com/plivo/plivo-go/tree/v7.31.0) (2023-06-02)
**Feature - CNAM Lookup**
- Added New Param `cnam_lookup` in to the response of the [list all numbers API], [list single number API]
Expand Down
2 changes: 1 addition & 1 deletion baseclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/google/go-querystring/query"
)

const sdkVersion = "7.31.0"
const sdkVersion = "7.32.0"

const lookupBaseUrl = "lookup.plivo.com"

Expand Down
108 changes: 108 additions & 0 deletions calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ type CallRecordResponse struct {
RecordingID string `json:"recording_id,omitempty" url:"recording_id,omitempty"`
}

type CallStreamResponse struct {
Message string `json:"message,omitempty" url:"message,omitempty"`
APIID string `json:"api_id,omitempty" url:"api_id,omitempty"`
StreamID string `json:"stream_id,omitempty" url:"stream_id,omitempty"`
}

type CallPlayParams struct {
URLs string `json:"urls" url:"urls"`
Length string `json:"length,omitempty" url:"length,omitempty"`
Expand Down Expand Up @@ -218,6 +224,60 @@ type CallDTMFResponseBody struct {
ApiID string `json:"api_id,omitempty" url:"api_id,omitempty"`
}

type CallStreamParams struct {
ServiceUrl string `json:"service_url,omitempty" url:"service_url,omitempty"`
Bidirectional bool `json:"bidirectional,omitempty" url:"bidirectional,omitempty"`
AudioTrack string `json:"audio_track,omitempty" url:"audio_track,omitempty"`
StreamTimeout int64 `json:"stream_timeout,omitempty" url:"stream_timeout,omitempty"`
StatusCallbackUrl string `json:"status_callback_url,omitempty" url:"status_callback_url,omitempty"`
StatusCallbackMethod string `json:"status_callback_method,omitempty" url:"status_callback_method,omitempty"`
ContentType string `json:"content_type,omitempty" url:"content_type,omitempty"`
ExtraHeaders string `json:"extra_headers,omitempty" url:"extra_headers,omitempty"`
}

type CallStreamGetAllObject struct {
AudioTrack string `json:"audio_track" url:"audio_track"`
Bidirectional bool `json:"bidirectional" url:"bidirectional"`
BilledAmount string `json:"billed_amount" url:"billed_amount"`
BillDuration int64 `json:"bill_duration" url:"bill_duration"`
CallUUID string `json:"call_uuid" url:"call_uuid"`
CreatedAt string `json:"created_at" url:"created_at"`
EndTime string `json:"end_time" url:"end_time"`
PlivoAuthId string `json:"plivo_auth_id" url:"plivo_auth_id"`
ResourceURI string `json:"resource_uri" url:"resource_uri"`
RoundedBillDuration string `json:"rounded_bill_duration" url:"rounded_bill_duration"`
ServiceURL string `json:"service_url" url:"service_url"`
StartTime string `json:"start_time" url:"start_time"`
Status string `json:"status" url:"status"`
StatusCallbackURL string `json:"status_callback_url" url:"status_callback_url"`
StreamID string `json:"stream_id" url:"stream_id"`
}

type CallStreamGetAll struct {
ApiID string `json:"api_id,omitempty" url:"api_id,omitempty"`
Meta Meta `json:"meta,omitempty" url:"meta,omitempty"`
Objects []CallStreamGetAllObject `json:"objects" url:"objects"`
}

type CallStreamGetSpecific struct {
ApiID string `json:"api_id,omitempty" url:"api_id,omitempty"`
AudioTrack string `json:"audio_track" url:"audio_track"`
Bidirectional bool `json:"bidirectional" url:"bidirectional"`
BilledAmount string `json:"billed_amount" url:"billed_amount"`
BillDuration int64 `json:"bill_duration" url:"bill_duration"`
CallUUID string `json:"call_uuid" url:"call_uuid"`
CreatedAt string `json:"created_at" url:"created_at"`
EndTime string `json:"end_time" url:"end_time"`
PlivoAuthId string `json:"plivo_auth_id" url:"plivo_auth_id"`
ResourceURI string `json:"resource_uri" url:"resource_uri"`
RoundedBillDuration string `json:"rounded_bill_duration" url:"rounded_bill_duration"`
ServiceURL string `json:"service_url" url:"service_url"`
StartTime string `json:"start_time" url:"start_time"`
Status string `json:"status" url:"status"`
StatusCallbackURL string `json:"status_callback_url" url:"status_callback_url"`
StreamID string `json:"stream_id" url:"stream_id"`
}

func (service *CallService) List(params CallListParams) (response *CallListResponse, err error) {
req, err := service.client.NewRequest("GET", params, "Call")
if err != nil {
Expand Down Expand Up @@ -337,6 +397,54 @@ func (service *CallService) StopRecording(callId string) (err error) {
return
}

func (service *CallService) Stream(CallId string, params CallStreamParams) (response *CallStreamResponse, err error) {
req, err := service.client.NewRequest("POST", params, "Call/%s/Stream", CallId)
if err != nil {
return
}
response = &CallStreamResponse{}
err = service.client.ExecuteRequest(req, response, isVoiceRequest())
return
}

func (service *CallService) StopAllStreams(CallId string) (err error) {
req, err := service.client.NewRequest("DELETE", nil, "Call/%s/Stream", CallId)
if err != nil {
return
}
err = service.client.ExecuteRequest(req, nil, isVoiceRequest())
return
}

func (service *CallService) StopSpecificStream(CallId string, StreamId string) (err error) {
req, err := service.client.NewRequest("DELETE", nil, "Call/%s/Stream/%s", CallId, StreamId)
if err != nil {
return
}
err = service.client.ExecuteRequest(req, nil, isVoiceRequest())
return
}

func (service *CallService) GetAllStreams(CallId string) (response *CallStreamGetAll, err error) {
req, err := service.client.NewRequest("GET", nil, "Call/%s/Stream", CallId)
if err != nil {
return
}
response = &CallStreamGetAll{}
err = service.client.ExecuteRequest(req, response, isVoiceRequest())
return
}

func (service *CallService) GetSpecificStream(CallId string, StreamId string) (response *CallStreamGetSpecific, err error) {
req, err := service.client.NewRequest("GET", nil, "Call/%s/Stream/%s", CallId, StreamId)
if err != nil {
return
}
response = &CallStreamGetSpecific{}
err = service.client.ExecuteRequest(req, response, isVoiceRequest())
return
}

func (service *CallService) Speak(callId string, params CallSpeakParams) (response *CallSpeakResponse, err error) {
req, err := service.client.NewRequest("POST", params, "Call/%s/Speak", callId)
if err != nil {
Expand Down
60 changes: 60 additions & 0 deletions calls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,63 @@ func TestCallService_CancelRequest(t *testing.T) {

assertRequest(t, "DELETE", "Request/%s", RequestID)
}

func TestCallService_Stream(t *testing.T) {
expectResponse("liveCallStreamCreateResponse.json", 202)
CallID := "CallId"

if _, err := client.Calls.Stream(CallID, CallStreamParams{ServiceUrl: "test-url"}); err != nil {
panic(err)
}

cl := client.httpClient
client.httpClient = nil
_, err := client.Calls.Stream(CallID, CallStreamParams{ServiceUrl: "test-url"})
if err == nil {
client.httpClient = cl
panic(errors.New("error expected"))
}
client.httpClient = cl

assertRequest(t, "POST", "Call/%s/Stream", CallID)
}

func TestCallService_StopAllStreams(t *testing.T) {
expectResponse("", 204)
CallID := "CallId"

if err := client.Calls.StopAllStreams(CallID); err != nil {
panic(err)
}

cl := client.httpClient
client.httpClient = nil
err := client.Calls.StopAllStreams(CallID)
if err == nil {
client.httpClient = cl
panic(errors.New("error expected"))
}
client.httpClient = cl

assertRequest(t, "DELETE", "Call/%s/Stream", CallID)
}

func TestCallService_GetAllStreams(t *testing.T) {
expectResponse("liveCallStreamGetAllResponse.json", 200)
CallID := "CallId"

if _, err := client.Calls.GetAllStreams(CallID); err != nil {
panic(err)
}

cl := client.httpClient
client.httpClient = nil
_, err := client.Calls.GetAllStreams(CallID)
if err == nil {
client.httpClient = cl
panic(errors.New("error expected"))
}
client.httpClient = cl

assertRequest(t, "GET", "Call/%s/Stream", CallID)
}
5 changes: 5 additions & 0 deletions fixtures/liveCallStreamCreateResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"api_id": "ff09383e-246f-11ed-a1fe-0242ac110004",
"message": "audio streaming started",
"stream_id": "30852c23-ee79-4eb4-b7b9-cd360545965b"
}
21 changes: 21 additions & 0 deletions fixtures/liveCallStreamGetAllResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"api_id": "8d1d27f0-24f8-11ed-8311-0242ac11000b",
"meta": {
"count": 1,
"limit": 20,
"next": null,
"offset": 0,
"previous": null
},
"objects": [
{
"call_uuid": "4f045f7a-b04a-4364-8523-74e6072f4f72",
"end_time": null,
"service_url": "ws://3ee3-106-51-87-58.ngrok.io",
"start_time": null,
"status": "initiated",
"status_callback_url": "",
"stream_id": "c436abf8-e8a1-4d96-9aa8-b9143f7f3517"
}
]
}
60 changes: 60 additions & 0 deletions xml/plivoxml.go
Original file line number Diff line number Diff line change
Expand Up @@ -1383,3 +1383,63 @@ func wordTitle(str string) string {
lengthOfFinalString := len(finalString)
return finalString[1:lengthOfFinalString]
}

type StreamElement struct {
Contents string `xml:",innerxml"`

Bidirectional *bool `xml:"bidirectional,attr"`

AudioTrack *string `xml:"audioTrack,attr"`

StreamTimeout *int `xml:"streamTimeout,attr"`

StatusCallbackUrl *string `xml:"statusCallbackUrl,attr"`

StatusCallbackMethod *string `xml:"statusCallbackMethod,attr"`

ContentType *string `xml:"contentType,attr"`

ExtraHeaders *string `xml:"extraHeaders,attr"`

XMLName xml.Name `xml:"Stream"`
}

func (e StreamElement) SetBidirectional(value bool) StreamElement {
e.Bidirectional = &value
return e
}

func (e StreamElement) SetAudioTrack(value string) StreamElement {
e.AudioTrack = &value
return e
}

func (e StreamElement) SetStreamTimeout(value int) StreamElement {
e.StreamTimeout = &value
return e
}

func (e StreamElement) SetStatusCallbackUrl(value string) StreamElement {
e.StatusCallbackUrl = &value
return e
}

func (e StreamElement) SetStatusCallbackMethod(value string) StreamElement {
e.StatusCallbackMethod = &value
return e
}

func (e StreamElement) SetContentType(value string) StreamElement {
e.ContentType = &value
return e
}

func (e StreamElement) SetExtraHeaders(value string) StreamElement {
e.ExtraHeaders = &value
return e
}

func (e StreamElement) SetContents(value string) StreamElement {
e.Contents = value
return e
}
8 changes: 8 additions & 0 deletions xml/plivoxml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,11 @@ func TestMPCXMLWithHold(t *testing.T) {
},
}.String())
}

func TestStreamXML(t *testing.T) {
assert.Equal(t, "<Response><Stream bidirectional=\"true\" extraHeaders=\"a=1,b=2\">wss://test.url</Stream></Response>", ResponseElement{
Contents: []interface{}{
new(StreamElement).SetBidirectional(true).SetContents("wss://test.url").SetExtraHeaders("a=1,b=2"),
},
}.String())
}

0 comments on commit 989f83c

Please sign in to comment.