Skip to content

Commit

Permalink
Feature/add call support (#1)
Browse files Browse the repository at this point in the history
* Add Call and CallResource
* Add proper api error parsing
  • Loading branch information
jtwatson authored May 25, 2020
1 parent d56d49d commit c14c123
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 31 deletions.
95 changes: 64 additions & 31 deletions twilio.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package twilio

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"

"github.com/google/go-querystring/query"
"github.com/pkg/errors"
"go.opencensus.io/trace"
)
Expand Down Expand Up @@ -62,7 +64,7 @@ func (c *Client) DisconnectCall(ctx context.Context, callSid string) error {
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return fmt.Errorf("twilio.Client.DisconnectCall(): expected status code 200, got %d", res.StatusCode)
return errors.WithMessage(decodeError(res.Body), "twilio.Client.DisconnectCall()")
}

return nil
Expand Down Expand Up @@ -91,45 +93,76 @@ func (c *Client) SetMute(ctx context.Context, conferenceSid, callSid string, mut
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return fmt.Errorf("twilio.Client.SetMute(): expected status code 200, got %d", res.StatusCode)
return errors.WithMessage(decodeError(res.Body), "twilio.Client.SetMute()")
}

return nil
}

// CallResource recieves call resource details
func (c *Client) CallResource(ctx context.Context, callSid string) {
// CallResource receives call resource details
func (c *Client) CallResource(ctx context.Context, callSid string) (*CallResource, error) {
ctx, span := trace.StartSpan(ctx, "twilio.Client.CallResource()")
defer span.End()

}
url := fmt.Sprintf("%s/Accounts/%s/Calls/%s.json", baseURL, c.accountSid, callSid)

req, err := c.newRequest(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, errors.WithMessage(err, "twilio.Client.CallResource()")
}

res, err := c.httpClient.Do(req)
if err != nil {
return nil, errors.WithMessage(err, "twilio.Client.CallResource(): http.Do(")
}
defer res.Body.Close()

// CallResource holds the details of a call resouce
type CallResource struct {
Sid string `json:"sid,omitempty"`
DateCreated string `json:"date_created,omitempty"`
DateUpdated string `json:"date_updated,omitempty"`
ParentCallSid string `json:"parent_call_sid,omitempty"`
AccountSid string `json:"account_sid,omitempty"`
To string `json:"to,omitempty"`
From string `json:"from,omitempty"`
PhoneNumberSid string `json:"phone_number_sid,omitempty"`
Status string `json:"status,omitempty"`
StartTime string `json:"start_time,omitempty"`
EndTime string `json:"end_time,omitempty"`
Duration string `json:"duration,omitempty"`
Price string `json:"price,omitempty"`
Direction string `json:"direction,omitempty"`
AnsweredBy string `json:"answered_by,omitempty"`
APIVersion string `json:"api_version,omitempty"`
ForwardedFrom string `json:"forwarded_from,omitempty"`
CallerName string `json:"caller_name,omitempty"`
URI string `json:"uri,omitempty"`
SubresourceUris SubresourceUris `json:"subresource_uris,omitempty"`
if res.StatusCode != http.StatusOK {
return nil, errors.WithMessage(decodeError(res.Body), "twilio.Client.CallResource()")
}

callResource := &CallResource{}

if err := json.NewDecoder(res.Body).Decode(callResource); err != nil {
return nil, errors.WithMessage(err, "twilio.Client.CallResource(): json.Decoder.Decode()")
}

return callResource, nil
}

// SubresourceUris holds details for subresource uri's
type SubresourceUris struct {
Notifications string `json:"notifications,omitempty"`
Recordings string `json:"recordings,omitempty"`
// Call creates an outbound call returning the resulting CallResource
func (c *Client) Call(ctx context.Context, call *Call) (*CallResource, error) {
ctx, span := trace.StartSpan(ctx, "twilio.Client.Call()")
defer span.End()

params, err := query.Values(call)
if err != nil {
return nil, errors.WithMessage(err, "twilio.Client.Call(): query.Values()")
}

url := fmt.Sprintf("%s/Accounts/%s/Calls.json", baseURL, c.accountSid)
body := strings.NewReader(params.Encode())

req, err := c.newRequest(ctx, http.MethodPost, url, body)
if err != nil {
return nil, errors.WithMessage(err, "twilio.Client.Call()")
}

res, err := c.httpClient.Do(req)
if err != nil {
return nil, errors.WithMessage(err, "twilio.Client.Call(): http.Do(")
}
defer res.Body.Close()

if res.StatusCode != http.StatusCreated {
return nil, errors.WithMessage(decodeError(res.Body), "twilio.Client.Call()")
}

callResource := &CallResource{}

if err := json.NewDecoder(res.Body).Decode(callResource); err != nil {
return nil, errors.WithMessage(err, "twilio.Client.Call(): json.Decoder.Decode()")
}

return callResource, nil
}
93 changes: 93 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package twilio

import (
"fmt"
)

// CallResource holds the details of a call resouce
type CallResource struct {
Sid string `json:"sid,omitempty"`
AccountSid string `json:"account_sid,omitempty"`
Annotation string `json:"annotation,omitempty"`
AnsweredBy string `json:"answered_by,omitempty"`
APIVersion string `json:"api_version,omitempty"`
CallerName string `json:"caller_name,omitempty"`
DateCreated string `json:"date_created,omitempty"`
DateUpdated string `json:"date_updated,omitempty"`
Direction string `json:"direction,omitempty"`
Duration string `json:"duration,omitempty"`
EndTime string `json:"end_time,omitempty"`
ForwardedFrom string `json:"forwarded_from,omitempty"`
From string `json:"from,omitempty"`
FromFormatted string `json:"from_formatted,omitempty"`
GroupSid string `json:"group_sid,omitempty"`
ParentCallSid string `json:"parent_call_sid,omitempty"`
PhoneNumberSid string `json:"phone_number_sid,omitempty"`
Price string `json:"price,omitempty"`
PriceUnit string `json:"price_unit,omitempty"`
QueueTime string `json:"queue_time,omitempty"`
StartTime string `json:"start_time,omitempty"`
Status string `json:"status,omitempty"`
SubresourceUris SubresourceUris `json:"subresource_uris,omitempty"`
To string `json:"to,omitempty"`
ToFormatted string `json:"to_formatted,omitempty"`
TrunkSid string `json:"trunk_sid,omitempty"`
URI string `json:"uri,omitempty"`
}

// SubresourceUris holds details for subresource uri's
type SubresourceUris struct {
Notifications string `json:"notifications,omitempty"`
Recordings string `json:"recordings,omitempty"`
Feedback string `json:"feedback,omitempty"`
FeedbackSummaries string `json:"feedback_summaries,omitempty"`
Payments string `json:"payments,omitempty"`
}

// Call describes a outgoing call settings
type Call struct {
AccountSid string `url:"AccountSid,omitempty"`
ApplicationSid string `url:"ApplicationSid,omitempty"`
AsyncAmd string `url:"AsyncAmd,omitempty"`
AsyncAmdStatusCallback string `url:"AsyncAmdStatusCallback,omitempty"`
AsyncAmdStatusCallbackMethod string `url:"AsyncAmdStatusCallbackMethod,omitempty"`
Byoc string `url:"Byoc,omitempty"`
CallerId string `url:"CallerId,omitempty"`
CallReason string `url:"CallReason,omitempty"`
FallbackMethod string `url:"FallbackMethod,omitempty"`
FallbackUrl string `url:"FallbackUrl,omitempty"`
From string `url:"From,omitempty"`
MachineDetection string `url:"MachineDetection,omitempty"`
MachineDetectionSilenceTimeout int `url:"MachineDetectionSilenceTimeout,omitempty"`
MachineDetectionSpeechEndThreshold int `url:"MachineDetectionSpeechEndThreshold,omitempty"`
MachineDetectionSpeechThreshold int `url:"MachineDetectionSpeechThreshold,omitempty"`
MachineDetectionTimeout int `url:"MachineDetectionTimeout,omitempty"`
Method string `url:"Method,omitempty"`
Record bool `url:"Record,omitempty"`
RecordingChannels string `url:"RecordingChannels,omitempty"`
RecordingStatusCallback string `url:"RecordingStatusCallback,omitempty"`
RecordingStatusCallbackEvent string `url:"RecordingStatusCallbackEvent,omitempty"`
RecordingStatusCallbackMethod string `url:"RecordingStatusCallbackMethod,omitempty"`
SendDigits string `url:"SendDigits,omitempty"`
SipAuthPassword string `url:"SipAuthPassword,omitempty"`
SipAuthUsername string `url:"SipAuthUsername,omitempty"`
StatusCallback string `url:"StatusCallback,omitempty"`
StatusCallbackEvent string `url:"StatusCallbackEvent,omitempty"`
StatusCallbackMethod string `url:"StatusCallbackMethod,omitempty"`
Timeout int `url:"Timeout,omitempty"`
To string `url:"To,omitempty"`
Trim string `url:"Trim,omitempty"`
Twiml string `url:"Twiml,omitempty"`
URL string `url:"Url,omitempty"`
}

type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
MoreInfo string `json:"more_info"`
Status int `json:"status"`
}

func (a *APIError) Error() string {
return fmt.Sprintf("APIError: %s: more_info: %s", a.Message, a.MoreInfo)
}
17 changes: 17 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package twilio

import (
"encoding/json"
"io"

"github.com/pkg/errors"
)

func decodeError(r io.Reader) error {
apiError := &APIError{}
if err := json.NewDecoder(r).Decode(apiError); err != nil {
return errors.WithMessage(err, "decodeError()")
}

return apiError
}

0 comments on commit c14c123

Please sign in to comment.