MDL-85706 message: implement messages datasource for custom reporting.

Create new entities for report data on messages/conversations, joined
to the user entity, providing data for the reportbuilder editor.
This commit is contained in:
Paul Holden
2025-08-22 15:20:30 +01:00
parent e6ca00993a
commit 5094bb2f1f
5 changed files with 830 additions and 0 deletions
+3
View File
@@ -49,7 +49,9 @@ $string['contactblocked'] = 'Contact blocked';
$string['contactrequests'] = 'Contact requests';
$string['contactrequestsent'] = 'Contact request sent';
$string['contacts'] = 'Contacts';
$string['conversation'] = 'Conversation';
$string['conversationactions'] = 'Conversation actions menu';
$string['conversationtype'] = 'Type';
$string['decline'] = 'Decline';
$string['defaultmessageoutputs'] = 'Notification settings';
$string['deleteallconfirm'] = "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.";
@@ -214,6 +216,7 @@ $string['processorsettings'] = 'Processor settings';
$string['providerenabled'] = 'Sending "{$a}" enabled status';
$string['providerprocesorislocked'] = '"{$a->provider}" on "{$a->processor}" is locked on';
$string['providerprocesorisdisallowed'] = '"{$a->provider}" on "{$a->processor}" is locked off';
$string['recipient'] = 'Recipient';
$string['removecontact'] = 'Remove contact';
$string['removecontactconfirm'] = 'Are you sure you want to remove {$a} from your contacts?';
$string['removecoursefilter'] = 'Remove filter for course {$a}';
@@ -0,0 +1,149 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_message\reportbuilder\datasource;
use core\lang_string;
use core_message\reportbuilder\local\entities\{conversation, message};
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\filters\text;
use core_reportbuilder\local\helpers\database;
/**
* Messages datasource
*
* @package core_message
* @copyright 2025 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class messages extends datasource {
/**
* Return user friendly name of the datasource
*
* @return string
*/
public static function get_name(): string {
return get_string('messages', 'core_message');
}
/**
* Initialise report
*/
protected function initialise(): void {
// Message.
$messageentity = new message();
$messagealias = $messageentity->get_table_alias('messages');
$this->set_main_table('messages', $messagealias);
$this->add_entity($messageentity);
// Conversation.
$conversationentity = new conversation();
$conversationalias = $conversationentity->get_table_alias('message_conversations');
$this->add_entity($conversationentity
->add_join("LEFT JOIN {message_conversations} {$conversationalias}
ON {$conversationalias}.id = {$messagealias}.conversationid"));
// Author.
$authorentity = new user();
$authoralias = $authorentity->get_table_alias('user');
$this->add_entity($authorentity
->add_join("LEFT JOIN {user} {$authoralias} ON {$authoralias}.id = {$messagealias}.useridfrom"));
// Recipient.
$recipiententity = new user();
$recipientalias = $recipiententity->get_table_alias('user');
$membersalias = database::generate_alias();
$this->add_entity($recipiententity
->set_entity_name('recipient')
->set_entity_title(new lang_string('recipient', 'core_message'))
->add_joins($conversationentity->get_joins())
->add_joins([
"LEFT JOIN {message_conversation_members} {$membersalias}
ON {$membersalias}.conversationid = {$conversationalias}.id
AND {$membersalias}.userid != {$messagealias}.useridfrom",
"LEFT JOIN {user} {$recipientalias} ON {$recipientalias}.id = {$membersalias}.userid",
]));
// Add all columns/filters/conditions from entities to be available in custom reports.
$this->add_all_from_entities();
}
/**
* Return the columns that will be added to the report as part of default setup
*
* @return string[]
*/
public function get_default_columns(): array {
return [
'user:fullname',
'recipient:fullname',
'message:message',
'message:timecreated',
];
}
/**
* Return the default sorting that will be added to the report upon creation
*
* @return int[]
*/
public function get_default_column_sorting(): array {
return [
'user:fullname' => SORT_ASC,
'recipient:fullname' => SORT_ASC,
'message:timecreated' => SORT_ASC,
];
}
/**
* Return the filters that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_filters(): array {
return [
'user:fullname',
'recipient:fullname',
'message:message',
];
}
/**
* Return the conditions that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_conditions(): array {
return [
'message:message',
];
}
/**
* Return the condition values that will be set for the report upon creation
*
* @return array
*/
public function get_default_condition_values(): array {
return [
'message:message_operator' => text::IS_NOT_EMPTY,
];
}
}
@@ -0,0 +1,205 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_message\reportbuilder\local\entities;
use core\lang_string;
use core_message\api;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
/**
* Conversation entity
*
* @package core_message
* @copyright 2025 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class conversation extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'message_conversations',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('conversation', 'core_message');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
$conversationalias = $this->get_table_alias('message_conversations');
// Type.
$columns[] = (new column(
'type',
new lang_string('conversationtype', 'core_message'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$conversationalias}.type")
->set_is_sortable(true)
->add_callback(static function (?string $type): string {
$types = [
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new lang_string('individualconversations', 'core_message'),
api::MESSAGE_CONVERSATION_TYPE_GROUP => new lang_string('groupconversations', 'core_message'),
api::MESSAGE_CONVERSATION_TYPE_SELF => new lang_string('selfconversation', 'core_message'),
];
if ($type === null || !array_key_exists($type, $types)) {
return '';
}
return (string) $types[$type];
});
// Name.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$conversationalias}.name")
->set_is_sortable(true)
->add_callback(static function (?string $name): string {
if ($name === null) {
return '';
}
return format_string($name);
});
// Enabled.
$columns[] = (new column(
'enabled',
new lang_string('enabled', 'core_message'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
->add_field("{$conversationalias}.enabled")
->set_is_sortable(true)
->add_callback([format::class, 'boolean_as_text']);
// Time created.
$columns[] = (new column(
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$conversationalias}.timecreated")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$conversationalias = $this->get_table_alias('message_conversations');
// Type.
$filters[] = (new filter(
select::class,
'type',
new lang_string('conversationtype', 'core_message'),
$this->get_entity_name(),
"{$conversationalias}.type",
))
->add_joins($this->get_joins())
->set_options([
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new lang_string('individualconversations', 'core_message'),
api::MESSAGE_CONVERSATION_TYPE_GROUP => new lang_string('groupconversations', 'core_message'),
api::MESSAGE_CONVERSATION_TYPE_SELF => new lang_string('selfconversation', 'core_message'),
]);
// Name.
$filters[] = (new filter(
text::class,
'name',
new lang_string('name'),
$this->get_entity_name(),
"{$conversationalias}.name",
))
->add_joins($this->get_joins());
// Enabled.
$filters[] = (new filter(
boolean_select::class,
'enabled',
new lang_string('enabled', 'core_message'),
$this->get_entity_name(),
"{$conversationalias}.enabled",
))
->add_joins($this->get_joins());
// Time created.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$conversationalias}.timecreated",
))
->add_joins($this->get_joins());
return $filters;
}
}
@@ -0,0 +1,173 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_message\reportbuilder\local\entities;
use core\lang_string;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{date, text};
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
use stdClass;
/**
* Message entity
*
* @package core_message
* @copyright 2025 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class message extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'messages',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('message', 'core_message');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
$messagealias = $this->get_table_alias('messages');
// Subject.
$columns[] = (new column(
'subject',
new lang_string('subject'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$messagealias}.subject")
->set_is_sortable(true)
->add_callback(static function (?string $subject): string {
if ($subject === null) {
return '';
}
return format_string($subject);
});
// Message.
$columns[] = (new column(
'message',
new lang_string('message', 'core_message'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_fields("{$messagealias}.fullmessage, {$messagealias}.fullmessageformat, {$messagealias}.fullmessagetrust")
->set_is_sortable(true)
->add_callback(static function (?string $fullmessage, stdClass $message): string {
if ($fullmessage === null) {
return '';
}
return format_text($fullmessage, $message->fullmessageformat, ['trusted' => $message->fullmessagetrust]);
});
// Time created.
$columns[] = (new column(
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$messagealias}.timecreated")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$messagealias = $this->get_table_alias('messages');
// Subject.
$filters[] = (new filter(
text::class,
'subject',
new lang_string('subject'),
$this->get_entity_name(),
"{$messagealias}.subject",
))
->add_joins($this->get_joins());
// Message.
$filters[] = (new filter(
text::class,
'message',
new lang_string('message', 'core_message'),
$this->get_entity_name(),
"{$messagealias}.fullmessage",
))
->add_joins($this->get_joins());
// Time created.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$messagealias}.timecreated",
))
->add_joins($this->get_joins());
return $filters;
}
}
@@ -0,0 +1,300 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_message\reportbuilder\datasource;
use core\clock;
use core_message\api;
use core_message\tests\helper;
use core_reportbuilder_generator;
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
use core_reportbuilder\tests\core_reportbuilder_testcase;
/**
* Unit tests for messages datasource
*
* @package core_message
* @covers \core_message\reportbuilder\datasource\messages
* @copyright 2025 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class messages_test extends core_reportbuilder_testcase {
/** @var clock $clock */
private readonly clock $clock;
/**
* Mock the clock
*/
protected function setUp(): void {
parent::setUp();
$this->clock = $this->mock_clock_with_frozen(1622502000);
}
/**
* Test default datasource
*/
public function test_datasource_default(): void {
$this->resetAfterTest();
// Test subject.
$course = $this->getDataGenerator()->create_course();
$userone = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe', 'lastname' => 'Zebra']);
$usertwo = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Morris', 'lastname' => 'Moo']);
$userthree = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Aaron', 'lastname' => 'Ant']);
$groupconversation = api::create_conversation(
api::MESSAGE_CONVERSATION_TYPE_GROUP,
[$userone->id, $usertwo->id, $userthree->id],
);
helper::send_fake_message_to_conversation($userone, $groupconversation->id, 'Are you somewhere feeling lonely?');
$this->clock->bump(HOURSECS);
$privateconversation = api::create_conversation(
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$userone->id, $usertwo->id],
);
helper::send_fake_message_to_conversation($usertwo, $privateconversation->id, 'Or is someone loving you?');
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 1]);
$content = $this->get_custom_report_content($report->get('id'));
// Default columns are author, recipient, message, time created. Sorted by author, recipient, time created.
$this->assertEquals([
[
fullname($usertwo),
fullname($userone),
'<div class="text_to_html">Or is someone loving you?</div>',
'Tuesday, 1 June 2021, 8:00 AM',
],
[
fullname($userone),
fullname($userthree),
'<div class="text_to_html">Are you somewhere feeling lonely?</div>',
'Tuesday, 1 June 2021, 7:00 AM',
],
[
fullname($userone),
fullname($usertwo),
'<div class="text_to_html">Are you somewhere feeling lonely?</div>',
'Tuesday, 1 June 2021, 7:00 AM',
],
], array_map('array_values', $content));
}
/**
* Test datasource columns that aren't added by default
*/
public function test_datasource_non_default_columns(): void {
$this->resetAfterTest();
// Test subject.
$userone = $this->getDataGenerator()->create_user();
$usertwo = $this->getDataGenerator()->create_user();
$conversation = api::create_conversation(
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$userone->id, $usertwo->id],
'My conversation',
);
helper::send_fake_message_to_conversation($userone, $conversation->id);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 0]);
// Message.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'message:subject']);
// Conversation.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:type']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:name']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:enabled']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:timecreated']);
$content = $this->get_custom_report_content($report->get('id'));
$this->assertEquals([
[
'No subject',
'Private',
'My conversation',
'Yes',
'Tuesday, 1 June 2021, 7:00 AM',
],
], array_map('array_values', $content));
}
/**
* Data provider for {@see test_datasource_filters}
*
* @return array[]
*/
public static function datasource_filters_provider(): array {
return [
// Message.
'Message subject' => ['message:subject', [
'message:subject_operator' => text::IS_EQUAL_TO,
'message:subject_value' => 'No subject',
], true],
'Message subject (no match)' => ['message:subject', [
'message:subject_operator' => text::IS_EQUAL_TO,
'message:subject_value' => 'Another subject',
], false],
'Message content' => ['message:message', [
'message:message_operator' => text::IS_EQUAL_TO,
'message:message_value' => 'Hi',
], true],
'Message content (no match)' => ['message:message', [
'message:message_operator' => text::IS_EQUAL_TO,
'message:message_value' => 'Bye',
], false],
'Message time created' => ['message:timecreated', [
'message:timecreated_operator' => date::DATE_RANGE,
'message:timecreated_from' => 1622502000,
], true],
'Message time created (no match)' => ['message:timecreated', [
'message:timecreated_operator' => date::DATE_RANGE,
'message:timecreated_to' => 1622502000,
], false],
// Conversation.
'Conversation type' => ['conversation:type', [
'conversation:type_operator' => select::EQUAL_TO,
'conversation:type_value' => api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
], true],
'Conversation type (no match)' => ['conversation:type', [
'conversation:type_operator' => select::EQUAL_TO,
'conversation:type_value' => api::MESSAGE_CONVERSATION_TYPE_GROUP,
], false],
'Conversation name' => ['conversation:name', [
'conversation:name_operator' => text::IS_EQUAL_TO,
'conversation:name_value' => 'My conversation',
], true],
'Conversation name (no match)' => ['conversation:name', [
'conversation:name_operator' => text::IS_EQUAL_TO,
'conversation:name_value' => 'Another conversation',
], false],
'Conversation enabled' => ['conversation:enabled', [
'conversation:enabled_operator' => boolean_select::CHECKED,
], true],
'Conversation enabled (no match)' => ['conversation:enabled', [
'conversation:enabled_operator' => boolean_select::NOT_CHECKED,
], false],
'Conversation time created' => ['conversation:timecreated', [
'conversation:timecreated_operator' => date::DATE_RANGE,
'conversation:timecreated_from' => 1622502000,
], true],
'Conversation time created (no match)' => ['conversation:timecreated', [
'conversation:timecreated_operator' => date::DATE_RANGE,
'conversation:timecreated_to' => 1622502000,
], false],
// Author.
'User firstname' => ['user:firstname', [
'user:firstname_operator' => text::IS_EQUAL_TO,
'user:firstname_value' => 'Zoe',
], true],
'User firstname (no match)' => ['user:firstname', [
'user:firstname_operator' => text::IS_EQUAL_TO,
'user:firstname_value' => 'Aaron',
], false],
// Recipient.
'Recipient firstname' => ['recipient:firstname', [
'recipient:firstname_operator' => text::IS_EQUAL_TO,
'recipient:firstname_value' => 'Aaron',
], true],
'Recipient firstname (no match)' => ['recipient:firstname', [
'recipient:firstname_operator' => text::IS_EQUAL_TO,
'recipient:firstname_value' => 'Zoe',
], false],
];
}
/**
* Test datasource filters
*
* @param string $filtername
* @param array $filtervalues
* @param bool $expectmatch
*
* @dataProvider datasource_filters_provider
*/
public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void {
$this->resetAfterTest();
// Test subject.
$userone = $this->getDataGenerator()->create_user(['firstname' => 'Zoe']);
$usertwo = $this->getDataGenerator()->create_user(['firstname' => 'Aaron']);
$conversation = api::create_conversation(
api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
[$userone->id, $usertwo->id],
'My conversation',
);
helper::send_fake_message_to_conversation($userone, $conversation->id, 'Hi');
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
// Create report containing single column, and given filter.
$report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 0]);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'message:subject']);
// Add filter, set it's values.
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
$content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
if ($expectmatch) {
$this->assertEquals([
['No subject'],
], array_map('array_values', $content));
} else {
$this->assertEmpty($content);
}
}
/**
* Stress test datasource
*
* In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
*/
public function test_stress_datasource(): void {
if (!PHPUNIT_LONGTEST) {
$this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
}
$this->resetAfterTest();
// Test subject.
$userone = $this->getDataGenerator()->create_user();
$usertwo = $this->getDataGenerator()->create_user();
$conversation = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [$userone->id, $usertwo->id]);
helper::send_fake_message_to_conversation($userone, $conversation->id);
$this->datasource_stress_test_columns(messages::class);
$this->datasource_stress_test_columns_aggregation(messages::class);
$this->datasource_stress_test_conditions(messages::class, 'message:subject');
}
}