diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 669c386..fe69b07 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -35,6 +35,8 @@ jobs:
BW_ACCOUNT_ID: ${{ secrets.BW_ACCOUNT_ID }}
BW_USERNAME: ${{ secrets.BW_USERNAME }}
BW_PASSWORD: ${{ secrets.BW_PASSWORD }}
+ BW_CLIENT_ID: ${{ secrets.BW_CLIENT_ID }}
+ BW_CLIENT_SECRET: ${{ secrets.BW_CLIENT_SECRET }}
BW_VOICE_APPLICATION_ID: ${{ secrets.BW_VOICE_APPLICATION_ID }}
BW_MESSAGING_APPLICATION_ID: ${{ secrets.BW_MESSAGING_APPLICATION_ID }}
BW_NUMBER: ${{ secrets.BW_NUMBER }}
diff --git a/.gitignore b/.gitignore
index 8cb8bd3..dd02192 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ composer.lock
.phpunit.result.cache
composer.phar
.idea
+.env*
\ No newline at end of file
diff --git a/src/BRTC/BRTCClient.php b/src/BRTC/BRTCClient.php
new file mode 100644
index 0000000..f01af03
--- /dev/null
+++ b/src/BRTC/BRTCClient.php
@@ -0,0 +1,35 @@
+config = $config;
+ }
+
+ private $client;
+
+ /**
+ * Provides access to the BRTC API controller
+ * @return Controllers\APIController
+ */
+ public function getClient()
+ {
+ if ($this->client == null) {
+ $this->client = new Controllers\APIController($this->config);
+ }
+ return $this->client;
+ }
+}
diff --git a/src/BRTC/Controllers/APIController.php b/src/BRTC/Controllers/APIController.php
new file mode 100644
index 0000000..4b918d0
--- /dev/null
+++ b/src/BRTC/Controllers/APIController.php
@@ -0,0 +1,366 @@
+ $accountId,
+ ));
+
+ //validate and preprocess url
+ $_queryUrl = APIHelper::cleanUrl($this->config->getBaseUri(Servers::BRTCDEFAULT) . $_queryBuilder);
+
+ //prepare headers
+ $_headers = array(
+ 'user-agent' => BaseController::USER_AGENT,
+ 'Accept' => 'application/json',
+ 'content-type' => 'application/json; charset=utf-8'
+ );
+
+ //json encode body
+ $_bodyJson = Request\Body::Json($body);
+
+ //set authentication via existing OAuth/configureAuth
+ $this->configureAuth($_headers, 'voice');
+
+ $_httpRequest = new HttpRequest(HttpMethod::POST, $_headers, $_queryUrl);
+
+ //call on-before Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnBeforeRequest($_httpRequest);
+ }
+
+ // Set request timeout
+ Request::timeout($this->config->getTimeout());
+
+ // and invoke the API call request to fetch the response
+ $response = Request::post($_queryUrl, $_headers, $_bodyJson);
+
+ $_httpResponse = new HttpResponse($response->code, $response->headers, $response->raw_body);
+ $_httpContext = new HttpContext($_httpRequest, $_httpResponse);
+
+ //call on-after Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnAfterRequest($_httpContext);
+ }
+
+ //handle errors defined at the API level
+ $this->validateResponse($_httpResponse, $_httpContext);
+
+ $mapper = $this->getJsonMapper();
+ $deserializedResponse = $mapper->mapClass(
+ $response->body,
+ 'BandwidthLib\\BRTC\\Models\\CreateEndpointResponse'
+ );
+ return new ApiResponse($response->code, $response->headers, $deserializedResponse);
+ }
+
+ /**
+ * Lists BRTC endpoints for an account.
+ *
+ * @param string $accountId
+ * @param string|null $type Filter by endpoint type
+ * @param string|null $status Filter by endpoint status
+ * @param string|null $direction Filter by endpoint direction
+ * @param string|null $pageToken Pagination token
+ * @param int|null $pageSize Number of results per page
+ * @return ApiResponse response from the API call
+ * @throws APIException Thrown if API call fails
+ */
+ public function listEndpoints(
+ string $accountId,
+ ?string $type = null,
+ ?string $status = null,
+ ?string $direction = null,
+ ?string $pageToken = null,
+ ?int $pageSize = null
+ ) {
+ //prepare query string for API call
+ $_queryBuilder = '/accounts/{accountId}/endpoints';
+
+ //process optional query parameters
+ $_queryBuilder = APIHelper::appendUrlWithTemplateParameters($_queryBuilder, array(
+ 'accountId' => $accountId,
+ ));
+
+ //process optional query parameters
+ APIHelper::appendUrlWithQueryParameters($_queryBuilder, array(
+ 'type' => $type,
+ 'status' => $status,
+ 'direction' => $direction,
+ 'pageToken' => $pageToken,
+ 'pageSize' => $pageSize,
+ ));
+
+ //validate and preprocess url
+ $_queryUrl = APIHelper::cleanUrl($this->config->getBaseUri(Servers::BRTCDEFAULT) . $_queryBuilder);
+
+ //prepare headers
+ $_headers = array(
+ 'user-agent' => BaseController::USER_AGENT,
+ 'Accept' => 'application/json'
+ );
+
+ //set authentication via existing OAuth/configureAuth
+ $this->configureAuth($_headers, 'voice');
+
+ $_httpRequest = new HttpRequest(HttpMethod::GET, $_headers, $_queryUrl);
+
+ //call on-before Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnBeforeRequest($_httpRequest);
+ }
+
+ // Set request timeout
+ Request::timeout($this->config->getTimeout());
+
+ // and invoke the API call request to fetch the response
+ $response = Request::get($_queryUrl, $_headers);
+
+ $_httpResponse = new HttpResponse($response->code, $response->headers, $response->raw_body);
+ $_httpContext = new HttpContext($_httpRequest, $_httpResponse);
+
+ //call on-after Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnAfterRequest($_httpContext);
+ }
+
+ //handle errors defined at the API level
+ $this->validateResponse($_httpResponse, $_httpContext);
+
+ $mapper = $this->getJsonMapper();
+ $deserializedResponse = $mapper->mapClass(
+ $response->body,
+ 'BandwidthLib\\BRTC\\Models\\ListEndpointsResponse'
+ );
+ return new ApiResponse($response->code, $response->headers, $deserializedResponse);
+ }
+
+ /**
+ * Gets details for a specific BRTC endpoint.
+ *
+ * @param string $accountId
+ * @param string $endpointId
+ * @return ApiResponse response from the API call
+ * @throws APIException Thrown if API call fails
+ */
+ public function getEndpoint(
+ string $accountId,
+ string $endpointId
+ ) {
+ //prepare query string for API call
+ $_queryBuilder = '/accounts/{accountId}/endpoints/{endpointId}';
+
+ //process optional query parameters
+ $_queryBuilder = APIHelper::appendUrlWithTemplateParameters($_queryBuilder, array(
+ 'accountId' => $accountId,
+ 'endpointId' => $endpointId,
+ ));
+
+ //validate and preprocess url
+ $_queryUrl = APIHelper::cleanUrl($this->config->getBaseUri(Servers::BRTCDEFAULT) . $_queryBuilder);
+
+ //prepare headers
+ $_headers = array(
+ 'user-agent' => BaseController::USER_AGENT,
+ 'Accept' => 'application/json'
+ );
+
+ //set authentication via existing OAuth/configureAuth
+ $this->configureAuth($_headers, 'voice');
+
+ $_httpRequest = new HttpRequest(HttpMethod::GET, $_headers, $_queryUrl);
+
+ //call on-before Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnBeforeRequest($_httpRequest);
+ }
+
+ // Set request timeout
+ Request::timeout($this->config->getTimeout());
+
+ // and invoke the API call request to fetch the response
+ $response = Request::get($_queryUrl, $_headers);
+
+ $_httpResponse = new HttpResponse($response->code, $response->headers, $response->raw_body);
+ $_httpContext = new HttpContext($_httpRequest, $_httpResponse);
+
+ //call on-after Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnAfterRequest($_httpContext);
+ }
+
+ //handle errors defined at the API level
+ $this->validateResponse($_httpResponse, $_httpContext);
+
+ $mapper = $this->getJsonMapper();
+ $deserializedResponse = $mapper->mapClass(
+ $response->body,
+ 'BandwidthLib\\BRTC\\Models\\EndpointResponse'
+ );
+ return new ApiResponse($response->code, $response->headers, $deserializedResponse);
+ }
+
+ /**
+ * Deletes a BRTC endpoint.
+ *
+ * @param string $accountId
+ * @param string $endpointId
+ * @return ApiResponse response from the API call
+ * @throws APIException Thrown if API call fails
+ */
+ public function deleteEndpoint(
+ string $accountId,
+ string $endpointId
+ ) {
+ //prepare query string for API call
+ $_queryBuilder = '/accounts/{accountId}/endpoints/{endpointId}';
+
+ //process optional query parameters
+ $_queryBuilder = APIHelper::appendUrlWithTemplateParameters($_queryBuilder, array(
+ 'accountId' => $accountId,
+ 'endpointId' => $endpointId,
+ ));
+
+ //validate and preprocess url
+ $_queryUrl = APIHelper::cleanUrl($this->config->getBaseUri(Servers::BRTCDEFAULT) . $_queryBuilder);
+
+ //prepare headers
+ $_headers = array(
+ 'user-agent' => BaseController::USER_AGENT,
+ 'Content-Type' => '', // prevent curl from injecting Content-Type: application/x-www-form-urlencoded on empty DELETE body
+ );
+
+ //set authentication via existing OAuth/configureAuth
+ $this->configureAuth($_headers, 'voice');
+
+ $_httpRequest = new HttpRequest(HttpMethod::DELETE, $_headers, $_queryUrl);
+
+ //call on-before Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnBeforeRequest($_httpRequest);
+ }
+
+ // Set request timeout
+ Request::timeout($this->config->getTimeout());
+
+ // and invoke the API call request to fetch the response
+ $response = Request::delete($_queryUrl, $_headers);
+
+ $_httpResponse = new HttpResponse($response->code, $response->headers, $response->raw_body);
+ $_httpContext = new HttpContext($_httpRequest, $_httpResponse);
+
+ //call on-after Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnAfterRequest($_httpContext);
+ }
+
+ //handle errors defined at the API level
+ $this->validateResponse($_httpResponse, $_httpContext);
+
+ return new ApiResponse($response->code, $response->headers, null);
+ }
+
+ /**
+ * Updates the BXML for a BRTC endpoint.
+ *
+ * @param string $accountId
+ * @param string $endpointId
+ * @param string $body Valid BXML string
+ * @return ApiResponse response from the API call
+ * @throws APIException Thrown if API call fails
+ */
+ public function updateEndpointBxml(
+ string $accountId,
+ string $endpointId,
+ string $body
+ ) {
+ //prepare query string for API call
+ $_queryBuilder = '/accounts/{accountId}/endpoints/{endpointId}/bxml';
+
+ //process optional query parameters
+ $_queryBuilder = APIHelper::appendUrlWithTemplateParameters($_queryBuilder, array(
+ 'accountId' => $accountId,
+ 'endpointId' => $endpointId,
+ ));
+
+ //validate and preprocess url
+ $_queryUrl = APIHelper::cleanUrl($this->config->getBaseUri(Servers::BRTCDEFAULT) . $_queryBuilder);
+
+ //prepare headers
+ $_headers = array(
+ 'user-agent' => BaseController::USER_AGENT,
+ 'content-type' => 'application/xml; charset=utf-8'
+ );
+
+ //set authentication via existing OAuth/configureAuth
+ $this->configureAuth($_headers, 'voice');
+
+ $_httpRequest = new HttpRequest(HttpMethod::PUT, $_headers, $_queryUrl);
+
+ //call on-before Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnBeforeRequest($_httpRequest);
+ }
+
+ // Set request timeout
+ Request::timeout($this->config->getTimeout());
+
+ // and invoke the API call request to fetch the response
+ $response = Request::put($_queryUrl, $_headers, $body);
+
+ $_httpResponse = new HttpResponse($response->code, $response->headers, $response->raw_body);
+ $_httpContext = new HttpContext($_httpRequest, $_httpResponse);
+
+ //call on-after Http callback
+ if ($this->getHttpCallBack() != null) {
+ $this->getHttpCallBack()->callOnAfterRequest($_httpContext);
+ }
+
+ //handle errors defined at the API level
+ $this->validateResponse($_httpResponse, $_httpContext);
+
+ return new ApiResponse($response->code, $response->headers, null);
+ }
+}
diff --git a/src/BRTC/Models/CreateEndpointRequest.php b/src/BRTC/Models/CreateEndpointRequest.php
new file mode 100644
index 0000000..9690d59
--- /dev/null
+++ b/src/BRTC/Models/CreateEndpointRequest.php
@@ -0,0 +1,57 @@
+type = $type;
+ $this->direction = $direction;
+ $this->eventCallbackUrl = $eventCallbackUrl;
+ $this->eventFallbackUrl = $eventFallbackUrl;
+ $this->tag = $tag;
+ $this->connectionMetadata = $connectionMetadata;
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['type'] = $this->type;
+ $json['direction'] = $this->direction;
+ $json['eventCallbackUrl'] = $this->eventCallbackUrl;
+ $json['eventFallbackUrl'] = $this->eventFallbackUrl;
+ $json['tag'] = $this->tag;
+ $json['connectionMetadata'] = $this->connectionMetadata;
+
+ return array_filter($json, function ($val) {
+ return $val !== null;
+ });
+ }
+}
diff --git a/src/BRTC/Models/CreateEndpointResponse.php b/src/BRTC/Models/CreateEndpointResponse.php
new file mode 100644
index 0000000..4464477
--- /dev/null
+++ b/src/BRTC/Models/CreateEndpointResponse.php
@@ -0,0 +1,40 @@
+links = func_get_arg(0);
+ $this->data = func_get_arg(1);
+ $this->errors = func_get_arg(2);
+ }
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['links'] = isset($this->links) ? array_values($this->links) : null;
+ $json['data'] = $this->data;
+ $json['errors'] = isset($this->errors) ? array_values($this->errors) : null;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/CreateEndpointResponseData.php b/src/BRTC/Models/CreateEndpointResponseData.php
new file mode 100644
index 0000000..169f604
--- /dev/null
+++ b/src/BRTC/Models/CreateEndpointResponseData.php
@@ -0,0 +1,65 @@
+endpointId = $endpointId;
+ $this->type = $type;
+ $this->status = $status;
+ $this->creationTimestamp = $creationTimestamp;
+ $this->expirationTimestamp = $expirationTimestamp;
+ $this->tag = $tag;
+ $this->devices = $devices;
+ $this->token = $token;
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['endpointId'] = $this->endpointId;
+ $json['type'] = $this->type;
+ $json['status'] = $this->status;
+ $json['creationTimestamp'] = $this->creationTimestamp;
+ $json['expirationTimestamp'] = $this->expirationTimestamp;
+ $json['tag'] = $this->tag;
+ $json['devices'] = $this->devices;
+ $json['token'] = $this->token;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/Device.php b/src/BRTC/Models/Device.php
new file mode 100644
index 0000000..724756b
--- /dev/null
+++ b/src/BRTC/Models/Device.php
@@ -0,0 +1,41 @@
+deviceId = $deviceId;
+ $this->deviceName = $deviceName;
+ $this->status = $status;
+ $this->creationTimestamp = $creationTimestamp;
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['deviceId'] = $this->deviceId;
+ $json['deviceName'] = $this->deviceName;
+ $json['status'] = $this->status;
+ $json['creationTimestamp'] = $this->creationTimestamp;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/Endpoint.php b/src/BRTC/Models/Endpoint.php
new file mode 100644
index 0000000..a12107a
--- /dev/null
+++ b/src/BRTC/Models/Endpoint.php
@@ -0,0 +1,60 @@
+endpointId = $endpointId;
+ $this->type = $type;
+ $this->status = $status;
+ $this->creationTimestamp = $creationTimestamp;
+ $this->expirationTimestamp = $expirationTimestamp;
+ $this->tag = $tag;
+ $this->devices = $devices;
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['endpointId'] = $this->endpointId;
+ $json['type'] = $this->type;
+ $json['status'] = $this->status;
+ $json['creationTimestamp'] = $this->creationTimestamp;
+ $json['expirationTimestamp'] = $this->expirationTimestamp;
+ $json['tag'] = $this->tag;
+ $json['devices'] = $this->devices;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/EndpointEvent.php b/src/BRTC/Models/EndpointEvent.php
new file mode 100644
index 0000000..27b27c7
--- /dev/null
+++ b/src/BRTC/Models/EndpointEvent.php
@@ -0,0 +1,71 @@
+endpointId = $endpointId;
+ $this->type = $type;
+ $this->status = $status;
+ $this->creationTimestamp = $creationTimestamp;
+ $this->expirationTimestamp = $expirationTimestamp;
+ $this->tag = $tag;
+ $this->eventTime = $eventTime;
+ $this->eventType = $eventType;
+ $this->device = $device;
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['endpointId'] = $this->endpointId;
+ $json['type'] = $this->type;
+ $json['status'] = $this->status;
+ $json['creationTimestamp'] = $this->creationTimestamp;
+ $json['expirationTimestamp'] = $this->expirationTimestamp;
+ $json['tag'] = $this->tag;
+ $json['eventTime'] = $this->eventTime;
+ $json['eventType'] = $this->eventType;
+ $json['device'] = $this->device;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/EndpointResponse.php b/src/BRTC/Models/EndpointResponse.php
new file mode 100644
index 0000000..a30710f
--- /dev/null
+++ b/src/BRTC/Models/EndpointResponse.php
@@ -0,0 +1,40 @@
+links = func_get_arg(0);
+ $this->data = func_get_arg(1);
+ $this->errors = func_get_arg(2);
+ }
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['links'] = isset($this->links) ? array_values($this->links) : null;
+ $json['data'] = $this->data;
+ $json['errors'] = isset($this->errors) ? array_values($this->errors) : null;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/Endpoints.php b/src/BRTC/Models/Endpoints.php
new file mode 100644
index 0000000..3f1895b
--- /dev/null
+++ b/src/BRTC/Models/Endpoints.php
@@ -0,0 +1,56 @@
+endpointId = $endpointId;
+ $this->type = $type;
+ $this->status = $status;
+ $this->creationTimestamp = $creationTimestamp;
+ $this->expirationTimestamp = $expirationTimestamp;
+ $this->tag = $tag;
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['endpointId'] = $this->endpointId;
+ $json['type'] = $this->type;
+ $json['status'] = $this->status;
+ $json['creationTimestamp'] = $this->creationTimestamp;
+ $json['expirationTimestamp'] = $this->expirationTimestamp;
+ $json['tag'] = $this->tag;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/ErrorObject.php b/src/BRTC/Models/ErrorObject.php
new file mode 100644
index 0000000..cc090c0
--- /dev/null
+++ b/src/BRTC/Models/ErrorObject.php
@@ -0,0 +1,35 @@
+type = func_get_arg(0);
+ $this->description = func_get_arg(1);
+ }
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['type'] = $this->type;
+ $json['description'] = $this->description;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/Link.php b/src/BRTC/Models/Link.php
new file mode 100644
index 0000000..8e9c545
--- /dev/null
+++ b/src/BRTC/Models/Link.php
@@ -0,0 +1,35 @@
+rel = func_get_arg(0);
+ $this->href = func_get_arg(1);
+ }
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['rel'] = $this->rel;
+ $json['href'] = $this->href;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/ListEndpointsResponse.php b/src/BRTC/Models/ListEndpointsResponse.php
new file mode 100644
index 0000000..82f6fd0
--- /dev/null
+++ b/src/BRTC/Models/ListEndpointsResponse.php
@@ -0,0 +1,44 @@
+links = func_get_arg(0);
+ $this->page = func_get_arg(1);
+ $this->data = func_get_arg(2);
+ $this->errors = func_get_arg(3);
+ }
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['links'] = isset($this->links) ? array_values($this->links) : null;
+ $json['page'] = $this->page;
+ $json['data'] = isset($this->data) ? array_values($this->data) : null;
+ $json['errors'] = isset($this->errors) ? array_values($this->errors) : null;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BRTC/Models/Page.php b/src/BRTC/Models/Page.php
new file mode 100644
index 0000000..6e9cecd
--- /dev/null
+++ b/src/BRTC/Models/Page.php
@@ -0,0 +1,43 @@
+pageSize = func_get_arg(0);
+ $this->totalElements = func_get_arg(1);
+ $this->totalPages = func_get_arg(2);
+ $this->pageNumber = func_get_arg(3);
+ }
+ }
+
+ public function jsonSerialize(): array
+ {
+ $json = array();
+ $json['pageSize'] = $this->pageSize;
+ $json['totalElements'] = $this->totalElements;
+ $json['totalPages'] = $this->totalPages;
+ $json['pageNumber'] = $this->pageNumber;
+
+ return array_filter($json);
+ }
+}
diff --git a/src/BandwidthClient.php b/src/BandwidthClient.php
index 8e80179..915ccbd 100644
--- a/src/BandwidthClient.php
+++ b/src/BandwidthClient.php
@@ -25,6 +25,7 @@ public function __construct($config)
private $phoneNumberLookup;
private $voice;
private $webRtc;
+ private $brtc;
/**
* Provides access to Messaging client
@@ -86,4 +87,16 @@ public function getWebRtc()
return $this->webRtc;
}
+ /**
+ * Provides access to BRTC client
+ * @return BRTC\BRTCClient
+ */
+ public function getBRTC()
+ {
+ if ($this->brtc == null) {
+ $this->brtc = new BRTC\BRTCClient($this->config);
+ }
+ return $this->brtc;
+ }
+
}
diff --git a/src/Configuration.php b/src/Configuration.php
index 2f25f72..6467105 100644
--- a/src/Configuration.php
+++ b/src/Configuration.php
@@ -371,6 +371,7 @@ public function getBaseUri(string $server = Servers::DEFAULT_)
Servers::PHONENUMBERLOOKUPDEFAULT => 'https://api.bandwidth.com/v2',
Servers::VOICEDEFAULT => 'https://voice.bandwidth.com',
Servers::WEBRTCDEFAULT => 'https://api.webrtc.bandwidth.com/v1',
+ Servers::BRTCDEFAULT => 'https://api.bandwidth.com/v2',
),
Environments::CUSTOM => array(
Servers::DEFAULT_ => '{base_url}',
@@ -379,6 +380,7 @@ public function getBaseUri(string $server = Servers::DEFAULT_)
Servers::PHONENUMBERLOOKUPDEFAULT => '{base_url}',
Servers::VOICEDEFAULT => '{base_url}',
Servers::WEBRTCDEFAULT => '{base_url}',
+ Servers::BRTCDEFAULT => '{base_url}',
),
);
}
diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php
index 7a41cc3..ee1dd98 100644
--- a/src/Controllers/BaseController.php
+++ b/src/Controllers/BaseController.php
@@ -82,14 +82,14 @@ protected function validateResponse(HttpResponse $response, HttpContext $_httpCo
*/
protected function configureAuth(&$headers, $authType)
{
- if (!empty($this->config->getAccessToken()) &&
- (empty($this->config->getAccessTokenExpiration()) ||
+ if (!empty($this->config->getAccessToken()) &&
+ (empty($this->config->getAccessTokenExpiration()) ||
$this->config->getAccessTokenExpiration() > time() + 60)
) {
$headers['Authorization'] = 'Bearer ' . $this->config->getAccessToken();
return;
}
-
+
if (!empty($this->config->getClientId()) && !empty($this->config->getClientSecret())) {
$_tokenUrl = 'https://api.bandwidth.com/api/v1/oauth2/token';
$_tokenHeaders = array (
@@ -109,10 +109,10 @@ protected function configureAuth(&$headers, $authType)
return;
}
-
+
$username = '';
$password = '';
-
+
switch ($authType) {
case 'messaging':
$username = $this->config->getMessagingBasicAuthUserName();
@@ -135,7 +135,7 @@ protected function configureAuth(&$headers, $authType)
$password = $this->config->getMultiFactorAuthBasicAuthPassword();
break;
}
-
+
Request::auth($username, $password);
}
}
diff --git a/src/Servers.php b/src/Servers.php
index 4c22bea..ef5463e 100644
--- a/src/Servers.php
+++ b/src/Servers.php
@@ -41,4 +41,9 @@ class Servers
* TODO: Write general description for this element
*/
const WEBRTCDEFAULT = "WebRtcDefault";
+
+ /**
+ * Base URL for BRTC endpoints
+ */
+ const BRTCDEFAULT = "BRTCDefault";
}
diff --git a/src/Voice/Bxml/Connect.php b/src/Voice/Bxml/Connect.php
new file mode 100644
index 0000000..bceb7f4
--- /dev/null
+++ b/src/Voice/Bxml/Connect.php
@@ -0,0 +1,77 @@
+endpoints = $endpoints;
+ }
+
+ /**
+ * Add an Endpoint to the Connect verb
+ *
+ * @param Endpoint $endpoint
+ * @return $this
+ */
+ public function addEndpoint(Endpoint $endpoint): Connect {
+ $this->endpoints[] = $endpoint;
+ return $this;
+ }
+
+ /**
+ * Sets the eventCallbackUrl attribute for Connect
+ *
+ * @param string $eventCallbackUrl The URL to send event callbacks to
+ * @return $this
+ */
+ public function eventCallbackUrl(string $eventCallbackUrl): Connect {
+ $this->eventCallbackUrl = $eventCallbackUrl;
+ return $this;
+ }
+
+ /**
+ * Converts the Connect verb into a DOMElement
+ *
+ * @param DOMDocument $doc
+ * @return DOMElement
+ */
+ public function toBxml(DOMDocument $doc): DOMElement {
+ $element = $doc->createElement("Connect");
+
+ if(isset($this->eventCallbackUrl)) {
+ $element->setAttribute("eventCallbackUrl", $this->eventCallbackUrl);
+ }
+
+ if(isset($this->endpoints)) {
+ foreach ($this->endpoints as $endpoint) {
+ $element->appendChild($endpoint->toBxml($doc));
+ }
+ }
+
+ return $element;
+ }
+}
diff --git a/src/Voice/Bxml/Endpoint.php b/src/Voice/Bxml/Endpoint.php
new file mode 100644
index 0000000..6a29845
--- /dev/null
+++ b/src/Voice/Bxml/Endpoint.php
@@ -0,0 +1,41 @@
+endpointId = $endpointId;
+ }
+
+ /**
+ * Converts the Endpoint verb into a DOMElement
+ *
+ * @param DOMDocument $doc
+ * @return DOMElement
+ */
+ public function toBxml(DOMDocument $doc): DOMElement {
+ $element = $doc->createElement("Endpoint");
+ $element->appendChild($doc->createTextNode($this->endpointId));
+ return $element;
+ }
+}
diff --git a/src/Voice/Controllers/APIController.php b/src/Voice/Controllers/APIController.php
index 82a7461..659b831 100644
--- a/src/Voice/Controllers/APIController.php
+++ b/src/Voice/Controllers/APIController.php
@@ -929,7 +929,7 @@ public function getDownloadCallRecording(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/calls/{callId}/recordings/{recordingId}/media';
//process optional query parameters
@@ -1040,7 +1040,7 @@ public function deleteRecordingMedia(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/calls/{callId}/recordings/{recordingId}/media';
//process optional query parameters
@@ -1149,7 +1149,7 @@ public function getCallTranscription(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/calls/{callId}/recordings/{recordingId}/transcription';
//process optional query parameters
@@ -1266,7 +1266,7 @@ public function createTranscribeCallRecording(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/calls/{callId}/recordings/{recordingId}/transcription';
//process optional query parameters
@@ -1386,7 +1386,7 @@ public function deleteCallTranscription(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/calls/{callId}/recordings/{recordingId}/transcription';
//process optional query parameters
@@ -1952,7 +1952,7 @@ public function getConferenceMember(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/conferences/{conferenceId}/members/{memberId}';
//process optional query parameters
@@ -2179,7 +2179,7 @@ public function getConferenceRecording(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/conferences/{conferenceId}/recordings/{recordingId}';
//process optional query parameters
@@ -2294,7 +2294,7 @@ public function getDownloadConferenceRecording(
) {
//prepare query string for API call
- $_queryBuilder =
+ $_queryBuilder =
'/api/v2/accounts/{accountId}/conferences/{conferenceId}/recordings/{recordingId}/media';
//process optional query parameters
diff --git a/src/Voice/VoiceClient.php b/src/Voice/VoiceClient.php
index 089b842..c79a0bd 100644
--- a/src/Voice/VoiceClient.php
+++ b/src/Voice/VoiceClient.php
@@ -34,4 +34,5 @@ public function getClient()
}
return $this->client;
}
+
}
diff --git a/tests/ApiTest.php b/tests/ApiTest.php
index 44c9bc9..8c04d6a 100644
--- a/tests/ApiTest.php
+++ b/tests/ApiTest.php
@@ -14,7 +14,6 @@ final class ApiTest extends TestCase
{
protected static $bandwidthClient;
protected static $messagingMFAClient;
-
public static function setUpBeforeClass(): void {
$config = new BandwidthLib\Configuration(
array(
@@ -72,7 +71,7 @@ public function testUploadDownloadMedia() {
$mediaId = "text-media-id-" . uniqid() . ".txt";
$content = "Hello world";
$contentType = 'text/plain';
-
+
//media upload
self::$messagingMFAClient->getMessaging()->getClient()->uploadMedia(getenv("BW_ACCOUNT_ID"), $mediaId, $content, $contentType);
@@ -130,7 +129,7 @@ public function testCreateCallWithAmdAndGetCallState() {
//get phone call information
// $response = self::$bandwidthClient->getVoice()->getClient()->getCall(getenv("BW_ACCOUNT_ID"), $callId);
- // if (($response->getStatus() == 404) ) {
+ // if (($response->getStatus() == 404) ) {
// $this->assertTrue(is_a($response->getResult()->enqueuedTime, 'DateTime'));
// }
}
@@ -230,7 +229,7 @@ public function testAsyncTnLookup() {
$this->assertIsString($statusResponse->getResult()->data->results[0]->countryCodeA3);
$this->assertIsArray($statusResponse->getResult()->errors);
}
-
+
public function testSyncTnLookup() {
$body = new BandwidthLib\PhoneNumberLookup\Models\CreateLookupRequest();
$body->phoneNumbers = [getenv("USER_NUMBER")];
@@ -251,4 +250,86 @@ public function testSyncTnLookup() {
$this->assertIsString($response->getResult()->data->results[0]->countryCodeA3);
$this->assertIsArray($response->getResult()->errors);
}
+
+ public function testCreateListGetDeleteEndpoint() {
+ $accountId = getenv("BW_ACCOUNT_ID");
+ $brtcClient = self::$bandwidthClient->getBRTC()->getClient();
+
+ // Create endpoint
+ $createReq = new BandwidthLib\BRTC\Models\CreateEndpointRequest(
+ 'WEBRTC',
+ 'INBOUND',
+ getenv("BASE_CALLBACK_URL") . "/brtc/events",
+ null,
+ 'php-sdk-test'
+ );
+ $createResp = $brtcClient->createEndpoint($accountId, $createReq)->getResult();
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\CreateEndpointResponse::class, $createResp);
+ $this->assertIsArray($createResp->links);
+ $this->assertNotEmpty($createResp->links);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\Link::class, $createResp->links[0]);
+ $this->assertIsString($createResp->links[0]->rel);
+ $this->assertIsString($createResp->links[0]->href);
+ $this->assertNotNull($createResp->data);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\CreateEndpointResponseData::class, $createResp->data);
+ $this->assertNotNull($createResp->data->endpointId);
+ $this->assertIsString($createResp->data->endpointId);
+ $this->assertEquals('WEBRTC', $createResp->data->type);
+ $this->assertNotNull($createResp->data->status);
+ $this->assertNotNull($createResp->data->creationTimestamp);
+ $this->assertNotNull($createResp->data->expirationTimestamp);
+ $this->assertEquals('php-sdk-test', $createResp->data->tag);
+ $this->assertNotNull($createResp->data->token);
+ $this->assertIsString($createResp->data->token);
+ $this->assertIsArray($createResp->data->devices);
+ $this->assertIsArray($createResp->errors);
+
+ $endpointId = $createResp->data->endpointId;
+
+ // List endpoints
+ $listResp = $brtcClient->listEndpoints($accountId)->getResult();
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\ListEndpointsResponse::class, $listResp);
+ $this->assertIsArray($listResp->links);
+ $this->assertNotEmpty($listResp->links);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\Link::class, $listResp->links[0]);
+ $this->assertIsString($listResp->links[0]->rel);
+ $this->assertIsString($listResp->links[0]->href);
+ $this->assertNotNull($listResp->page);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\Page::class, $listResp->page);
+ $this->assertIsArray($listResp->data);
+ $this->assertNotEmpty($listResp->data);
+ $this->assertIsArray($listResp->errors);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\Endpoints::class, $listResp->data[0]);
+ $this->assertNotNull($listResp->data[0]->endpointId);
+ $this->assertNotNull($listResp->data[0]->type);
+ $this->assertNotNull($listResp->data[0]->status);
+ $this->assertNotNull($listResp->data[0]->creationTimestamp);
+ $this->assertNotNull($listResp->data[0]->expirationTimestamp);
+ $this->assertNotNull($listResp->data[0]->tag);
+ $ids = array_map(fn($ep) => $ep->endpointId, $listResp->data);
+ $this->assertContains($endpointId, $ids, 'Created endpoint should be in list');
+
+ // Get endpoint
+ $getResp = $brtcClient->getEndpoint($accountId, $endpointId)->getResult();
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\EndpointResponse::class, $getResp);
+ $this->assertIsArray($getResp->links);
+ $this->assertNotEmpty($getResp->links);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\Link::class, $getResp->links[0]);
+ $this->assertIsString($getResp->links[0]->rel);
+ $this->assertIsString($getResp->links[0]->href);
+ $this->assertNotNull($getResp->data);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\Endpoint::class, $getResp->data);
+ $this->assertEquals($endpointId, $getResp->data->endpointId);
+ $this->assertEquals('WEBRTC', $getResp->data->type);
+ $this->assertNotNull($getResp->data->status);
+ $this->assertNotNull($getResp->data->creationTimestamp);
+ $this->assertNotNull($getResp->data->expirationTimestamp);
+ $this->assertEquals('php-sdk-test', $getResp->data->tag);
+ $this->assertIsArray($getResp->data->devices);
+ $this->assertIsArray($getResp->errors);
+
+ // Delete endpoint
+ $deleteResp = $brtcClient->deleteEndpoint($accountId, $endpointId);
+ $this->assertEquals(204, $deleteResp->getStatusCode());
+ }
}
diff --git a/tests/BrtcModelTest.php b/tests/BrtcModelTest.php
new file mode 100644
index 0000000..a5bc438
--- /dev/null
+++ b/tests/BrtcModelTest.php
@@ -0,0 +1,120 @@
+assertEquals('ep-123', $event->endpointId);
+ $this->assertEquals('WEBRTC', $event->type);
+ $this->assertEquals('ACTIVE', $event->status);
+ $this->assertEquals('2025-01-01T00:00:00Z', $event->creationTimestamp);
+ $this->assertEquals('2025-01-02T00:00:00Z', $event->expirationTimestamp);
+ $this->assertEquals('test-tag', $event->tag);
+ $this->assertEquals('2025-01-01T01:00:00Z', $event->eventTime);
+ $this->assertEquals('DEVICE_CONNECTED', $event->eventType);
+ $this->assertInstanceOf(BandwidthLib\BRTC\Models\Device::class, $event->device);
+ $this->assertEquals('dev-123', $event->device->deviceId);
+
+ $json = $event->jsonSerialize();
+ $this->assertEquals('ep-123', $json['endpointId']);
+ $this->assertEquals('DEVICE_CONNECTED', $json['eventType']);
+ $this->assertEquals('2025-01-01T01:00:00Z', $json['eventTime']);
+ }
+
+ public function testEndpointEventModelDefaults() {
+ $event = new BandwidthLib\BRTC\Models\EndpointEvent();
+ $this->assertNull($event->endpointId);
+ $this->assertNull($event->type);
+ $this->assertNull($event->device);
+ $this->assertNull($event->eventType);
+ }
+
+ public function testDeviceModel() {
+ $device = new BandwidthLib\BRTC\Models\Device('dev-456', 'My Phone', 'ACTIVE', '2025-01-01T00:00:00Z');
+
+ $this->assertEquals('dev-456', $device->deviceId);
+ $this->assertEquals('My Phone', $device->deviceName);
+ $this->assertEquals('ACTIVE', $device->status);
+ $this->assertEquals('2025-01-01T00:00:00Z', $device->creationTimestamp);
+
+ $json = $device->jsonSerialize();
+ $this->assertEquals('dev-456', $json['deviceId']);
+ $this->assertEquals('My Phone', $json['deviceName']);
+ $this->assertEquals('ACTIVE', $json['status']);
+ $this->assertEquals('2025-01-01T00:00:00Z', $json['creationTimestamp']);
+ }
+
+ public function testDeviceModelDefaults() {
+ $device = new BandwidthLib\BRTC\Models\Device();
+ $this->assertNull($device->deviceId);
+ $this->assertNull($device->deviceName);
+ $this->assertNull($device->status);
+ $this->assertNull($device->creationTimestamp);
+ }
+
+ public function testPageModel() {
+ $page = new BandwidthLib\BRTC\Models\Page(20, 100, 5, 1);
+
+ $this->assertEquals(20, $page->pageSize);
+ $this->assertEquals(100, $page->totalElements);
+ $this->assertEquals(5, $page->totalPages);
+ $this->assertEquals(1, $page->pageNumber);
+
+ $json = $page->jsonSerialize();
+ $this->assertEquals(20, $json['pageSize']);
+ $this->assertEquals(100, $json['totalElements']);
+ $this->assertEquals(5, $json['totalPages']);
+ $this->assertEquals(1, $json['pageNumber']);
+ }
+
+ public function testPageModelDefaults() {
+ $page = new BandwidthLib\BRTC\Models\Page();
+ $this->assertNull($page->pageSize);
+ $this->assertNull($page->totalElements);
+ }
+
+ public function testLinkModel() {
+ $link = new BandwidthLib\BRTC\Models\Link('self', 'https://api.bandwidth.com/endpoints');
+
+ $this->assertEquals('self', $link->rel);
+ $this->assertEquals('https://api.bandwidth.com/endpoints', $link->href);
+
+ $json = $link->jsonSerialize();
+ $this->assertEquals('self', $json['rel']);
+ $this->assertEquals('https://api.bandwidth.com/endpoints', $json['href']);
+ }
+
+ public function testErrorObjectModel() {
+ $error = new BandwidthLib\BRTC\Models\ErrorObject('VALIDATION_ERROR', 'Field is required');
+
+ $this->assertEquals('VALIDATION_ERROR', $error->type);
+ $this->assertEquals('Field is required', $error->description);
+
+ $json = $error->jsonSerialize();
+ $this->assertEquals('VALIDATION_ERROR', $json['type']);
+ $this->assertEquals('Field is required', $json['description']);
+ }
+}
diff --git a/tests/BxmlTest.php b/tests/BxmlTest.php
index 44bdb2f..7fd5a74 100644
--- a/tests/BxmlTest.php
+++ b/tests/BxmlTest.php
@@ -540,4 +540,29 @@ public function testStopTranscription() {
$responseXml = $response->toBxml();
$this->assertEquals($expectedXml, $responseXml);
}
+
+ public function testConnectAndEndpoint() {
+ $endpoint1 = new BandwidthLib\Voice\Bxml\Endpoint("endpoint-123");
+ $endpoint2 = new BandwidthLib\Voice\Bxml\Endpoint("endpoint-456");
+ $connect = new BandwidthLib\Voice\Bxml\Connect([
+ $endpoint1,
+ $endpoint2
+ ]);
+ $response = new BandwidthLib\Voice\Bxml\Response();
+ $response->addVerb($connect);
+ $expectedXml = 'endpoint-123endpoint-456';
+ $responseXml = $response->toBxml();
+ $this->assertEquals($expectedXml, $responseXml);
+ }
+
+ public function testConnectWithEventCallbackUrl() {
+ $endpoint = new BandwidthLib\Voice\Bxml\Endpoint("endpoint-789");
+ $connect = new BandwidthLib\Voice\Bxml\Connect([$endpoint]);
+ $connect->eventCallbackUrl("https://example.com/events");
+ $response = new BandwidthLib\Voice\Bxml\Response();
+ $response->addVerb($connect);
+ $expectedXml = 'endpoint-789';
+ $responseXml = $response->toBxml();
+ $this->assertEquals($expectedXml, $responseXml);
+ }
}