diff --git a/app/config/collections.php b/app/config/collections.php index aa6afffb17..f2ee405c92 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1845,7 +1845,7 @@ $commonCollections = [ 'filters' => [], ], [ - '$id' => ID::custom('providerId'), + '$id' => ID::custom('providerType'), 'type' => Database::VAR_STRING, 'format' => '', 'size' => Database::LENGTH_KEY, @@ -1855,13 +1855,24 @@ $commonCollections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => ID::custom('providerId'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], [ '$id' => ID::custom('providerInternalId'), 'type' => Database::VAR_STRING, 'format' => '', 'size' => Database::LENGTH_KEY, 'signed' => true, - 'required' => true, + 'required' => false, 'default' => null, 'array' => false, 'filters' => [], @@ -1909,18 +1920,11 @@ $commonCollections = [ ], [ '$id' => ID::custom('_key_identifier'), - 'type' => Database::INDEX_KEY, + 'type' => Database::INDEX_UNIQUE, 'attributes' => ['identifier'], 'lengths' => [], 'orders' => [], ], - [ - '$id' => ID::custom('_key_identifier_providerId'), - 'type' => Database::INDEX_UNIQUE, - 'attributes' => ['providerId', 'identifier'], - 'lengths' => [], - 'orders' => [], - ] ], ], ]; diff --git a/app/config/errors.php b/app/config/errors.php index 1117f20e48..e915091940 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -781,6 +781,11 @@ return [ 'description' => 'Provider with the requested ID is of incorrect type: ', 'code' => 400, ], + Exception::PROVIDER_INTERNAL_UPDATE_DISABLED => [ + 'name' => Exception::PROVIDER_INTERNAL_UPDATE_DISABLED, + 'description' => 'Provider with the requested ID cannot be disabled.', + 'code' => 400, + ], /** Topic Errors */ Exception::TOPIC_NOT_FOUND => [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 96c0483cca..1d6780f48b 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -148,7 +148,20 @@ App::post('/v1/account') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); + $target = Authorization::skip(fn() => $dbForProject->createDocument('targets', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ], + 'userId' => $user->getId(), + 'userInternalId' => $user->getInternalId(), + 'providerType' => 'email', + 'identifier' => $email, + ]))); + $user->setAttribute('targets', [$target]); + $dbForProject->deleteCachedDocument('users', $user->getId()); } catch (Duplicate) { throw new Exception(Exception::USER_ALREADY_EXISTS); } @@ -656,7 +669,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); + $userDoc = Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); + $dbForProject->createDocument('targets', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::user($user->getId())), + Permission::delete(Role::user($user->getId())), + ], + 'userId' => $userDoc->getId(), + 'userInternalId' => $userDoc->getInternalId(), + 'providerType' => 'email', + 'identifier' => $email, + ])); } catch (Duplicate) { $failureRedirect(Exception::USER_ALREADY_EXISTS); } @@ -1240,6 +1264,7 @@ App::post('/v1/account/sessions/phone') Query::equal('internal', [true]), Query::equal('type', ['sms']) ])); + if ($provider === false || $provider->isEmpty()) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -1328,17 +1353,21 @@ App::post('/v1/account/sessions/phone') $target = $dbForProject->findOne('targets', [ Query::equal('identifier', [$phone]), - Query::equal('providerInternalId', [$provider->getInternalId()]) ]); - if (!$target) { + if (!$target || $target->isEmpty()) { $target = $dbForProject->createDocument('targets', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::user($user->getId())), + Permission::delete(Role::user($user->getId())), + ], 'userId' => $user->getId(), 'userInternalId' => $user->getInternalId(), - 'providerId' => $provider->getId(), - 'providerInternalId' => $provider->getInternalId(), + 'providerType' => 'sms', 'identifier' => $phone, ])); + $dbForProject->deleteCachedDocument('users', $user->getId()); } $messageDoc = $dbForProject->createDocument('messages', new Document([ @@ -1995,6 +2024,7 @@ App::patch('/v1/account/email') throw new Exception(Exception::USER_INVALID_CREDENTIALS); } + $oldEmail = $user->getAttribute('email'); $email = \strtolower($email); // Makes sure this email is not already used in another identity @@ -2019,6 +2049,23 @@ App::patch('/v1/account/email') ->setAttribute('passwordUpdate', DateTime::now()); } + $target = $dbForProject->findOne('targets', [ + Query::equal('identifier', [$email]), + ]); + + if ($target && !$target->isEmpty()) { + throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); + } + + /** + * @var Document $oldTarget + */ + $oldTarget = $user->find('identifier', $oldEmail, 'targets'); + + if ($oldTarget !== false && !$oldTarget->isEmpty()) { + $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)); + } + try { $user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user)); } catch (Duplicate) { @@ -2065,6 +2112,19 @@ App::patch('/v1/account/phone') throw new Exception(Exception::USER_INVALID_CREDENTIALS); } + $target = $dbForProject->findOne('targets', [ + Query::equal('identifier', [$phone]), + ]); + + if ($target && !$target->isEmpty()) { + throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); + } + + /** + * @var Document $oldTarget + */ + $oldTarget = $user->find('identifier', $user->getAttribute('phone'), 'targets'); + $user ->setAttribute('phone', $phone) ->setAttribute('phoneVerification', false) // After this user needs to confirm phone number again @@ -2078,6 +2138,10 @@ App::patch('/v1/account/phone') ->setAttribute('passwordUpdate', DateTime::now()); } + if ($oldTarget !== false && !$oldTarget->isEmpty()) { + $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)); + } + try { $user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user)); } catch (Duplicate $th) { @@ -2904,6 +2968,7 @@ App::post('/v1/account/verification/phone') Query::equal('internal', [true]), Query::equal('type', ['sms']) ])); + if ($provider === false || $provider->isEmpty()) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -2952,17 +3017,21 @@ App::post('/v1/account/verification/phone') $target = $dbForProject->findOne('targets', [ Query::equal('identifier', [$user->getAttribute('phone')]), - Query::equal('providerInternalId', [$provider->getInternalId()]) ]); - if (!$target) { + if (!$target || $target->isEmpty()) { $target = $dbForProject->createDocument('targets', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::user($user->getId())), + Permission::delete(Role::user($user->getId())), + ], 'userId' => $user->getId(), 'userInternalId' => $user->getInternalId(), - 'providerId' => $provider->getId(), - 'providerInternalId' => $provider->getInternalId(), + 'providerType' => 'sms', 'identifier' => $user->getAttribute('phone'), ])); + $dbForProject->deleteCachedDocument('users', $user->getId()); } $messageDoc = $dbForProject->createDocument('messages', new Document([ diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 2cc9639fbd..1f4f7b62b6 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -806,14 +806,17 @@ App::patch('/v1/messaging/providers/mailgun/:providerId') ]); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + $credentials = $provider->getAttribute('credentials'); if ($isEuRegion === true || $isEuRegion === false) { @@ -893,14 +896,17 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId') ]); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + if (!empty($apiKey)) { $provider->setAttribute('credentials', [ 'apiKey' => $apiKey, @@ -971,14 +977,17 @@ App::patch('/v1/messaging/providers/msg91/:providerId') ]); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + $credentials = $provider->getAttribute('credentials'); if (!empty($senderId)) { @@ -1055,14 +1064,17 @@ App::patch('/v1/messaging/providers/telesign/:providerId') ]); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + $credentials = $provider->getAttribute('credentials'); if (!empty($username)) { @@ -1139,14 +1151,17 @@ App::patch('/v1/messaging/providers/textmagic/:providerId') ]); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + $credentials = $provider->getAttribute('credentials'); if (!empty($username)) { @@ -1223,14 +1238,17 @@ App::patch('/v1/messaging/providers/twilio/:providerId') ]); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + $credentials = $provider->getAttribute('credentials'); if (!empty($accountSid)) { @@ -1307,14 +1325,17 @@ App::patch('/v1/messaging/providers/vonage/:providerId') ]); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + $credentials = $provider->getAttribute('credentials'); if (!empty($apiKey)) { @@ -1383,14 +1404,17 @@ App::patch('/v1/messaging/providers/fcm/:providerId') $provider->setAttribute('name', $name); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + if (!empty($serverKey)) { $provider->setAttribute('credentials', ['serverKey' => $serverKey]); } @@ -1456,14 +1480,17 @@ App::patch('/v1/messaging/providers/apns/:providerId') $provider->setAttribute('name', $name); } - if ($enabled === true || $enabled === false) { - $provider->setAttribute('enabled', $enabled); - } - if ($internal === true) { $provider->setAttribute('internal', $internal); } + if ($enabled === true || $enabled === false) { + if ($provider->getAttribute('internal') === true && $enabled === false) { + throw new Exception(Exception::PROVIDER_INTERNAL_UPDATE_DISABLED); + } + $provider->setAttribute('enabled', $enabled); + } + $credentials = $provider->getAttribute('credentials'); if (!empty($authKey)) { diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index a4ad61733f..b19080e894 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -650,8 +650,7 @@ App::post('/v1/teams/:teamId/memberships') $target = $dbForProject->createDocument('targets', new Document([ 'userId' => $invitee->getId(), 'userInternalId' => $invitee->getInternalId(), - 'providerId' => $provider->getId(), - 'providerInternalId' => $provider->getInternalId(), + 'providerType' => 'sms', 'identifier' => $phone, ])); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 71deded08f..d9968c83a7 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -394,18 +394,25 @@ App::post('/v1/users/:userId/targets') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TARGET) + ->param('targetId', '', new CustomId(), 'Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('userId', '', new UID(), 'User ID.') - ->param('targetId', '', new UID(), 'Target ID.') - ->param('providerId', '', new UID(), 'Provider ID.') + ->param('providerType', '', new WhiteList(['email', 'sms', 'push']), 'The target provider type. Can be one of the following: `email`, `sms` or `push`.') ->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)') + ->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true) ->inject('queueForEvents') ->inject('response') ->inject('dbForProject') - ->action(function (string $userId, string $targetId, string $providerId, string $identifier, Event $queueForEvents, Response $response, Database $dbForProject) { - $provider = $dbForProject->getDocument('providers', $providerId); + ->action(function (string $targetId, string $userId, string $providerType, string $identifier, string $providerId, Event $queueForEvents, Response $response, Database $dbForProject) { + $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; - if ($provider->isEmpty()) { - throw new Exception(Exception::PROVIDER_NOT_FOUND); + $provider = new Document(); + + if ($providerType === 'push') { + $provider = $dbForProject->getDocument('providers', $providerId); + + if ($provider->isEmpty()) { + throw new Exception(Exception::PROVIDER_NOT_FOUND); + } } $user = $dbForProject->getDocument('users', $userId); @@ -423,8 +430,9 @@ App::post('/v1/users/:userId/targets') try { $target = $dbForProject->createDocument('targets', new Document([ '$id' => $targetId, - 'providerId' => $providerId, - 'providerInternalId' => $provider->getInternalId(), + 'providerId' => $providerId ?? null, + 'providerInternalId' => $provider->getInternalId() ?? null, + 'providerType' => $providerType, 'userId' => $userId, 'userInternalId' => $user->getInternalId(), 'identifier' => $identifier, @@ -1223,8 +1231,8 @@ App::patch('/v1/users/:userId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -App::patch('/v1/users/:userId/targets/:targetId/identifier') - ->desc('Update user target\'s identifier') +App::patch('/v1/users/:userId/targets/:targetId') + ->desc('Update User target') ->groups(['api', 'users']) ->label('audits.event', 'target.update') ->label('audits.resource', 'target/{response.$id}') @@ -1232,19 +1240,19 @@ App::patch('/v1/users/:userId/targets/:targetId/identifier') ->label('scope', 'targets.write') ->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'users') - ->label('sdk.method', 'updateTargetIdentifier') - ->label('sdk.description', '/docs/references/users/update-target-identifier.md') + ->label('sdk.method', 'updateTarget') + ->label('sdk.description', '/docs/references/users/update-target.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TARGET) ->param('userId', '', new UID(), 'User ID.') ->param('targetId', '', new UID(), 'Target ID.') - ->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)') + ->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true) + ->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)', true) ->inject('queueForEvents') ->inject('response') ->inject('dbForProject') - ->action(function (string $userId, string $targetId, string $identifier, Event $queueForEvents, Response $response, Database $dbForProject) { - + ->action(function (string $userId, string $targetId, string $providerId, string $identifier, Event $queueForEvents, Response $response, Database $dbForProject) { $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { @@ -1261,7 +1269,20 @@ App::patch('/v1/users/:userId/targets/:targetId/identifier') throw new Exception(Exception::USER_TARGET_NOT_FOUND); } - $target->setAttribute('identifier', $identifier); + if ($identifier) { + $target->setAttribute('identifier', $identifier); + } + + if ($providerId) { + $provider = $dbForProject->getDocument('providers', $providerId); + + if ($provider->isEmpty()) { + throw new Exception(Exception::PROVIDER_NOT_FOUND); + } + + $target->setAttribute('providerId', $provider->getId()); + $target->setAttribute('providerInternalId', $provider->getInternalId()); + } $target = $dbForProject->updateDocument('targets', $target->getId(), $target); $dbForProject->deleteCachedDocument('users', $user->getId()); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index d887a5a520..b7043a9738 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -236,6 +236,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_INTERNAL_UPDATE_DISABLED = 'provider_internal_update_disabled'; /** Topic */ public const TOPIC_NOT_FOUND = 'topic_not_found'; diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index dcef84c2df..68bae1b4d0 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -98,6 +98,11 @@ class Messaging extends Action $recipients = \array_merge($recipients, $targets); } + $internalProvider = $dbForProject->findOne('providers', [ + Query::equal('internal', [true]), + Query::equal('type', [$recipients[0]->getAttribute('providerType')]), + ]); + /** * @var array> $identifiersByProviderId */ @@ -109,6 +114,11 @@ class Messaging extends Action $providers = []; foreach ($recipients as $recipient) { $providerId = $recipient->getAttribute('providerId'); + + if (!$providerId) { + $providerId = $internalProvider->getId(); + } + if (!isset($identifiersByProviderId[$providerId])) { $identifiersByProviderId[$providerId] = []; } @@ -118,17 +128,26 @@ class Messaging extends Action /** * @var array[] $results */ - $results = batch(\array_map(function ($providerId) use ($identifiersByProviderId, $providers, $message, $dbForProject) { - return function () use ($providerId, $identifiersByProviderId, $providers, $message, $dbForProject) { - $provider = $dbForProject->getDocument('providers', $providerId); + $results = batch(\array_map(function ($providerId) use ($identifiersByProviderId, $providers, $internalProvider, $message, $dbForProject) { + return function () use ($providerId, $identifiersByProviderId, $providers, $internalProvider, $message, $dbForProject) { + $provider = new Document(); + + if ($internalProvider->getId() === $providerId) { + $provider = $internalProvider; + } else { + $provider = $dbForProject->getDocument('providers', $providerId); + } + $providers[] = $provider; $identifiers = $identifiersByProviderId[$providerId]; + $adapter = match ($provider->getAttribute('type')) { 'sms' => $this->sms($provider), 'push' => $this->push($provider), 'email' => $this->email($provider), default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE) }; + $maxBatchSize = $adapter->getMaxMessagesPerRequest(); $batches = \array_chunk($identifiers, $maxBatchSize); $batchIndex = 0; @@ -139,12 +158,14 @@ class Messaging extends Action $deliveryErrors = []; $messageData = clone $message; $messageData->setAttribute('to', $batch); + $data = match ($provider->getAttribute('type')) { 'sms' => $this->buildSMSMessage($messageData, $provider), 'push' => $this->buildPushMessage($messageData), 'email' => $this->buildEmailMessage($messageData, $provider), default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE) }; + try { $adapter->send($data); $deliveredTotal += \count($batch); @@ -168,10 +189,12 @@ class Messaging extends Action $deliveredTotal = 0; $deliveryErrors = []; + foreach ($results as $result) { $deliveredTotal += $result['deliveredTotal']; $deliveryErrors = \array_merge($deliveryErrors, $result['deliveryErrors']); } + $message->setAttribute('deliveryErrors', $deliveryErrors); if (\count($message->getAttribute('deliveryErrors')) > 0) { @@ -179,6 +202,7 @@ class Messaging extends Action } else { $message->setAttribute('status', 'sent'); } + $message->removeAttribute('to'); foreach ($providers as $provider) { diff --git a/src/Appwrite/Utopia/Response/Model/Target.php b/src/Appwrite/Utopia/Response/Model/Target.php index c6c5929ee3..f6346f409a 100644 --- a/src/Appwrite/Utopia/Response/Model/Target.php +++ b/src/Appwrite/Utopia/Response/Model/Target.php @@ -41,6 +41,12 @@ class Target extends Model 'default' => '', 'example' => '259125845563242502', ]) + ->addRule('providerType', [ + 'type' => self::TYPE_STRING, + 'description' => 'The target provider type. Can be one of the following: `email`, `sms` or `push`.', + 'default' => '', + 'example' => 'email', + ]) ->addRule('identifier', [ 'type' => self::TYPE_STRING, 'description' => 'The target identifier.', diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 93fd6cdc74..b57b680674 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -934,10 +934,11 @@ trait Base } }'; case self::$CREATE_USER_TARGET: - return 'mutation createUserTarget($userId: String!, $targetId: String!, $providerId: String!, $identifier: String!){ - usersCreateTarget(userId: $userId, targetId: $targetId, providerId: $providerId, identifier: $identifier) { + return 'mutation createUserTarget($userId: String!, $targetId: String!, $providerType: String!, $identifier: String! $providerId: String){ + usersCreateTarget(userId: $userId, targetId: $targetId, providerType: $providerType, identifier: $identifier, providerId: $providerId) { _id userId + providerType providerId identifier } @@ -949,6 +950,7 @@ trait Base targets { _id userId + providerType providerId identifier } @@ -959,15 +961,17 @@ trait Base usersGetTarget(userId: $userId, targetId: $targetId) { _id userId + providerType providerId identifier } }'; case self::$UPDATE_USER_TARGET: - return 'mutation updateUserTarget($userId: String!, $targetId: String!, $identifier: String!){ - usersUpdateTargetIdentifier(userId: $userId, targetId: $targetId, identifier: $identifier) { + return 'mutation updateUserTarget($userId: String!, $targetId: String!, $providerId: String, $identifier: String){ + usersUpdateTarget(userId: $userId, targetId: $targetId, providerId: $providerId, identifier: $identifier) { _id userId + providerType providerId identifier } diff --git a/tests/e2e/Services/GraphQL/MessagingTest.php b/tests/e2e/Services/GraphQL/MessagingTest.php index c0322372ba..112e9e5633 100644 --- a/tests/e2e/Services/GraphQL/MessagingTest.php +++ b/tests/e2e/Services/GraphQL/MessagingTest.php @@ -395,6 +395,7 @@ class MessagingTest extends Scope 'query' => $query, 'variables' => [ 'targetId' => ID::unique(), + 'providerType' => 'email', 'userId' => $userId, 'providerId' => $providerId, 'identifier' => 'token', @@ -604,6 +605,7 @@ class MessagingTest extends Scope 'query' => $query, 'variables' => [ 'targetId' => ID::unique(), + 'providerType' => 'email', 'userId' => $user['body']['data']['usersCreate']['_id'], 'providerId' => $providerId, 'identifier' => $to, @@ -678,119 +680,13 @@ class MessagingTest extends Scope */ public function testUpdateEmail(array $email) { - if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) { - $this->markTestSkipped('Email DSN not provided'); - } - - $emailDSN = new DSN(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN')); - $to = $emailDSN->getParam('to'); - $from = $emailDSN->getParam('from'); - $isEuRegion = $emailDSN->getParam('isEuRegion'); - $apiKey = $emailDSN->getPassword(); - $domain = $emailDSN->getUser(); - - if (empty($to) || empty($from) || empty($apiKey) || empty($domain) || empty($isEuRegion)) { - $this->markTestSkipped('Email provider not configured'); - } - - $query = $this->getQuery(self::$CREATE_MAILGUN_PROVIDER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'providerId' => ID::unique(), - 'name' => 'Mailgun2', - 'apiKey' => $apiKey, - 'domain' => $domain, - 'from' => $from, - 'isEuRegion' => filter_var($isEuRegion, FILTER_VALIDATE_BOOLEAN), - ], - ]; - $provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $provider['headers']['status-code']); - - $providerId = $provider['body']['data']['messagingCreateMailgunProvider']['_id']; - - $query = $this->getQuery(self::$CREATE_TOPIC); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'topicId' => ID::unique(), - 'name' => 'topic1', - 'description' => 'Active users', - ], - ]; - $topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $topic['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_USER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'userId' => ID::unique(), - 'email' => 'random2-mail@mail.org', - 'password' => 'password', - 'name' => 'Messaging User', - ] - ]; - $user = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $user['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_USER_TARGET); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'targetId' => ID::unique(), - 'userId' => $user['body']['data']['usersCreate']['_id'], - 'providerId' => $providerId, - 'identifier' => $to, - ], - ]; - $target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $target['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_SUBSCRIBER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'subscriberId' => ID::unique(), - 'topicId' => $topic['body']['data']['messagingCreateTopic']['_id'], - 'targetId' => $target['body']['data']['usersCreateTarget']['_id'], - ], - ]; - $subscriber = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), $graphQLPayload); - - $this->assertEquals(200, $subscriber['headers']['status-code']); - $query = $this->getQuery(self::$CREATE_EMAIL); $graphQLPayload = [ 'query' => $query, 'variables' => [ 'messageId' => ID::unique(), 'status' => 'draft', - 'topics' => [$topic['body']['data']['messagingCreateTopic']['_id']], + 'topics' => [$email['topics'][0]], 'subject' => 'Khali beats Undertaker', 'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', ], @@ -916,6 +812,7 @@ class MessagingTest extends Scope 'query' => $query, 'variables' => [ 'targetId' => ID::unique(), + 'providerType' => 'sms', 'userId' => $user['body']['data']['usersCreate']['_id'], 'providerId' => $providerId, 'identifier' => $to, @@ -988,117 +885,13 @@ class MessagingTest extends Scope */ public function testUpdateSMS(array $sms) { - if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) { - $this->markTestSkipped('SMS DSN not provided'); - } - - $smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN')); - $to = $smsDSN->getParam('to'); - $from = $smsDSN->getParam('from'); - $authKey = $smsDSN->getPassword(); - $senderId = $smsDSN->getUser(); - - if (empty($to) || empty($from) || empty($senderId) || empty($authKey)) { - $this->markTestSkipped('SMS provider not configured'); - } - - $query = $this->getQuery(self::$CREATE_MSG91_PROVIDER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'providerId' => ID::unique(), - 'name' => 'Msg91-2', - 'senderId' => $senderId, - 'authKey' => $authKey, - 'from' => $from, - ], - ]; - $provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $provider['headers']['status-code']); - - $providerId = $provider['body']['data']['messagingCreateMsg91Provider']['_id']; - - $query = $this->getQuery(self::$CREATE_TOPIC); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'topicId' => ID::unique(), - 'name' => 'topic1', - 'description' => 'Active users', - ], - ]; - $topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $topic['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_USER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'userId' => ID::unique(), - 'email' => 'random4-email@mail.org', - 'password' => 'password', - 'name' => 'Messaging User', - ] - ]; - $user = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $user['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_USER_TARGET); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'targetId' => ID::unique(), - 'userId' => $user['body']['data']['usersCreate']['_id'], - 'providerId' => $providerId, - 'identifier' => $to, - ], - ]; - $target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $target['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_SUBSCRIBER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'subscriberId' => ID::unique(), - 'topicId' => $topic['body']['data']['messagingCreateTopic']['_id'], - 'targetId' => $target['body']['data']['usersCreateTarget']['_id'], - ], - ]; - $subscriber = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), $graphQLPayload); - - $this->assertEquals(200, $subscriber['headers']['status-code']); - $query = $this->getQuery(self::$CREATE_SMS); $graphQLPayload = [ 'query' => $query, 'variables' => [ 'messageId' => ID::unique(), 'status' => 'draft', - 'topics' => [$topic['body']['data']['messagingCreateTopic']['_id']], + 'topics' => [$sms['topics'][0]], 'content' => '345463', ], ]; @@ -1219,6 +1012,7 @@ class MessagingTest extends Scope 'query' => $query, 'variables' => [ 'targetId' => ID::unique(), + 'providerType' => 'push', 'userId' => $user['body']['data']['usersCreate']['_id'], 'providerId' => $providerId, 'identifier' => $to, @@ -1293,112 +1087,13 @@ class MessagingTest extends Scope */ public function testUpdatePushNotification(array $push) { - if (empty(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) { - $this->markTestSkipped('Push DSN empty'); - } - - $pushDSN = new DSN(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); - $to = $pushDSN->getParam('to'); - $serverKey = $pushDSN->getPassword(); - - if (empty($to) || empty($serverKey)) { - $this->markTestSkipped('Push provider not configured'); - } - - $query = $this->getQuery(self::$CREATE_FCM_PROVIDER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'providerId' => ID::unique(), - 'name' => 'FCM2', - 'serverKey' => $serverKey, - ], - ]; - $provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $provider['headers']['status-code']); - $providerId = $provider['body']['data']['messagingCreateFcmProvider']['_id']; - - $query = $this->getQuery(self::$CREATE_TOPIC); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'topicId' => ID::unique(), - 'name' => 'topic1', - 'description' => 'Active users', - ], - ]; - $topic = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $topic['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_USER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'userId' => ID::unique(), - 'email' => 'random5-email@mail.org', - 'password' => 'password', - 'name' => 'Messaging User', - ] - ]; - $user = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $user['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_USER_TARGET); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'targetId' => ID::unique(), - 'userId' => $user['body']['data']['usersCreate']['_id'], - 'providerId' => $providerId, - 'identifier' => $to, - ], - ]; - $target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), $graphQLPayload); - - $this->assertEquals(200, $target['headers']['status-code']); - - $query = $this->getQuery(self::$CREATE_SUBSCRIBER); - $graphQLPayload = [ - 'query' => $query, - 'variables' => [ - 'subscriberId' => ID::unique(), - 'topicId' => $topic['body']['data']['messagingCreateTopic']['_id'], - 'targetId' => $target['body']['data']['usersCreateTarget']['_id'], - ], - ]; - $subscriber = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), $graphQLPayload); - - $this->assertEquals(200, $subscriber['headers']['status-code']); - $query = $this->getQuery(self::$CREATE_PUSH_NOTIFICATION); $graphQLPayload = [ 'query' => $query, 'variables' => [ 'messageId' => ID::unique(), 'status' => 'draft', - 'topics' => [$topic['body']['data']['messagingCreateTopic']['_id']], + 'topics' => [$push['topics'][0]], 'title' => 'Push Notification Title', 'body' => 'Push Notifiaction Body', ], diff --git a/tests/e2e/Services/GraphQL/UsersTest.php b/tests/e2e/Services/GraphQL/UsersTest.php index d750de5834..59c0c4a805 100644 --- a/tests/e2e/Services/GraphQL/UsersTest.php +++ b/tests/e2e/Services/GraphQL/UsersTest.php @@ -78,6 +78,7 @@ class UsersTest extends Scope 'variables' => [ 'targetId' => ID::unique(), 'userId' => $user['_id'], + 'providerType' => 'email', 'providerId' => $providerId, 'identifier' => 'identifier', ] @@ -479,7 +480,7 @@ class UsersTest extends Scope ], $this->getHeaders()), $graphQLPayload); $this->assertEquals(200, $target['headers']['status-code']); - $this->assertEquals('newidentifier', $target['body']['data']['usersUpdateTargetIdentifier']['identifier']); + $this->assertEquals('newidentifier', $target['body']['data']['usersUpdateTarget']['identifier']); } public function testDeleteUserSessions() diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index 21d22827f0..daafd52e8f 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -317,6 +317,7 @@ trait MessagingBase 'x-appwrite-key' => $this->getProject()['apiKey'], ]), [ 'targetId' => ID::unique(), + 'providerType' => 'email', 'providerId' => $provider['body']['$id'], 'identifier' => 'my-token', ]); @@ -558,6 +559,7 @@ trait MessagingBase 'isEuRegion' => filter_var($isEuRegion, FILTER_VALIDATE_BOOLEAN), 'from' => $from ]); + $this->assertEquals(201, $provider['headers']['status-code']); // Create Topic @@ -570,6 +572,7 @@ trait MessagingBase 'name' => 'topic1', 'description' => 'Test Topic' ]); + $this->assertEquals(201, $topic['headers']['status-code']); // Create User @@ -593,6 +596,7 @@ trait MessagingBase 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ 'targetId' => ID::unique(), + 'providerType' => 'email', 'providerId' => $provider['body']['$id'], 'identifier' => $to, ]); @@ -645,21 +649,6 @@ trait MessagingBase */ public function testUpdateEmail(array $email) { - if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) { - $this->markTestSkipped('Email DSN not provided'); - } - - $emailDSN = new DSN(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN')); - $to = $emailDSN->getParam('to'); - $from = $emailDSN->getParam('from'); - $isEuRegion = $emailDSN->getParam('isEuRegion'); - $apiKey = $emailDSN->getPassword(); - $domain = $emailDSN->getUser(); - - if (empty($to) || empty($from) || empty($apiKey) || empty($domain) || empty($isEuRegion)) { - $this->markTestSkipped('Email provider not configured'); - } - $message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $email['body']['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -669,71 +658,6 @@ trait MessagingBase // Test failure as the message has already been sent. $this->assertEquals(400, $message['headers']['status-code']); - // Create provider - $provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/mailgun', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), [ - 'providerId' => ID::unique(), - 'name' => 'Mailgun-provider-2', - 'apiKey' => $apiKey, - 'domain' => $domain, - 'isEuRegion' => filter_var($isEuRegion, FILTER_VALIDATE_BOOLEAN), - 'from' => $from - ]); - $this->assertEquals(201, $provider['headers']['status-code']); - - // Create Topic - $topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'topicId' => ID::unique(), - 'name' => 'topic1', - 'description' => 'Test Topic' - ]); - $this->assertEquals(201, $topic['headers']['status-code']); - - // Create User - $user = $this->client->call(Client::METHOD_POST, '/users', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'userId' => ID::unique(), - 'email' => 'random-email@mail.org', - 'password' => 'password', - 'name' => 'Messaging User', - ]); - - $this->assertEquals(201, $user['headers']['status-code']); - - // Create Target - $target = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'targetId' => ID::unique(), - 'providerId' => $provider['body']['$id'], - 'identifier' => $to, - ]); - - $this->assertEquals(201, $target['headers']['status-code']); - - // Create Subscriber - $subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'subscriberId' => ID::unique(), - 'targetId' => $target['body']['$id'], - ]); - - $this->assertEquals(201, $subscriber['headers']['status-code']); - // Create Email $email = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', [ 'content-type' => 'application/json', @@ -742,7 +666,7 @@ trait MessagingBase ], [ 'messageId' => ID::unique(), 'status' => 'draft', - 'topics' => [$topic['body']['$id']], + 'topics' => [$email['body']['topics'][0]], 'subject' => 'Khali beats Undertaker', 'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', ]); @@ -801,6 +725,7 @@ trait MessagingBase 'authKey' => $authKey, 'from' => $from ]); + $this->assertEquals(201, $provider['headers']['status-code']); // Create Topic @@ -813,6 +738,7 @@ trait MessagingBase 'name' => 'topic1', 'description' => 'Test Topic' ]); + $this->assertEquals(201, $topic['headers']['status-code']); // Create User @@ -836,6 +762,7 @@ trait MessagingBase 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ 'targetId' => ID::unique(), + 'providerType' => 'sms', 'providerId' => $provider['body']['$id'], 'identifier' => $to, ]); @@ -887,20 +814,6 @@ trait MessagingBase */ public function testUpdateSMS(array $sms) { - if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) { - $this->markTestSkipped('SMS DSN not provided'); - } - - $smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN')); - $to = $smsDSN->getParam('to'); - $from = $smsDSN->getParam('from'); - $authKey = $smsDSN->getPassword(); - $senderId = $smsDSN->getUser(); - - if (empty($to) || empty($from) || empty($senderId) || empty($authKey)) { - $this->markTestSkipped('SMS provider not configured'); - } - $message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/sms/' . $sms['body']['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -910,70 +823,6 @@ trait MessagingBase // Test failure as the message has already been sent. $this->assertEquals(400, $message['headers']['status-code']); - // Create provider - $provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/msg91', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), [ - 'providerId' => ID::unique(), - 'name' => 'Msg91-2', - 'senderId' => $senderId, - 'authKey' => $authKey, - 'from' => $from - ]); - $this->assertEquals(201, $provider['headers']['status-code']); - - // Create Topic - $topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'topicId' => ID::unique(), - 'name' => 'topic1', - 'description' => 'Test Topic' - ]); - $this->assertEquals(201, $topic['headers']['status-code']); - - // Create User - $user = $this->client->call(Client::METHOD_POST, '/users', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'userId' => ID::unique(), - 'email' => 'random2-email@mail.org', - 'password' => 'password', - 'name' => 'Messaging User', - ]); - - $this->assertEquals(201, $user['headers']['status-code']); - - // Create Target - $target = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'targetId' => ID::unique(), - 'providerId' => $provider['body']['$id'], - 'identifier' => $to, - ]); - - $this->assertEquals(201, $target['headers']['status-code']); - - // Create Subscriber - $subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'subscriberId' => ID::unique(), - 'targetId' => $target['body']['$id'], - ]); - - $this->assertEquals(201, $subscriber['headers']['status-code']); - // Create SMS $sms = $this->client->call(Client::METHOD_POST, '/messaging/messages/sms', [ 'content-type' => 'application/json', @@ -982,7 +831,7 @@ trait MessagingBase ], [ 'messageId' => ID::unique(), 'status' => 'draft', - 'topics' => [$topic['body']['$id']], + 'topics' => [$sms['body']['topics'][0]], 'content' => '047487', ]); @@ -1036,6 +885,7 @@ trait MessagingBase 'name' => 'FCM-1', 'serverKey' => $serverKey, ]); + $this->assertEquals(201, $provider['headers']['status-code']); // Create Topic @@ -1048,6 +898,7 @@ trait MessagingBase 'name' => 'topic1', 'description' => 'Test Topic' ]); + $this->assertEquals(201, $topic['headers']['status-code']); // Create User @@ -1071,6 +922,7 @@ trait MessagingBase 'x-appwrite-key' => $this->getProject()['apiKey'], ], [ 'targetId' => ID::unique(), + 'providerType' => 'push', 'providerId' => $provider['body']['$id'], 'identifier' => $to, ]); @@ -1123,18 +975,6 @@ trait MessagingBase */ public function testUpdatePushNotification(array $push) { - if (empty(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) { - $this->markTestSkipped('Push DSN empty'); - } - - $pushDSN = new DSN(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); - $to = $pushDSN->getParam('to'); - $serverKey = $pushDSN->getPassword(); - - if (empty($to) || empty($serverKey)) { - $this->markTestSkipped('Push provider not configured'); - } - $message = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/push/' . $push['body']['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1144,68 +984,6 @@ trait MessagingBase // Test failure as the message has already been sent. $this->assertEquals(400, $message['headers']['status-code']); - // Create provider - $provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/fcm', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), [ - 'providerId' => ID::unique(), - 'name' => 'FCM-2', - 'serverKey' => $serverKey, - ]); - $this->assertEquals(201, $provider['headers']['status-code']); - - // Create Topic - $topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'topicId' => ID::unique(), - 'name' => 'topic1', - 'description' => 'Test Topic' - ]); - $this->assertEquals(201, $topic['headers']['status-code']); - - // Create User - $user = $this->client->call(Client::METHOD_POST, '/users', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'userId' => ID::unique(), - 'email' => 'random4-email@mail.org', - 'password' => 'password', - 'name' => 'Messaging User', - ]); - - $this->assertEquals(201, $user['headers']['status-code']); - - // Create Target - $target = $this->client->call(Client::METHOD_POST, '/users/' . $user['body']['$id'] . '/targets', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'targetId' => ID::unique(), - 'providerId' => $provider['body']['$id'], - 'identifier' => $to, - ]); - - $this->assertEquals(201, $target['headers']['status-code']); - - // Create Subscriber - $subscriber = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['body']['$id'] . '/subscribers', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'subscriberId' => ID::unique(), - 'targetId' => $target['body']['$id'], - ]); - - $this->assertEquals(201, $subscriber['headers']['status-code']); - // Create push notification $push = $this->client->call(Client::METHOD_POST, '/messaging/messages/push', [ 'content-type' => 'application/json', @@ -1214,7 +992,7 @@ trait MessagingBase ], [ 'messageId' => ID::unique(), 'status' => 'draft', - 'topics' => [$topic['body']['$id']], + 'topics' => [$push['body']['topics'][0]], 'title' => 'Test-Notification', 'body' => 'Test-Notification-Body', ]); diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 9ca00aa5df..c9cb17d7b5 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -1232,7 +1232,7 @@ trait UsersBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'providerId' => 'unique()', + 'providerId' => ID::unique(), 'name' => 'Sengrid1', 'apiKey' => 'my-apikey', 'from' => 'from@domain.com', @@ -1244,6 +1244,7 @@ trait UsersBase ], $this->getHeaders()), [ 'targetId' => ID::unique(), 'providerId' => $provider['body']['$id'], + 'providerType' => 'email', 'identifier' => 'my-token', ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -1257,7 +1258,7 @@ trait UsersBase */ public function testUpdateUserTarget(array $data): array { - $response = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/targets/' . $data['$id'] . '/identifier', array_merge([ + $response = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/targets/' . $data['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1303,11 +1304,14 @@ trait UsersBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); + $this->assertEquals(204, $response['headers']['status-code']); + $response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/targets', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); + $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(0, $response['body']['total']); }