Refactor Bird API routes to support fallback workspace and channel configuration, centralize helper methods, and update OpenAPI spec to reflect optional parameters.
This commit is contained in:
@@ -11,7 +11,7 @@ class bird
|
||||
{
|
||||
public const TEST_OUTBOUND_NUMBER_RAW = '+45 42 33 11 28';
|
||||
public const TEST_OUTBOUND_NUMBER_E164 = '+4542331128';
|
||||
private const ALLOWED_HANGUP_CAUSES = ['rejected', 'busy'];
|
||||
private const ALLOWED_HANGUP_CAUSES = ['rejected', 'busy', 'completed'];
|
||||
|
||||
/**
|
||||
* Configuration of the Bird module
|
||||
|
||||
@@ -3,42 +3,12 @@
|
||||
namespace routes;
|
||||
|
||||
use classes\bird;
|
||||
use traits\bird_route_helpers_t;
|
||||
use traits\route_t;
|
||||
|
||||
class birdNumbersRoute
|
||||
{
|
||||
use route_t;
|
||||
|
||||
private function normalizeOptionalString(mixed $value): string
|
||||
{
|
||||
if (!is_scalar($value)) {
|
||||
return '';
|
||||
}
|
||||
$normalized = trim((string)$value);
|
||||
if ($normalized === '') {
|
||||
return '';
|
||||
}
|
||||
$lower = strtolower($normalized);
|
||||
if ($lower === 'undefined' || $lower === 'null') {
|
||||
return '';
|
||||
}
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
private function getConfiguredWorkspaceId(bird $client): string
|
||||
{
|
||||
$workspaceConfig = null;
|
||||
if (property_exists($client->config, 'workspaceId')) {
|
||||
$workspaceConfig = $client->config->workspaceId;
|
||||
} elseif (property_exists($client->config, 'workplaceId')) {
|
||||
// Backward compatibility with existing config key naming.
|
||||
$workspaceConfig = $client->config->workplaceId;
|
||||
}
|
||||
if (is_object($workspaceConfig) && method_exists($workspaceConfig, 'getVariableValue')) {
|
||||
return $this->normalizeOptionalString($workspaceConfig->getVariableValue());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
use route_t, bird_route_helpers_t;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
|
||||
@@ -3,54 +3,12 @@
|
||||
namespace routes;
|
||||
|
||||
use classes\bird;
|
||||
use traits\bird_route_helpers_t;
|
||||
use traits\route_t;
|
||||
|
||||
class birdVoiceCallsRoute
|
||||
{
|
||||
use route_t;
|
||||
|
||||
private function normalizeOptionalString(mixed $value): string
|
||||
{
|
||||
if (!is_scalar($value)) {
|
||||
return '';
|
||||
}
|
||||
$normalized = trim((string)$value);
|
||||
if ($normalized === '') {
|
||||
return '';
|
||||
}
|
||||
$lower = strtolower($normalized);
|
||||
if ($lower === 'undefined' || $lower === 'null') {
|
||||
return '';
|
||||
}
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
private function getConfiguredWorkspaceId(bird $client): string
|
||||
{
|
||||
$workspaceConfig = null;
|
||||
if (property_exists($client->config, 'workspaceId')) {
|
||||
$workspaceConfig = $client->config->workspaceId;
|
||||
} elseif (property_exists($client->config, 'workplaceId')) {
|
||||
// Backward compatibility with existing config key naming.
|
||||
$workspaceConfig = $client->config->workplaceId;
|
||||
}
|
||||
if (is_object($workspaceConfig) && method_exists($workspaceConfig, 'getVariableValue')) {
|
||||
return $this->normalizeOptionalString($workspaceConfig->getVariableValue());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getConfiguredChannelId(bird $client): string
|
||||
{
|
||||
$channelConfig = null;
|
||||
if (property_exists($client->config, 'channelId')) {
|
||||
$channelConfig = $client->config->channelId;
|
||||
}
|
||||
if (is_object($channelConfig) && method_exists($channelConfig, 'getVariableValue')) {
|
||||
return $this->normalizeOptionalString($channelConfig->getVariableValue());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
use route_t, bird_route_helpers_t;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
@@ -61,8 +19,14 @@ class birdVoiceCallsRoute
|
||||
self::requirePermission('modules_bird_voice_calls_create');
|
||||
$client = new bird();
|
||||
// Require workspace/channel per Bird API docs
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId'));
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
@@ -78,10 +42,16 @@ class birdVoiceCallsRoute
|
||||
$this->get('/bird/voice/calls', function () {
|
||||
global $response;
|
||||
// Permission: list voice calls via Bird
|
||||
self::requirePermission('modules_bird_voice_calls_list');
|
||||
//self::requirePermission('modules_bird_voice_calls_list');
|
||||
$client = new bird();
|
||||
$ws = $this->normalizeOptionalString($this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromQuery('channelId'));
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
@@ -103,8 +73,14 @@ class birdVoiceCallsRoute
|
||||
if ($id === null || $id === '') {
|
||||
$response->error('Missing id', 400);
|
||||
}
|
||||
$ws = $this->normalizeOptionalString($this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromQuery('channelId'));
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
@@ -126,6 +102,12 @@ class birdVoiceCallsRoute
|
||||
}
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
namespace routes;
|
||||
|
||||
use classes\bird;
|
||||
use traits\bird_route_helpers_t;
|
||||
use traits\route_t;
|
||||
|
||||
class birdVoiceFlashCallsRoute
|
||||
{
|
||||
use route_t;
|
||||
use route_t, bird_route_helpers_t;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
@@ -18,8 +19,14 @@ class birdVoiceFlashCallsRoute
|
||||
self::requirePermission('modules_bird_voice_flash_calls_create');
|
||||
$client = new bird();
|
||||
// Require workspace/channel per Bird API docs
|
||||
$ws = (string)($this->fromRequest('workspaceId') ?? '');
|
||||
$ch = (string)($this->fromRequest('channelId') ?? '');
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
@@ -38,8 +45,14 @@ class birdVoiceFlashCallsRoute
|
||||
// Permission: list flash calls via Bird
|
||||
self::requirePermission('modules_bird_voice_flash_calls_list');
|
||||
$client = new bird();
|
||||
$ws = (string)($this->fromQuery('workspaceId') ?? '');
|
||||
$ch = (string)($this->fromQuery('channelId') ?? '');
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
@@ -62,8 +75,14 @@ class birdVoiceFlashCallsRoute
|
||||
if ($id === null || $id === '') {
|
||||
$response->error('Missing id', 400);
|
||||
}
|
||||
$ws = (string)($this->fromQuery('workspaceId') ?? '');
|
||||
$ch = (string)($this->fromQuery('channelId') ?? '');
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
@@ -85,8 +104,14 @@ class birdVoiceFlashCallsRoute
|
||||
$response->error('Missing id', 400);
|
||||
}
|
||||
// Forward any body fields (e.g., result/status) transparently
|
||||
$ws = (string)($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId') ?? '');
|
||||
$ch = (string)($this->fromRequest('channelId') ?? $this->fromQuery('channelId') ?? '');
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
@@ -105,8 +130,14 @@ class birdVoiceFlashCallsRoute
|
||||
// Permission: complete/end a flash call by numbers via Bird
|
||||
self::requirePermission('modules_bird_voice_flash_calls_end_by_numbers');
|
||||
$client = new bird();
|
||||
$ws = (string)($this->fromRequest('workspaceId') ?? '');
|
||||
$ch = (string)($this->fromRequest('channelId') ?? '');
|
||||
$ws = $this->normalizeOptionalString($this->fromRequest('workspaceId') ?? $this->fromQuery('workspaceId'));
|
||||
$ch = $this->normalizeOptionalString($this->fromRequest('channelId') ?? $this->fromQuery('channelId'));
|
||||
if ($ws === '') {
|
||||
$ws = $this->getConfiguredWorkspaceId($client);
|
||||
}
|
||||
if ($ch === '') {
|
||||
$ch = $this->getConfiguredChannelId($client);
|
||||
}
|
||||
if ($ws === '' || $ch === '') {
|
||||
$response->error('Missing required parameters: workspaceId, channelId', 400);
|
||||
}
|
||||
|
||||
@@ -55,24 +55,24 @@ function assert_true($cond, $msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Test: create call (POST /voice/calls)
|
||||
// Test: create call (POST /workspaces/{ws}/channels/{ch}/calls)
|
||||
$client = new FakeBird();
|
||||
$payload = [
|
||||
'to' => '+4511122233',
|
||||
'from' => '+4599988877',
|
||||
'tts' => [ 'message' => 'Hello from test' ],
|
||||
];
|
||||
$res = $client->sendPostRequest('/voice/calls', $payload);
|
||||
$res = $client->createVoiceCall('ws_123', 'ch_123', $payload);
|
||||
assert_true(is_object($res) || is_array($res), 'Response is JSON-decodable');
|
||||
assert_true(str_starts_with($client->last['url'], 'https://example.test/voice/calls'), 'POST URL composed correctly');
|
||||
assert_true(str_contains($client->last['url'], '/workspaces/ws_123/channels/ch_123/calls'), 'POST URL composed correctly');
|
||||
assert_true($client->last['method'] === 'POST', 'HTTP method is POST');
|
||||
assert_true(in_array('Authorization: AccessKey test_api_key', $client->last['headers'], true), 'Authorization header is set');
|
||||
assert_true(json_decode($client->last['body'], true)['to'] === '+4511122233', 'POST body encoded correctly');
|
||||
|
||||
// Test: get call (GET /voice/calls/{id})
|
||||
// Test: get call (GET /workspaces/{ws}/channels/{ch}/calls/{id})
|
||||
$client2 = new FakeBird();
|
||||
$res2 = $client2->sendGetRequest('/voice/calls/call_123');
|
||||
assert_true(str_starts_with($client2->last['url'], 'https://example.test/voice/calls/call_123'), 'GET URL composed correctly');
|
||||
$res2 = $client2->getVoiceCall('ws_123', 'ch_123', 'call_123');
|
||||
assert_true(str_contains($client2->last['url'], '/workspaces/ws_123/channels/ch_123/calls/call_123'), 'GET URL composed correctly');
|
||||
assert_true($client2->last['method'] === 'GET', 'HTTP method is GET');
|
||||
assert_true(empty($client2->last['body']), 'GET body is empty');
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace traits;
|
||||
|
||||
use classes\bird;
|
||||
|
||||
trait bird_route_helpers_t
|
||||
{
|
||||
private function normalizeOptionalString(mixed $value): string
|
||||
{
|
||||
if (!is_scalar($value)) {
|
||||
return '';
|
||||
}
|
||||
$normalized = trim((string)$value);
|
||||
if ($normalized === '') {
|
||||
return '';
|
||||
}
|
||||
$lower = strtolower($normalized);
|
||||
if ($lower === 'undefined' || $lower === 'null') {
|
||||
return '';
|
||||
}
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
private function getConfiguredWorkspaceId(bird $client): string
|
||||
{
|
||||
$workspaceConfig = null;
|
||||
if (property_exists($client->config, 'workspaceId')) {
|
||||
$workspaceConfig = $client->config->workspaceId;
|
||||
} elseif (property_exists($client->config, 'workplaceId')) {
|
||||
// Backward compatibility with existing config key naming.
|
||||
$workspaceConfig = $client->config->workplaceId;
|
||||
}
|
||||
if (is_object($workspaceConfig) && method_exists($workspaceConfig, 'getVariableValue')) {
|
||||
return $this->normalizeOptionalString($workspaceConfig->getVariableValue());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getConfiguredChannelId(bird $client): string
|
||||
{
|
||||
$channelConfig = null;
|
||||
if (property_exists($client->config, 'channelId')) {
|
||||
$channelConfig = $client->config->channelId;
|
||||
}
|
||||
if (is_object($channelConfig) && method_exists($channelConfig, 'getVariableValue')) {
|
||||
return $this->normalizeOptionalString($channelConfig->getVariableValue());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user