Implement Calls Statistics

This commit is contained in:
Timothée Jaussoin 2023-09-19 13:07:09 +00:00
parent d2c5e9f48f
commit a9fb3fd1c1
16 changed files with 623 additions and 126 deletions

View file

@ -45,9 +45,21 @@ class Account extends Authenticatable
public static $dtmfProtocols = ['sipinfo' => 'SIPInfo', 'rfc2833' => 'RFC2833', 'sipmessage' => 'SIP Message'];
/**
* Scopes
*/
public static function boot()
{
parent::boot();
static::deleted(function ($item) {
StatisticsMessage::where('from_username', $item->username)
->where('from_domain', $item->domain)
->delete();
StatisticsCall::where('from_username', $item->username)
->where('from_domain', $item->domain)
->delete();
});
}
protected static function booted()
{
static::addGlobalScope('domain', function (Builder $builder) {
@ -179,8 +191,8 @@ class Account extends Authenticatable
public function getFullIdentifierAttribute()
{
$displayName = $this->attributes['display_name']
? '"' . $this->attributes['display_name'] . '" '
: '';
? '"' . $this->attributes['display_name'] . '" '
: '';
return $displayName . '<sip:' . $this->getIdentifierAttribute() . '>';
}

View file

@ -3,10 +3,11 @@
namespace App\Http\Controllers\Admin;
use App\Account;
use App\StatisticsMessage;
use App\StatisticsCall;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\StatisticsMessage;
use Carbon\Carbon;
use Carbon\CarbonInterval;
use Carbon\CarbonPeriod;
@ -50,6 +51,12 @@ class StatisticsController extends Controller
$data = StatisticsMessage::orderBy($dateColumn, 'asc');
break;
case 'calls':
$dateColumn = 'initiated_at';
$label = 'Calls';
$data = StatisticsCall::orderBy($dateColumn, 'asc');
break;
case 'accounts':
$label = 'Accounts';
$data = Account::orderBy($dateColumn, 'asc');

View file

@ -0,0 +1,92 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\StatisticsCall;
use App\StatisticsCallDevice;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class StatisticsCallController extends Controller
{
public function store(Request $request)
{
$request->validate([
'id' => 'required|string|max:64',
'from' => 'required|string|max:256',
'to' => 'required|string|max:256',
'initiated_at' => 'required|iso_date',
'ended_at' => 'iso_date',
'conference_id' => 'string|nullable',
]);
$statisticsCall = new StatisticsCall;
$statisticsCall->id = $request->get('id');
list($statisticsCall->from_username, $statisticsCall->from_domain) = explode('@', $request->get('from'));
list($statisticsCall->to_username, $statisticsCall->to_domain) = explode('@', $request->get('to'));
$statisticsCall->initiated_at = $request->get('initiated_at');
$statisticsCall->ended_at = $request->get('ended_at');
//$statisticsCall->conference_id = $request->get('conference_id');
try {
return $statisticsCall->saveOrFail();
} catch (\Exception $e) {
Log::channel('database_errors')->error($e->getMessage());
abort(400, 'Database error');
}
}
public function storeDevice(Request $request, string $callId, string $deviceId)
{
$request->validate([
'rang_at' => 'iso_date',
'invite_terminated.at' => 'required_with:invite_terminated.state,iso_date',
'invite_terminated.state' => 'required_with:invite_terminated.at,string',
]);
try {
return StatisticsCallDevice::updateOrCreate(
['call_id' => $callId, 'device_id' => $deviceId],
[
'rang_at' => $request->get('rang_at'),
'invite_terminated_at' => $request->get('invite_terminated.at'),
'invite_terminated_state' => $request->get('invite_terminated.state')
]
);
} catch (\Exception $e) {
Log::channel('database_errors')->error($e->getMessage());
abort(400, 'Database error');
}
}
public function update(Request $request, string $callId)
{
$request->validate([
'ended_at' => 'required|iso_date',
]);
$statisticsCall = StatisticsCall::where('id', $callId)->firstOrFail();
$statisticsCall->ended_at = $request->get('ended_at');
$statisticsCall->save();
return $statisticsCall;
}
}

View file

@ -1,4 +1,21 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace App\Http\Controllers\Api;
@ -22,7 +39,7 @@ class StatisticsMessageController extends Controller
$statisticsMessage = new StatisticsMessage;
$statisticsMessage->id = $request->get('id');
$statisticsMessage->from = $request->get('from');
list($statisticsMessage->from_username, $statisticsMessage->from_domain) = explode('@', $request->get('from'));
$statisticsMessage->sent_at = $request->get('sent_at');
$statisticsMessage->encrypted = $request->get('encrypted');
//$statisticsMessage->conference_id = $request->get('conference_id');
@ -43,9 +60,11 @@ class StatisticsMessageController extends Controller
'received_at' => 'required|iso_date'
]);
list($toUsername, $toDomain) = explode('@', $to);
try {
return StatisticsMessageDevice::updateOrCreate(
['message_id' => $messageId, 'to' => $to, 'device_id' => $deviceId],
['message_id' => $messageId, 'to_username' => $toUsername, 'to_domain' => $toDomain, 'device_id' => $deviceId],
['last_status' => $request->get('last_status'), 'received_at' => $request->get('received_at')]
);
} catch (\Exception $e) {

View file

@ -0,0 +1,32 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class StatisticsCall extends Model
{
use HasFactory;
public $incrementing = false;
protected $casts = ['initiated_at' => 'datetime', 'ended_at' => 'datetime'];
protected $keyType = 'string';
}

View file

@ -0,0 +1,36 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class StatisticsCallDevice extends Model
{
use HasFactory;
protected $fillable = ['call_id', 'device_id', 'rang_at', 'invite_terminated_at', 'invite_terminated_state', 'call_id'];
protected $casts = ['rang_at' => 'datetime'];
public function call()
{
return $this->hasOne(StatisticsCall::class, 'id', 'call_id');
}
}

View file

@ -26,7 +26,7 @@ class StatisticsMessageDevice extends Model
{
use HasFactory;
protected $fillable = ['message_id', 'to', 'device_id', 'last_status', 'received_at'];
protected $fillable = ['message_id', 'to_username', 'to_domain', 'device_id', 'last_status', 'received_at'];
protected $casts = ['received_at' => 'datetime'];
public function message()

View file

@ -0,0 +1,21 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class StatisticsCallFactory extends Factory
{
public function definition(): array
{
return [
'id' => $this->faker->uuid(),
'from_username' => $this->faker->userName(),
'from_domain' => $this->faker->domainName(),
'to_username' => $this->faker->userName(),
'to_domain' => $this->faker->domainName(),
'initiated_at' => $this->faker->dateTimeBetween('-1 year'),
'ended_at' => $this->faker->dateTimeBetween('-1 year'),
];
}
}

View file

@ -10,7 +10,8 @@ class StatisticsMessageFactory extends Factory
{
return [
'id' => $this->faker->uuid(),
'from' => $this->faker->email(),
'from_username' => $this->faker->userName(),
'from_domain' => $this->faker->domainName(),
'sent_at' => $this->faker->dateTimeBetween('-1 year'),
'encrypted' => false
];

View file

@ -0,0 +1,105 @@
<?php
use App\StatisticsMessage;
use App\StatisticsMessageDevice;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('statistics_calls', function (Blueprint $table) {
$table->string('id', 64)->unique();
$table->string('from_username', 256);
$table->string('from_domain', 256);
$table->string('to_username', 256);
$table->string('to_domain', 256);
$table->dateTime('initiated_at');
$table->dateTime('ended_at')->nullable();
$table->string('conference_id')->nullable();
$table->timestamps();
$table->index(['from_username', 'from_domain']);
$table->index('initiated_at');
});
Schema::create('statistics_call_devices', function (Blueprint $table) {
$table->id();
$table->string('call_id', 64);
$table->string('device_id', 64);
$table->dateTime('rang_at')->nullable();
$table->dateTime('invite_terminated_at')->nullable();
$table->string('invite_terminated_state')->nullable();
$table->timestamps();
$table->foreign('call_id')->references('id')->on('statistics_calls')->onDelete('cascade');
$table->unique(['call_id', 'device_id']);
});
Schema::disableForeignKeyConstraints();
Schema::drop('statistics_message_devices');
Schema::drop('statistics_messages');
Schema::create('statistics_messages', function (Blueprint $table) {
$table->string('id', 64)->unique();
$table->string('from_username', 256);
$table->string('from_domain', 256);
$table->dateTime('sent_at');
$table->boolean('encrypted')->default(false);
$table->string('conference_id')->nullable();
$table->timestamps();
$table->index(['from_username', 'from_domain']);
$table->index('sent_at');
});
Schema::create('statistics_message_devices', function (Blueprint $table) {
$table->id();
$table->string('message_id', 64);
$table->string('to_username', 256);
$table->string('to_domain', 256);
$table->string('device_id', 64);
$table->integer('last_status');
$table->dateTime('received_at');
$table->timestamps();
$table->foreign('message_id')->references('id')->on('statistics_messages')->onDelete('cascade');
$table->unique(['message_id', 'to_username', 'to_domain', 'device_id'], 'statistics_message_devices_message_id_to_u_to_d_device_id_unique');
});
Schema::enableForeignKeyConstraints();
}
public function down()
{
Schema::disableForeignKeyConstraints();
Schema::dropIfExists('statistics_calls');
Schema::dropIfExists('statistics_call_devices');
StatisticsMessageDevice::truncate();
StatisticsMessage::truncate();
Schema::table('statistics_messages', function(Blueprint $table) {
$table->dropIndex('statistics_messages_from_username_from_domain_index');
$table->dropColumn('from_username');
$table->dropColumn('from_domain');
$table->string('from', 256)->index();
});
Schema::table('statistics_message_devices', function(Blueprint $table) {
$table->dropUnique('statistics_message_devices_message_id_to_u_to_d_device_id_unique');
$table->dropColumn('to_username');
$table->dropColumn('to_domain');
$table->string('to', 256);
$table->unique(['message_id', 'to', 'device_id']);
});
Schema::enableForeignKeyConstraints();
}
};

View file

@ -19,23 +19,32 @@
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\StatisticsCall;
use App\StatisticsCallDevice;
use App\StatisticsMessage;
use App\StatisticsMessageDevice;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Schema;
class StatisticsMessagesSeeder extends Seeder
class StatisticsSeeder extends Seeder
{
public function run()
{
Schema::disableForeignKeyConstraints();
StatisticsMessageDevice::truncate();
StatisticsMessage::truncate();
StatisticsCallDevice::truncate();
StatisticsCall::truncate();
Schema::enableForeignKeyConstraints();
StatisticsMessage::factory()
->count(10000)
->create();
StatisticsCall::factory()
->count(10000)
->create();
}
}

View file

@ -13,6 +13,7 @@
'items' => [
route('admin.statistics.show', ['type' => 'messages']) => 'Messages',
route('admin.statistics.show', ['type' => 'accounts']) => 'Accounts',
route('admin.statistics.show', ['type' => 'calls']) => 'Calls',
],
])

View file

@ -657,7 +657,7 @@ JSON parameters:
* `id` required, string
* `from` required, string the sender of the message
* `sent_at` required, string, format ISO8601, when the mesage was actually sent
* `sent_at` required, string, format ISO8601, when the message was actually sent
* `encrypted` required, boolean
* `conference_id` string
@ -672,6 +672,44 @@ JSON parameters:
* `last_status` required, an integer containing the last status code
* `received_at` required, format ISO8601, when the message was received
### `POST /statistics/calls`
<span class="badge badge-warning">Admin</span>
Announce the beginning of a call.
JSON parameters:
* `id` required, string
* `from` required, string the initier of the call
* `to` required, string the destination of the call
* `initiated_at` required, string, format ISO8601, when the call was started
* `ended_at` string, format ISO8601, when the call finished
* `conference_id` string
### `PATCH /statistics/calls/{call_id}/devices/{device_id}`
<span class="badge badge-warning">Admin</span>
Complete a call status.
JSON parameters:
* `rang_at` format ISO8601, when the device rang
* `invite_terminated`
* `at` format ISO8601, when the invitation ended
* `state` the termination state
### `PATCH /statistics/calls/{call_id}`
<span class="badge badge-warning">Admin</span>
Update a call when ending.
JSON parameters:
* `ended_at` required, string, format ISO8601, when the call finished
# Non-API Endpoints
The following URLs are **not API endpoints** they are not returning `JSON` content and they are not located under `/api` but directly under the root path.

View file

@ -23,6 +23,7 @@ use App\Http\Controllers\Api\Admin\AccountController as AdminAccountController;
use App\Http\Controllers\Api\Admin\AccountTypeController;
use App\Http\Controllers\Api\Admin\ContactsListController;
use App\Http\Controllers\Api\StatisticsMessageController;
use App\Http\Controllers\Api\StatisticsCallController;
use Illuminate\Http\Request;
Route::get('/', 'Api\ApiController@documentation')->name('api');
@ -126,5 +127,11 @@ Route::group(['middleware' => ['auth.digest_or_key']], function () {
Route::post('/', 'store');
Route::patch('{message_id}/to/{to}/devices/{device_id}', 'storeDevice');
});
Route::prefix('statistics/calls')->controller(StatisticsCallController::class)->group(function () {
Route::post('/', 'store');
Route::patch('{call_id}', 'update');
Route::patch('{call_id}/devices/{device_id}', 'storeDevice');
});
});
});

View file

@ -1,112 +0,0 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Tests\Feature;
use App\Admin;
use App\StatisticsMessageDevice;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class ApiStatisticsMessagesTest extends TestCase
{
use WithFaker, RefreshDatabase;
protected $route = '/api/statistics/messages';
public function testMessages()
{
$admin = Admin::factory()->create();
$admin->account->generateApiKey();
$id = '1234';
$this->keyAuthenticated($admin->account)
->json('POST', $this->route, [
'id' => $id,
'from' => $this->faker->email(),
'sent_at' => $this->faker->iso8601(),
'encrypted' => false
])
->assertStatus(200);
$this->assertDatabaseHas('statistics_messages', [
'id' => $id
]);
$this->keyAuthenticated($admin->account)
->json('POST', $this->route, [
'id' => $id,
'from' => $this->faker->email(),
'sent_at' => $this->faker->iso8601(),
'encrypted' => false
])
->assertStatus(400);
$this->keyAuthenticated($admin->account)
->json('POST', $this->route, [
'id' => $id,
'from' => $this->faker->email(),
'sent_at' => 'bad_date',
'encrypted' => false
])
->assertJsonValidationErrors(['sent_at']);
// Patch previous message with devices
$to = $this->faker->email();
$device = $this->faker->uuid();
$receivedAt = $this->faker->iso8601();
$lastStatus = 200;
$newReceivedAt = $this->faker->iso8601();
$newLastStatus = 201;
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->route . '/' . $id . '/to/' . $to . ' /devices/' . $device, [
'last_status' => $lastStatus,
'received_at' => $receivedAt
])
->assertStatus(201);
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->route . '/' . $id . '/to/' . $to . ' /devices/' . $device, [
'last_status' => $newLastStatus,
'received_at' => $newReceivedAt
])
->assertStatus(200);
$this->assertSame(1, StatisticsMessageDevice::count());
$this->assertDatabaseHas('statistics_message_devices', [
'message_id' => $id,
'last_status' => $newLastStatus
]);
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->route . '/' . $id . '/to/' . $this->faker->email() . ' /devices/' . $this->faker->uuid(), [
'last_status' => $newLastStatus,
'received_at' => $newReceivedAt
])
->assertStatus(201);
$this->assertSame(2, StatisticsMessageDevice::count());
}
}

View file

@ -0,0 +1,229 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Tests\Feature;
use App\Account;
use App\Admin;
use App\StatisticsCallDevice;
use App\StatisticsMessageDevice;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class ApiStatisticsTest extends TestCase
{
use WithFaker, RefreshDatabase;
protected $routeMessages = '/api/statistics/messages';
protected $routeCalls = '/api/statistics/calls';
public function testMessages()
{
$admin = Admin::factory()->create();
$admin->account->generateApiKey();
$id = '1234';
$fromUsername = 'username';
$fromDomain = 'domain.com';
$account = Account::factory()->create([
'username' => $fromUsername,
'domain' => $fromDomain,
]);
$this->keyAuthenticated($admin->account)
->json('POST', $this->routeMessages, [
'id' => $id,
'from' => $fromUsername . '@' . $fromDomain,
'sent_at' => $this->faker->iso8601(),
'encrypted' => false
])
->assertStatus(200);
$this->assertDatabaseHas('statistics_messages', [
'id' => $id
]);
$this->keyAuthenticated($admin->account)
->json('POST', $this->routeMessages, [
'id' => $id,
'from' => $this->faker->email(),
'sent_at' => $this->faker->iso8601(),
'encrypted' => false
])
->assertStatus(400);
$this->keyAuthenticated($admin->account)
->json('POST', $this->routeMessages, [
'id' => $id,
'from' => $this->faker->email(),
'sent_at' => 'bad_date',
'encrypted' => false
])
->assertJsonValidationErrors(['sent_at']);
// Patch previous message with devices
$to = $this->faker->email();
$device = $this->faker->uuid();
$receivedAt = $this->faker->iso8601();
$lastStatus = 200;
$newReceivedAt = $this->faker->iso8601();
$newLastStatus = 201;
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeMessages . '/' . $id . '/to/' . $to . ' /devices/' . $device, [
'last_status' => $lastStatus,
'received_at' => $receivedAt
])
->assertStatus(201);
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeMessages . '/' . $id . '/to/' . $to . ' /devices/' . $device, [
'last_status' => $newLastStatus,
'received_at' => $newReceivedAt
])
->assertStatus(200);
$this->assertSame(1, StatisticsMessageDevice::count());
$this->assertDatabaseHas('statistics_message_devices', [
'message_id' => $id,
'last_status' => $newLastStatus
]);
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeMessages . '/' . $id . '/to/' . $this->faker->email() . ' /devices/' . $this->faker->uuid(), [
'last_status' => $newLastStatus,
'received_at' => $newReceivedAt
])
->assertStatus(201);
$this->assertSame(2, StatisticsMessageDevice::count());
// Deletion event test
$account->delete();
$this->assertDatabaseMissing('statistics_messages', [
'id' => $id
]);
}
public function testCalls()
{
$admin = Admin::factory()->create();
$admin->account->generateApiKey();
$id = '1234';
$fromUsername = 'username';
$fromDomain = 'domain.com';
$toUsername = 'usernameto';
$toDomain = 'domainto.com';
$account = Account::factory()->create([
'username' => $fromUsername,
'domain' => $fromDomain,
]);
$this->keyAuthenticated($admin->account)
->json('POST', $this->routeCalls, [
'id' => $id,
'from' => $fromUsername . '@' . $fromDomain,
'to' => $toUsername . '@' . $toDomain,
'initiated_at' => $this->faker->iso8601(),
])
->assertStatus(200);
$this->assertDatabaseHas('statistics_calls', [
'id' => $id
]);
$this->keyAuthenticated($admin->account)
->json('POST', $this->routeCalls, [
'id' => $id,
'from' => $fromUsername . '@' . $fromDomain,
'to' => $toUsername . '@' . $toDomain,
'initiated_at' => $this->faker->iso8601(),
])
->assertStatus(400);
// Patch previous call with devices*
$to = $this->faker->email();
$device = $this->faker->uuid();
$rangAt = $this->faker->iso8601();
$newRangAt = $this->faker->iso8601();
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeCalls . '/' . $id . '/devices/' . $device, [
'rang_at' => $rangAt,
'invite_terminated' => [
'at' => $this->faker->iso8601(),
'state' => 'declined'
]
])
->assertStatus(201);
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeCalls . '/' . $id . '/devices/' . $device, [
'rang_at' => $newRangAt,
'invite_terminated' => [
'at' => $this->faker->iso8601(),
'state' => 'declined'
]
])
->assertStatus(200);
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeCalls . '/' . $id . '/devices/' . $device, [
'invite_terminated' => [
'state' => 'declined'
]
])
->assertStatus(422);
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeCalls . '/' . $id . '/devices/' . $device, [
'rang_at' => $this->faker->iso8601()
])
->assertStatus(200);
$this->assertSame(1, StatisticsCallDevice::count());
// Update
$endedAt = $this->faker->iso8601();
$this->keyAuthenticated($admin->account)
->json('PATCH', $this->routeCalls . '/' . $id, [
'ended_at' => $endedAt
])
->assertStatus(200);
// Deletion event test
$account->delete();
$this->assertDatabaseMissing('statistics_calls', [
'id' => $id
]);
}
}