1
0
Fork 0
mirror of synced 2024-07-02 05:00:33 +12:00

Merge pull request #7589 from appwrite/feat-topic-totals-per-type

Feat topic totals per type
This commit is contained in:
Jake Barnby 2024-02-16 17:55:30 +13:00 committed by GitHub
commit 81532a026c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 160 additions and 17 deletions

View file

@ -1903,7 +1903,29 @@ $commonCollections = [
'filters' => [],
],
[
'$id' => ID::custom('total'),
'$id' => ID::custom('emailTotal'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => 0,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('smsTotal'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => 0,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('pushTotal'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,

View file

@ -872,7 +872,7 @@ return [
],
Exception::MESSAGE_MISSING_TARGET => [
'name' => Exception::MESSAGE_MISSING_TARGET,
'description' => 'Message with the requested ID is missing a target (Topics or Users or Targets).',
'description' => 'Message with the requested ID has no recipients (topics or users or targets).',
'code' => 400,
],
Exception::MESSAGE_ALREADY_SENT => [
@ -920,4 +920,11 @@ return [
'description' => 'Schedule with the requested ID could not be found.',
'code' => 404,
],
/** Targets */
Exception::TARGET_PROVIDER_INVALID_TYPE => [
'name' => Exception::TARGET_PROVIDER_INVALID_TYPE,
'description' => 'Target has an invalid provider type.',
'code' => 400,
],
];

View file

@ -163,6 +163,11 @@ App::post('/v1/account')
$user = Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn() => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => MESSAGE_TYPE_EMAIL,
@ -707,7 +712,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$userDoc = Authorization::skip(fn() => $dbForProject->createDocument('users', $user));
$dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::any()),
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
@ -1699,6 +1704,11 @@ App::post('/v1/account/tokens/phone')
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn() => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => MESSAGE_TYPE_SMS,

View file

@ -2260,7 +2260,19 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
try {
$subscriber = $dbForProject->createDocument('subscribers', $subscriber);
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('topics', $topicId, 'total', 1));
$totalAttribute = match ($target->getAttribute('providerType')) {
MESSAGE_TYPE_EMAIL => 'emailTotal',
MESSAGE_TYPE_SMS => 'smsTotal',
MESSAGE_TYPE_PUSH => 'pushTotal',
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
));
} catch (DuplicateException) {
throw new Exception(Exception::SUBSCRIBER_ALREADY_EXISTS);
}
@ -2311,7 +2323,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
throw new Exception(Exception::TOPIC_NOT_FOUND);
}
\array_push($queries, Query::equal('topicInternalId', [$topic->getInternalId()]));
$queries[] = Query::equal('topicInternalId', [$topic->getInternalId()]);
/**
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
@ -2512,8 +2524,23 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
throw new Exception(Exception::SUBSCRIBER_NOT_FOUND);
}
$target = $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'));
$dbForProject->deleteDocument('subscribers', $subscriberId);
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('topics', $topicId, 'total', 1));
$totalAttribute = match ($target->getAttribute('providerType')) {
MESSAGE_TYPE_EMAIL => 'emailTotal',
MESSAGE_TYPE_SMS => 'smsTotal',
MESSAGE_TYPE_PUSH => 'pushTotal',
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
min: 0
));
$queueForEvents
->setParam('topicId', $topic->getId())

View file

@ -115,6 +115,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
if ($email) {
try {
$target = $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => 'email',
@ -132,6 +137,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
if ($phone) {
try {
$target = $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => 'sms',
@ -498,6 +508,11 @@ App::post('/v1/users/:userId/targets')
try {
$target = $dbForProject->createDocument('targets', new Document([
'$id' => $targetId,
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'providerId' => $providerId ?? null,
'providerInternalId' => $provider->getInternalId() ?? null,
'providerType' => $providerType,
@ -1227,6 +1242,11 @@ App::patch('/v1/users/:userId/email')
} else {
if (\strlen($email) !== 0) {
$target = $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => 'email',
@ -1305,6 +1325,11 @@ App::patch('/v1/users/:userId/phone')
} else {
if (\strlen($number) !== 0) {
$target = $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => 'sms',

View file

@ -252,6 +252,7 @@ class Exception extends \Exception
public const PROVIDER_NOT_FOUND = 'provider_not_found';
public const PROVIDER_ALREADY_EXISTS = 'provider_already_exists';
public const PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type';
public const PROVIDER_MISSING_CREDENTIALS = 'provider_missing_credentials';
/** Topic */
@ -274,6 +275,9 @@ class Exception extends \Exception
public const MESSAGE_TARGET_NOT_PUSH = 'message_target_not_push';
public const MESSAGE_MISSING_SCHEDULE = 'message_missing_schedule';
/** Targets */
public const TARGET_PROVIDER_INVALID_TYPE = 'target_provider_invalid_type';
/** Schedules */
public const SCHEDULE_NOT_FOUND = 'schedule_not_found';

View file

@ -3,6 +3,7 @@
namespace Appwrite\Platform\Workers;
use Appwrite\Auth\Auth;
use Appwrite\Extend\Exception;
use Executor\Executor;
use Throwable;
use Utopia\Abuse\Abuse;
@ -263,12 +264,23 @@ class Deletes extends Action
Query::equal('targetInternalId', [$target->getInternalId()])
],
$dbForProject,
function (Document $subscriber) use ($dbForProject) {
function (Document $subscriber) use ($dbForProject, $target) {
$topicId = $subscriber->getAttribute('topicId');
$topicInternalId = $subscriber->getAttribute('topicInternalId');
$topic = $dbForProject->getDocument('topics', $topicId);
if (!$topic->isEmpty() && $topic->getInternalId() === $topicInternalId) {
$dbForProject->decreaseDocumentAttribute('topics', $topicId, 'total', min: 0);
$totalAttribute = match ($target->getAttribute('providerType')) {
MESSAGE_TYPE_EMAIL => 'emailTotal',
MESSAGE_TYPE_SMS => 'smsTotal',
MESSAGE_TYPE_PUSH => 'pushTotal',
default => throw new Exception('Invalid target provider type'),
};
$dbForProject->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
min: 0
);
}
}
);

View file

@ -249,7 +249,7 @@ class Messaging extends Action
}
// Deleting push targets when token has expired.
if (($result['error'] ?? '') === 'Expired device token.') {
if (($result['error'] ?? '') === 'Expired device token') {
$target = $dbForProject->findOne('targets', [
Query::equal('identifier', [$result['recipient']])
]);

View file

@ -7,7 +7,9 @@ class Topics extends Base
public const ALLOWED_ATTRIBUTES = [
'name',
'description',
'total'
'emailTotal',
'smsTotal',
'pushTotal',
];
/**

View file

@ -34,9 +34,21 @@ class Topic extends Model
'default' => '',
'example' => 'events',
])
->addRule('total', [
->addRule('emailTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total count of subscribers subscribed to topic.',
'description' => 'Total count of email subscribers subscribed to the topic.',
'default' => 0,
'example' => 100,
])
->addRule('smsTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total count of SMS subscribers subscribed to the topic.',
'default' => 0,
'example' => 100,
])
->addRule('pushTotal', [
'type' => self::TYPE_INTEGER,
'description' => 'Total count of push subscribers subscribed to the topic.',
'default' => 0,
'example' => 100,
])

View file

@ -2026,6 +2026,9 @@ trait Base
messagingCreateTopic(topicId: $topicId, name: $name) {
_id
name
emailTotal
smsTotal
pushTotal
}
}';
case self::$LIST_TOPICS:
@ -2035,6 +2038,9 @@ trait Base
topics {
_id
name
emailTotal
smsTotal
pushTotal
}
}
}';
@ -2043,6 +2049,9 @@ trait Base
messagingGetTopic(topicId: $topicId) {
_id
name
emailTotal
smsTotal
pushTotal
}
}';
case self::$UPDATE_TOPIC:
@ -2050,6 +2059,9 @@ trait Base
messagingUpdateTopic(topicId: $topicId, name: $name) {
_id
name
emailTotal
smsTotal
pushTotal
}
}';
case self::$DELETE_TOPIC:

View file

@ -355,7 +355,9 @@ trait MessagingBase
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => [
Query::equal('total', [0])->toString(),
Query::equal('emailTotal', [0])->toString(),
Query::equal('smsTotal', [0])->toString(),
Query::equal('pushTotal', [0])->toString(),
],
]);
@ -368,7 +370,9 @@ trait MessagingBase
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'queries' => [
Query::greaterThan('total', 0)->toString(),
Query::greaterThan('emailTotal', 0)->toString(),
Query::greaterThan('smsTotal', 0)->toString(),
Query::greaterThan('pushTotal', 0)->toString(),
],
]);
@ -390,7 +394,9 @@ trait MessagingBase
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('android-app', $response['body']['name']);
$this->assertEquals(0, $response['body']['total']);
$this->assertEquals(0, $response['body']['emailTotal']);
$this->assertEquals(0, $response['body']['smsTotal']);
$this->assertEquals(0, $response['body']['pushTotal']);
}
/**
@ -446,7 +452,9 @@ trait MessagingBase
$this->assertEquals(200, $topic['headers']['status-code']);
$this->assertEquals('android-app', $topic['body']['name']);
$this->assertEquals(1, $topic['body']['total']);
$this->assertEquals(1, $topic['body']['emailTotal']);
$this->assertEquals(0, $topic['body']['smsTotal']);
$this->assertEquals(0, $topic['body']['pushTotal']);
$response2 = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topics['private']['$id'] . '/subscribers', \array_merge([
'content-type' => 'application/json',
@ -695,7 +703,9 @@ trait MessagingBase
$this->assertEquals(200, $topic['headers']['status-code']);
$this->assertEquals('android-app', $topic['body']['name']);
$this->assertEquals(0, $topic['body']['total']);
$this->assertEquals(0, $topic['body']['emailTotal']);
$this->assertEquals(0, $topic['body']['smsTotal']);
$this->assertEquals(0, $topic['body']['pushTotal']);
}
/**