Compare commits

...
Author SHA1 Message Date
prateek banga 86a36582f1 adds resque scheduler worker 2023-10-19 20:12:34 +05:30
Prateek BangaandGitHub a0f0413984 Merge branch '1.5.x' into feat-sms-push-controllers 2023-10-19 14:33:14 +05:30
prateek banga 99b5ac8669 adds push and sms controllers 2023-10-19 13:29:48 +05:30
Jake BarnbyandGitHub 36d257c847 Merge pull request #6032 from appwrite/feat-topics-controller
Feat topics controller
2023-10-18 21:24:07 +13:00
Prateek Banga 55187d4781 fix typo 2023-10-18 13:30:01 +05:30
Prateek Banga 4bef86d88b review changes 2023-10-18 13:19:21 +05:30
Prateek Banga b86f531b30 adds data in message model and search param in messages query validator 2023-10-17 23:45:46 +05:30
Prateek Banga 8602c44d14 adds user targets graphql testsl 2023-10-17 23:29:10 +05:30
Prateek Banga ffeb3f8fcf review work 2023-10-17 22:53:26 +05:30
Prateek Banga 4ccffb6649 adds graphql tests for topics, subcribers, email 2023-10-17 03:41:42 +05:30
Prateek Banga feac25f401 adds already sent exception in update message 2023-10-13 18:35:49 +05:30
Prateek Banga 0b1b7646d5 review changes 2023-10-13 18:26:54 +05:30
Prateek Banga 4a44fb3330 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-10-13 14:52:02 +05:30
Jake BarnbyandGitHub 736b76cbfa Merge pull request #6023 from appwrite/feat-mailgun-provider
Feat provider controllers
2023-10-12 21:41:21 +13:00
Prateek Banga cc1769ed41 review changes 2023-10-12 11:48:26 +05:30
Prateek Banga 17ba6355e0 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-10-10 19:52:19 +05:30
Prateek Banga bb52647366 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-10-10 19:06:39 +05:30
Torsten DittmannandGitHub 8882318d0d Merge pull request #6875 from appwrite/sync-1.5.x-with-1.4.x
Sync 1.5.x with 1.4.x
2023-10-10 15:35:17 +02:00
Prateek Banga 1e5a2a78aa Merge branch '1.4.x' of https://github.com/appwrite/appwrite into sync-1.5.x-with-1.4.x 2023-10-10 17:46:44 +05:30
Prateek Banga ecfdab1598 Merge branch 'main' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-10-09 17:30:50 +05:30
Prateek Banga a77a212e2c adds scheduling for messaging worker 2023-10-09 17:29:26 +05:30
Prateek Banga a92390dda2 review changes 2023-10-06 19:23:46 +05:30
Christy JacobandGitHub 030f9629a6 Merge pull request #6496 from appwrite/fix-readme-cn
Update logo in README-CN.md
2023-10-06 08:55:17 -04:00
Prateek Banga d842191cc6 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-10-06 03:53:16 +05:30
Jake BarnbyandGitHub 76152019d6 Merge pull request #6514 from appwrite/sync-main-1.5.x
Sync main 1.5.x
2023-10-06 11:20:58 +13:00
Prateek Banga d5cc214546 Merge branch 'main' of https://github.com/appwrite/appwrite into sync-main-1.5.x 2023-10-06 03:44:36 +05:30
Prateek Banga 8e9f270ef0 update id param 2023-10-06 03:30:47 +05:30
Prateek Banga 22d445587e review changes 2023-10-05 16:57:48 +05:30
Steven NguyenandGitHub c5408393c8 Update logo in README-CN.md 2023-10-04 17:26:02 -07:00
Prateek Banga fb98d21ae5 adds graphql test cases for provider endpoints 2023-10-05 02:12:04 +05:30
Prateek Banga 0251703e3a Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-10-04 17:32:16 +05:30
Prateek Banga 80a19f9862 unique to ID::unique 2023-10-04 17:30:40 +05:30
Prateek Banga abab68f1a1 fixes catching in messaging worker 2023-10-04 17:28:38 +05:30
Prateek Banga 5891872fad Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-10-04 17:27:56 +05:30
Eldad A. FuxandGitHub 8a4302ffe6 Merge pull request #6454 from appwrite/hacktoberfest-update
Update with hacktoberfest instructions.
2023-10-04 14:42:53 +03:00
Prateek Banga c9c49158a3 changes to validator length 2023-10-04 16:24:29 +05:30
Prateek Banga 61785ab35a typo fix 2023-10-04 16:19:05 +05:30
Prateek Banga 48b91e39a8 review changes 2023-10-04 16:15:59 +05:30
4a43c84b29 Update CONTRIBUTING.md
Co-authored-by: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com>
2023-10-03 15:02:17 -04:00
Vincent (Wen Yu) GeandGitHub 897e975b49 Update with hacktoberfest instructions. 2023-10-03 10:36:27 -04:00
Torsten DittmannandGitHub 514a5c37fd Merge pull request #6253 from appwrite/feat-package-json
feat: package json
2023-10-02 13:42:20 +02:00
Prateek Banga 1a50b2948f lint fix 2023-09-27 19:10:56 +05:30
Prateek Banga 7708779ef1 adds email endpoint test 2023-09-27 19:08:35 +05:30
Prateek Banga 90352c57ac Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-27 18:58:18 +05:30
Prateek Banga 234f7f003b updated test cases for msg91 provider, moves worker to use provider from settings 2023-09-27 18:58:00 +05:30
Prateek Banga 63bd4d6daf Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-27 16:57:07 +05:30
Prateek Banga a0858ccc4e adds message description, options to mailgun provider 2023-09-26 13:12:44 +05:30
Prateek Banga b019273166 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-21 22:21:20 +05:30
Prateek Banga df6466a1d3 lint fix 2023-09-21 22:20:56 +05:30
Prateek Banga e00b273577 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-21 22:19:30 +05:30
Prateek Banga e2acb608fa fix tests with real sms providers 2023-09-21 22:16:02 +05:30
Prateek Banga d63eaf3ef0 fixes get message endpoint 2023-09-21 21:49:40 +05:30
Prateek Banga 06a05a3358 adds param messageId in send an email endpoint, tokenId as messageId in create phone session, membershipId as messageId in create team membership 2023-09-21 21:17:15 +05:30
Prateek Banga 3ebcfe548d Adds get message endpoint 2023-09-21 16:26:53 +05:30
Prateek Banga 342f122365 adds key auth to providers endpoint 2023-09-21 16:18:20 +05:30
Prateek Banga b4de44c905 fix small bug in worker and send email endpoint 2023-09-21 16:12:56 +05:30
Prateek Banga 39854239fb skip authorization in adding subscriber on targets 2023-09-21 02:29:57 +05:30
Prateek Banga 0510de5eab Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-20 23:37:35 +05:30
Prateek Banga 35d842171c review changes 2023-09-20 23:37:10 +05:30
Prateek Banga 3548c4ac47 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-20 23:03:11 +05:30
Prateek Banga dd14bbf785 lint fix 2023-09-20 23:02:53 +05:30
Prateek Banga 87011af914 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-20 23:00:48 +05:30
Prateek Banga 2fd7467e2a fix graphQL test 2023-09-20 22:59:47 +05:30
Prateek Banga 975b444761 fix test cases and bug fix in messaging worker 2023-09-20 20:51:03 +05:30
Prateek Banga 8d9e0ae7d0 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-20 17:53:08 +05:30
Prateek Banga 3456ad4e8c fixes merge conflict issue 2023-09-20 17:52:25 +05:30
Prateek Banga afef225339 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-20 17:39:36 +05:30
Prateek Banga f9030132a5 messaging worker logic to send message to providers batch limit 2023-09-20 17:38:29 +05:30
Prateek Banga a2e34b5b64 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-20 16:59:07 +05:30
Prateek Banga 67397cd763 fix tests and code changes 2023-09-20 15:55:22 +05:30
Prateek Banga bd36a09473 adds search in messaging controllers, updated mailgun endpoint with isEu param 2023-09-19 15:36:40 +05:30
Torsten Dittmann c1b0ef206b chore: remove unused props from package.json 2023-09-15 14:20:46 +02:00
Torsten Dittmann c58f5a4522 chore: add package.json 2023-09-15 14:17:10 +02:00
Jake BarnbyandGitHub afd421e196 Merge pull request #5986 from appwrite/feat-messages-event-config
Feat messages event config
2023-09-14 17:01:19 -04:00
Prateek Banga 25fc64c4ab moves provider to top level in events config 2023-09-15 02:28:32 +05:30
Prateek Banga d2ce8155e6 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-15 01:06:17 +05:30
Jake BarnbyandGitHub 250758a1d3 Merge pull request #5951 from appwrite/feat-add-messaging-response-models
Feat add messaging response models
2023-09-14 15:33:17 -04:00
prateek banga 7d51da2e69 review changes 2023-09-14 16:43:38 +05:30
prateek banga 2fe94be4c1 review changes 2023-09-14 16:34:53 +05:30
Prateek Banga 350549db7d Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-add-messaging-response-models 2023-09-14 02:17:34 +05:30
Jake BarnbyandGitHub 68dc92939f Merge pull request #6233 from appwrite/sync-1.4.x-to-1.5.x
Sync 1.4.x to 1.5.x
2023-09-13 16:47:00 -04:00
Prateek Banga dbd227ed50 Merge branch '1.4.x' of https://github.com/appwrite/appwrite into sync-1.4.x-to-1.5.x 2023-09-14 02:13:27 +05:30
prateek banga 4bb611efc4 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-13 17:48:18 +05:30
prateek banga 0066216bef adds enabled param for provider endpoints and review changes 2023-09-13 17:47:38 +05:30
prateek banga 778056d8e0 Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-13 17:08:37 +05:30
prateek banga f472ae578f Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-13 17:02:56 +05:30
prateek banga 9b6fc440d9 Review changes and adds enabled in providers collection 2023-09-13 16:57:59 +05:30
Jake Barnby 1f9872b398 Merge remote-tracking branch 'origin/1.5.x' into feat-add-messaging-response-models 2023-09-12 15:40:25 -04:00
Jake BarnbyandGitHub d709682d10 Merge pull request #5930 from appwrite/feat-add-messaging-collections-in-config
add collections to config file and messaging scopes to config file
2023-09-12 15:38:21 -04:00
Prateek Banga fbd4712a72 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-13 00:54:02 +05:30
Prateek Banga d742b0c0a9 removes provider type from create user target 2023-09-13 00:53:25 +05:30
Prateek Banga 945d048b6e Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-13 00:51:08 +05:30
Prateek Banga 6d5cc588bf Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-13 00:50:38 +05:30
Prateek Banga 851afae29a change test provider to authProviders 2023-09-13 00:50:16 +05:30
Prateek Banga d14fbde9e7 removes provider sub query 2023-09-13 00:40:51 +05:30
Prateek Banga 420eb8857a Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-13 00:07:32 +05:30
Prateek Banga 6affa6b5a0 Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-13 00:07:09 +05:30
Prateek Banga a723744b06 Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-13 00:06:49 +05:30
Prateek Banga 11a458f1df change providers to authProviders in remaining places 2023-09-13 00:06:18 +05:30
Prateek Banga e653e68869 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-12 23:33:04 +05:30
Prateek Banga 445ab98267 Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-12 23:32:30 +05:30
Prateek Banga 6631406236 Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-12 23:32:09 +05:30
Prateek Banga 7dd25f60f3 update console submodule changes 2023-09-12 23:30:04 +05:30
Prateek Banga 118a50b894 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-12 22:07:17 +05:30
Prateek Banga 720c7300dc Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-12 22:06:31 +05:30
Prateek Banga 68365babd1 Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-12 22:05:45 +05:30
Prateek Banga 497586378f removes unnecessary config load 2023-09-12 22:05:14 +05:30
Prateek Banga 9fbad72b08 Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-add-messaging-response-models 2023-09-12 22:03:34 +05:30
Prateek Banga 5136724a29 fix default,type index 2023-09-12 22:02:19 +05:30
Prateek Banga b54556d10e Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-12 21:31:45 +05:30
Prateek Banga a69368973b Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-12 21:31:06 +05:30
Prateek Banga 878e56ae7e Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-12 21:24:11 +05:30
Prateek Banga d14a4c2506 Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-add-messaging-response-models 2023-09-12 21:23:18 +05:30
Prateek Banga 2f2875e3c0 lint fix 2023-09-12 21:19:21 +05:30
Prateek Banga ba96f71a3f Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-add-messaging-collections-in-config 2023-09-12 21:15:00 +05:30
Prateek Banga b7119a2718 review changes 2023-09-12 21:03:44 +05:30
Prateek Banga cbea4264ea merge master 2023-09-12 20:53:16 +05:30
Prateek Banga 996de004fb Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-11 20:41:52 +05:30
Prateek Banga ddbee0a55b Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-11 20:41:12 +05:30
Prateek Banga 8957640d27 Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-11 20:40:32 +05:30
Prateek Banga 78a2f6f246 review changes 2023-09-11 20:36:43 +05:30
Prateek Banga 899c962061 Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-add-messaging-response-models 2023-09-11 20:30:30 +05:30
Prateek Banga a59cfb6461 review changes 2023-09-11 20:30:21 +05:30
Prateek Banga 32970ba8a0 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-11 19:54:24 +05:30
Prateek Banga 2ecf9ee98a udpates send email endpoint according to collection changes 2023-09-11 19:53:48 +05:30
Prateek Banga 27741644e8 Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-11 19:52:48 +05:30
Prateek Banga f4f1aa4b5b review changes and adds missing subqueries 2023-09-11 19:27:45 +05:30
Prateek Banga cf7b63fd2f Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-09 01:19:42 +05:30
Prateek Banga ee0f789e37 udpates deliveryError to be an array type 2023-09-09 01:18:26 +05:30
prateek banga a81006e961 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-06 16:31:07 +05:30
prateek banga 15773a3561 sets the correct response for send email 2023-09-06 16:30:46 +05:30
prateek banga 06361828a9 Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-06 15:41:19 +05:30
prateek banga 039e717d72 fixes in send email endpoint 2023-09-06 15:40:56 +05:30
prateek banga 3d286a2171 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-09-05 23:10:59 +05:30
prateek banga 38d81c058b lint fix 2023-09-05 23:10:33 +05:30
prateek banga 6aa443454b Merge branch 'feat-mailgun-provider' of https://github.com/appwrite/appwrite into feat-topics-controller 2023-09-05 22:58:43 +05:30
prateek banga 00498f0ae2 Merge branch 'feat-messages-event-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-05 22:58:14 +05:30
prateek banga 597fd965f7 Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-messages-event-config 2023-09-05 22:57:48 +05:30
prateek banga 4d31cd44d5 Merge branch 'feat-add-messaging-providers' of https://github.com/appwrite/appwrite into feat-add-messaging-response-models 2023-09-05 22:57:04 +05:30
prateek banga 45ab78caa7 Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-add-messaging-providers 2023-09-05 22:56:37 +05:30
prateek banga 78a53aee37 adds messaging event and updates messaging worker 2023-09-05 22:40:48 +05:30
prateek banga 01d5984965 Merge branch 'feat-add-messaging-response-models' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-05 13:34:10 +05:30
prateek banga 1a791f46e6 adds default in provider response model 2023-09-05 13:33:22 +05:30
prateek banga 337839103f Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-09-05 13:15:12 +05:30
prateek banga f4eac9f41d Merge branch 'feat-add-messaging-collections-in-config' into feat-mailgun-provider 2023-09-05 13:14:13 +05:30
prateek banga 3f95d6abd5 Merge branch 'feat-add-messaging-collections-in-config' of https://github.com/appwrite/appwrite into feat-add-messaging-collections-in-config 2023-09-05 01:34:02 +05:30
prateek banga ea324262e7 updated messaging collectin 2023-09-05 01:33:50 +05:30
prateek banga 8b2561d55a Merge branch '1.5.x' into feat-topics-controller 2023-08-31 19:54:45 +05:30
prateek banga 0b7d290419 Merge branch '1.5.x' into feat-mailgun-provider 2023-08-31 19:54:22 +05:30
prateek banga c263a7b130 Merge branch '1.5.x' into feat-messages-event-config 2023-08-31 19:53:35 +05:30
prateek banga 180493d642 Merge branch '1.5.x' into feat-add-messaging-response-models 2023-08-31 19:53:15 +05:30
prateek banga 0ed2729b86 Merge branch '1.5.x' into feat-add-messaging-providers 2023-08-31 19:50:58 +05:30
prateek banga 0c76cfcb00 Merge branch '1.5.x' into feat-add-messaging-collections-in-config 2023-08-31 19:50:20 +05:30
Eldad A. FuxandGitHub 0a289bce43 Merge pull request #6092 from appwrite/sync-master-into-1.5.x
Sync master into 1.5.x
2023-08-31 16:31:49 +03:00
prateek banga 2e89837c13 Merge branch 'master' into sync-master-into-1.5.x 2023-08-31 18:41:32 +05:30
prateek banga 82e165c4c3 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-31 18:33:02 +05:30
prateek banga 1762c81b8d Merge branch 'feat-messages-event-config' into feat-mailgun-provider 2023-08-31 18:32:26 +05:30
prateek banga 0534b11b32 Merge branch 'feat-add-messaging-response-models' into feat-messages-event-config 2023-08-31 18:26:32 +05:30
prateek banga 32b48a3c7c removes user id from subscriber model 2023-08-31 18:22:36 +05:30
prateek banga 8d14dcb839 Merge branch 'feat-add-messaging-providers' into feat-add-messaging-response-models 2023-08-31 18:18:59 +05:30
prateek banga e104e6d0f2 Merge branch 'feat-add-messaging-collections-in-config' into feat-add-messaging-providers 2023-08-31 18:16:48 +05:30
prateek banga 7fd124705c merge master 2023-08-31 18:11:56 +05:30
prateek banga 24eabf29d0 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-29 22:30:02 +05:30
prateek banga 112c7a5133 adds default attribute for providers 2023-08-29 22:29:48 +05:30
prateek banga 75d7f716e8 Merge branch '1.4.x' of https://github.com/appwrite/appwrite into feat-mailgun-provider 2023-08-29 22:13:12 +05:30
prateek banga e53af019da Revert "Working on messaging tests with worker"
This reverts commit c2c70d68e9.
2023-08-29 21:43:21 +05:30
wess c2c70d68e9 Working on messaging tests with worker 2023-08-28 16:27:55 -04:00
prateek banga 1d6276eb9f lint fix 2023-08-28 20:43:18 +05:30
prateek banga 491d58f146 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-28 20:42:12 +05:30
prateek banga 379f7b9aa2 fixes subquery attribute in collection config 2023-08-28 20:41:47 +05:30
prateek banga 264f0bd5dd adds subscriber controllers 2023-08-28 20:41:05 +05:30
prateek banga f743bf384b Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 19:48:40 +05:30
prateek banga 2c4e90d660 removes unnecessary test case 2023-08-25 19:48:21 +05:30
prateek banga 639fae3156 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 16:03:37 +05:30
prateek banga d7c8562260 removes target controllers from account service 2023-08-25 16:02:46 +05:30
prateek banga 9567bf3943 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 15:55:54 +05:30
prateek banga c59887e8fb fixes issue in sub queries 2023-08-25 15:55:04 +05:30
prateek banga de6bcc1861 fixes test 2023-08-25 14:40:48 +05:30
prateek banga 24853004f9 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 04:49:02 +05:30
prateek banga 9be0592dab lint fix 2023-08-25 04:46:15 +05:30
prateek banga a266682951 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 04:44:13 +05:30
prateek banga 94b7f67e44 changes id to providerId for provider create controllers 2023-08-25 04:43:40 +05:30
prateek banga d0c50403c8 adds test cases for topic controller 2023-08-25 04:36:41 +05:30
prateek banga b8f8fb1708 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 03:09:43 +05:30
prateek banga 5960666068 added messaging base test 2023-08-25 03:09:12 +05:30
prateek banga f835e1b52e Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 03:01:38 +05:30
prateek banga 048e8e81ba removes permission from provider controllers 2023-08-25 02:59:06 +05:30
prateek banga 33028ebbf2 Merge branch 'feat-mailgun-provider' into feat-topics-controller 2023-08-25 02:34:10 +05:30
prateek banga 33f4bd6ac4 adds custom id for provider controlers, fixes subquery 2023-08-25 02:32:51 +05:30
prateek banga 52f6b72f23 adds topic controllers 2023-08-25 01:45:17 +05:30
prateek banga df77f92860 adds sub query for targets and topics, permission for user target 2023-08-25 01:22:16 +05:30
prateek banga a87745f685 fix tests 2023-08-24 15:37:59 +05:30
prateek banga eafea1c41d formats messaging controller 2023-08-24 14:31:22 +05:30
prateek banga 5ca8dc0a73 merge cl-1.4.x 2023-08-24 14:10:26 +05:30
prateek banga e8cc60b0c2 adds audit resource in messaging controllers 2023-08-24 14:05:51 +05:30
prateek banga 82d220e158 fixes audit events 2023-08-24 13:43:30 +05:30
prateek banga a95c29d546 lint fix 2023-08-24 01:55:08 +05:30
prateek banga ec54000263 adds test for target controllers 2023-08-24 01:54:25 +05:30
prateek banga ac33d7828f Revert "Pint linter ran"
This reverts commit bf5f0a8419.
2023-08-24 01:53:52 +05:30
Jake BarnbyandGitHub 145b0f586d Merge pull request #6030 from appwrite/cl-1.4.x
Sync 1.5.x
2023-08-23 15:10:57 -04:00
wess bf5f0a8419 Pint linter ran 2023-08-23 13:12:43 -04:00
wess eba91ae55a Stubbing in tests and message builder 2023-08-23 12:22:04 -04:00
wess 3e7805afe1 Updates deps
Adds message builders to messaging worker

Adds Makefile to .gitignore ;)
2023-08-23 08:49:32 -04:00
prateek banga 55d00d9afa lint fix 2023-08-23 15:36:12 +05:30
prateek banga c225563d77 adds test for provider controllers 2023-08-23 15:31:29 +05:30
prateek banga dbbf4522e3 lint fix 2023-08-22 22:17:32 +05:30
prateek banga ab89abfc89 lint fix 2023-08-22 22:16:09 +05:30
prateek banga 78385a59c6 adds remaining provider controllers 2023-08-22 22:15:31 +05:30
prateek banga 9f19bec7a7 lint fix 2023-08-22 18:21:30 +05:30
prateek banga 4406c7bd55 adds messaging worker and send an email controller 2023-08-21 22:15:15 +05:30
prateek banga a6d613542b adds messaging api controlle for mailgun provider 2023-08-21 16:14:50 +05:30
prateek banga fe8b8d3dc6 fix lint 2023-08-21 14:14:06 +05:30
prateek banga 5e3065c30a merge resolve conflicts 2023-08-21 14:07:18 +05:30
prateek banga c0e4149feb Merge branch 'feat-messages-event-config' into feat-target-controllers-account-user 2023-08-18 23:35:00 +05:30
prateek banga cc1311b723 Merge branch 'feat-add-messaging-response-models' into feat-messages-event-config 2023-08-18 23:33:59 +05:30
prateek banga a174aca2c8 lint fix and rename subscriber model file correctly 2023-08-18 23:30:42 +05:30
prateek banga 36a5d1e245 adds remaining controllers for target in account and users 2023-08-18 23:28:12 +05:30
prateek banga f73b3bf272 deletes user cache document 2023-08-18 01:07:42 +05:30
prateek banga c83e4db23d lint fix 2023-08-17 19:02:07 +05:30
prateek banga 07b7a9db0f adds controller for creating a user target 2023-08-17 17:24:45 +05:30
prateek banga 455176d077 Merge branch 'feat-add-messaging-collections-in-config' into feat-target-controllers-account-user 2023-08-16 20:29:19 +05:30
prateek banga 40f419663c removes id attribute from collections config 2023-08-16 20:28:40 +05:30
prateek banga e53f189b3b lint fix 2023-08-16 20:17:36 +05:30
prateek banga c7dec3762b Adds getTarget and list targets controller to user and account 2023-08-16 20:13:38 +05:30
prateek banga ab655e69e6 Merge branch 'feat-add-messaging-collections-in-config' into feat-messages-event-config 2023-08-16 18:49:00 +05:30
prateek banga 9fc75e5535 moves messaging collection to common, adds subqueryTargets 2023-08-16 18:48:38 +05:30
prateek banga 37e5cecfb1 resolve merge conflicts 2023-08-16 18:35:15 +05:30
prateek banga caad1a07d9 resolve merge conflicts 2023-08-16 18:21:04 +05:30
prateek banga e2e4287801 resolve merge conflicts 2023-08-16 15:19:16 +05:30
prateek banga 45e03d3973 Merge branch 'feat-add-messaging-providers' into feat-add-messaging-response-models 2023-08-16 15:17:53 +05:30
prateek banga 92e587769a resolve merge conflicts 2023-08-16 15:16:45 +05:30
prateek banga 4b63a4abbb resolve merge conflicts 2023-08-14 21:13:47 +05:30
prateek banga 296a54d0ce adds messages event config and target attribute in user model 2023-08-14 21:11:24 +05:30
prateek banga d7f806e671 fix lint issuesl 2023-08-07 21:01:25 +05:30
prateek banga 3e70a19e4f add response models required for messaging service 2023-08-07 20:56:03 +05:30
prateek banga be1afc61b0 rename provider model to AuthProvider 2023-08-07 16:19:55 +05:30
prateek banga 9057a50a10 rename provider param in config 2023-08-07 15:54:00 +05:30
prateek banga 5baa0d674a rename providers.php to authProviders.php in docs and test 2023-08-07 14:17:02 +05:30
prateek banga 4ca798f4d7 fix lint issues 2023-08-04 19:01:55 +05:30
prateek banga 867f3e8c08 adds messaging providers rename providers.php to authProviders.php 2023-08-04 19:00:08 +05:30
prateek banga 19fd30d6d8 fix lint issues 2023-08-04 15:01:26 +05:30
prateek banga 13e73d8558 add scopes required for messaging service 2023-08-04 14:55:03 +05:30
prateek banga 6108be6ae0 Merge branch '1.4.x' of https://github.com/appwrite/appwrite into feat-add-messaging-collections-in-config 2023-08-04 14:31:10 +05:30
prateek banga 2d33681a0b add collections to config file 2023-08-02 20:28:30 +05:30
60 changed files with 7640 additions and 255 deletions
+12 -1
View File
@@ -98,4 +98,15 @@ _APP_VCS_GITHUB_CLIENT_SECRET=
_APP_VCS_GITHUB_WEBHOOK_SECRET=
_APP_MIGRATIONS_FIREBASE_CLIENT_ID=
_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=
_APP_ASSISTANT_OPENAI_API_KEY=
_APP_ASSISTANT_OPENAI_API_KEY=
_APP_MESSAGE_SMS_PROVIDER_MSG91_SENDER_ID=
_APP_MESSAGE_SMS_PROVIDER_MSG91_AUTH_KEY=
_APP_MESSAGE_SMS_PROVIDER_MSG91_FROM=
_APP_MESSAGE_SMS_PROVIDER_MSG91_TO=
_APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_API_KEY=
_APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_DOMAIN=
_APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_FROM=
_APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_RECEIVER_EMAIL=
_APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_IS_EU_REGION=
_APP_MESSAGE_PUSH_PROVIDER_FCM_SERVERY_KEY=
_APP_MESSAGE_PUSH_PROVIDER_FCM_RECEIVER_TOKEN=
+1
View File
@@ -13,3 +13,4 @@ debug/
app/sdks
dev/yasd_init.php
.phpunit.result.cache
Makefile
+5
View File
@@ -2,6 +2,11 @@
We would ❤️ you to contribute to Appwrite and help make it better! We want contributing to Appwrite to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, and new docs, as well as updates and tweaks, blog posts, workshops, and more.
## Here for Hacktoberfest?
If you're here to contribute during Hacktoberfest, we're so happy to see you here. Appwrite has been a long-time participant of Hacktoberfest and we welcome you, whatever your experience level. This year, we're **only taking contributions for issues tagged** `hacktoberfest`, so we can focus our resources to support your contributions.
You can [find issues using this query](https://github.com/search?q=org%3Aappwrite+is%3Aopen+type%3Aissue+label%3Ahacktoberfest&type=issues).
## How to Start?
If you are worried or dont know where to start, check out the next section that explains what kind of help we could use and where you can get involved. You can send your questions to [@appwrite](https://twitter.com/appwrite) on Twitter or to anyone from the [Appwrite team on Discord](https://appwrite.io/discord). You can also submit an issue, and a maintainer can guide you!
+1
View File
@@ -93,6 +93,7 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/worker-builds && \
chmod +x /usr/local/bin/worker-mails && \
chmod +x /usr/local/bin/worker-messaging && \
chmod +x /usr/local/bin/resque-scheduler && \
chmod +x /usr/local/bin/worker-webhooks && \
chmod +x /usr/local/bin/worker-migrations
+1 -1
View File
@@ -2,7 +2,7 @@
<br />
<p align="center">
<a href="https://appwrite.io" target="_blank"><img width="260" height="39" src="https://appwrite.io/images/appwrite.svg" alt="Appwrite Logo"></a>
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite Logo"></a>
<br />
<br />
<b>适用于[Flutter/Vue/Angular/React/iOS/Android/* 等等平台 *]的完整后端服务</b>
+598 -2
View File
@@ -5,7 +5,7 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Helpers\ID;
$providers = Config::getParam('providers', []);
$providers = Config::getParam('authProviders', []);
$auth = Config::getParam('auth', []);
/**
@@ -221,6 +221,17 @@ $commonCollections = [
'array' => false,
'filters' => ['subQueryMemberships'],
],
[
'$id' => ID::custom('targets'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['subQueryTargets'],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
@@ -1366,7 +1377,592 @@ $commonCollections = [
],
],
],
];
'providers' => [
'$collection' => ID::custom(DATABASE::METADATA),
'$id' => ID::custom('providers'),
'name' => 'Providers',
'attributes' => [
[
'$id' => ID::custom('name'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('provider'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('type'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('default'),
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => false,
'default' => false,
'array' => false,
],
[
'$id' => ID::custom('enabled'),
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => true,
'default' => true,
'array' => false,
],
[
'$id' => ID::custom('credentials'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => ['json', 'encrypt'],
],
[
'$id' => ID::custom('options'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'array' => false,
'filters' => ['json'],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 65535,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_provider'),
'type' => Database::INDEX_KEY,
'attributes' => ['provider'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_type'),
'type' => Database::INDEX_KEY,
'attributes' => ['type'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_default'),
'type' => Database::INDEX_KEY,
'attributes' => ['default'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_default_type'),
'type' => Database::INDEX_KEY,
'attributes' => ['default','type'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [],
'orders' => [],
]
],
],
'messages' => [
'$collection' => ID::custom(DATABASE::METADATA),
'$id' => ID::custom('messages'),
'name' => 'Messages',
'attributes' => [
[
'$id' => ID::custom('providerId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('providerInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('description'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('status'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => 'processing',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('data'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 65535,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => ['json'],
],
[
'$id' => ID::custom('to'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 65535,
'signed' => true,
'required' => true,
'default' => null,
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('deliveryTime'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('deliveredAt'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('deliveryErrors'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 65535,
'signed' => true,
'required' => false,
'default' => null,
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('deliveredTo'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_providerId'),
'type' => Database::INDEX_KEY,
'attributes' => ['providerId'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_providerInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['providerInternalId'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [],
'orders' => [],
],
],
],
'topics' => [
'$collection' => ID::custom(DATABASE::METADATA),
'$id' => ID::custom('topics'),
'name' => 'Topics',
'attributes' => [
[
'$id' => ID::custom('providerId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('providerInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('name'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('description'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 2048,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('targets'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['subQueryTopicTargets'],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_providerId'),
'type' => Database::INDEX_KEY,
'attributes' => ['providerId'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_providerInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['providerInternalId'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
]
],
],
'subscribers' => [
'$collection' => ID::custom(DATABASE::METADATA),
'$id' => ID::custom('subscribers'),
'name' => 'Subscribers',
'attributes' => [
[
'$id' => ID::custom('targetId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('targetInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('topicId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('topicInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_targetId'),
'type' => Database::INDEX_KEY,
'attributes' => ['targetId'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_targetInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['targetInternalId'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_topicId'),
'type' => Database::INDEX_KEY,
'attributes' => ['topicId'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_topicInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['topicInternalId'],
'lengths' => [],
'orders' => [],
]
],
],
'targets' => [
'$collection' => ID::custom(DATABASE::METADATA),
'$id' => ID::custom('targets'),
'name' => 'Targets',
'attributes' => [
[
'$id' => ID::custom('userId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('userInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('providerId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('providerInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('identifier'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_userId'),
'type' => Database::INDEX_KEY,
'attributes' => ['userId'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_userInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['userInternalId'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_providerId'),
'type' => Database::INDEX_KEY,
'attributes' => ['providerId'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_providerInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['providerInternalId'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_identifier'),
'type' => Database::INDEX_KEY,
'attributes' => ['identifier'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_identifier_providerId'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['providerId', 'identifier'],
'lengths' => [],
'orders' => [],
]
],
],
];
$projectCollections = array_merge([
'databases' => [
+63
View File
@@ -240,6 +240,16 @@ return [
'description' => 'OAuth2 provider returned some error.',
'code' => 424,
],
Exception::USER_TARGET_NOT_FOUND => [
'name' => Exception::USER_TARGET_NOT_FOUND,
'description' => 'The target could not be found.',
'code' => 404,
],
Exception::USER_TARGET_ALREADY_EXISTS => [
'name' => Exception::USER_TARGET_ALREADY_EXISTS,
'description' => 'A target with the same ID already exists.',
'code' => 409,
],
/** Teams */
Exception::TEAM_NOT_FOUND => [
@@ -728,4 +738,57 @@ return [
'description' => 'Migration is already in progress. You can check the status of the migration in your Appwrite Console\'s "Settings" > "Migrations".',
'code' => 409,
],
/** Provider Errors */
Exception::PROVIDER_NOT_FOUND => [
'name' => Exception::PROVIDER_NOT_FOUND,
'description' => 'Provider with the requested ID could not be found.',
'code' => 404,
],
Exception::PROVIDER_ALREADY_EXISTS => [
'name' => Exception::PROVIDER_ALREADY_EXISTS,
'description' => 'Provider with the requested ID already exists.',
'code' => 409,
],
Exception::PROVIDER_INCORRECT_TYPE => [
'name' => Exception::PROVIDER_INCORRECT_TYPE,
'description' => 'Provider with the requested ID is of incorrect type: ',
'code' => 400,
],
/** Topic Errors */
Exception::TOPIC_NOT_FOUND => [
'name' => Exception::TOPIC_NOT_FOUND,
'description' => 'Topic with the request ID could not be found.',
'code' => 404,
],
Exception::TOPIC_ALREADY_EXISTS => [
'name' => Exception::TOPIC_ALREADY_EXISTS,
'description' => 'Topic with the request ID already exists.',
'code' => 409,
],
/** Subscriber Errors */
Exception::SUBSCRIBER_NOT_FOUND => [
'name' => Exception::SUBSCRIBER_NOT_FOUND,
'description' => 'Subscriber with the request ID could not be found.',
'code' => 404,
],
Exception::SUBSCRIBER_ALREADY_EXISTS => [
'name' => Exception::SUBSCRIBER_ALREADY_EXISTS,
'description' => 'Subscriber with the request ID already exists.',
'code' => 409,
],
/** Message Errors */
Exception::MESSAGE_NOT_FOUND => [
'name' => Exception::MESSAGE_NOT_FOUND,
'description' => 'Message with the requested ID could not be found.',
'code' => 404,
],
Exception::MESSAGE_ALREADY_SENT => [
'name' => Exception::MESSAGE_ALREADY_SENT,
'description' => 'Message with the requested ID has already been sent.',
'code' => 400,
]
];
+62
View File
@@ -44,6 +44,20 @@ return [
'$description' => 'This event triggers when a verification token for a user is validated.'
],
],
'targets' => [
'$model' => Response::MODEL_TARGET,
'$resource' => true,
'$description' => 'This event triggers on any user\'s target event.',
'create' => [
'$description' => 'This event triggers when a user\'s target is created.',
],
'update' => [
'$description' => 'This event triggers when a user\'s target is updated.',
],
'delete' => [
'$description' => 'This event triggers when a user\'s target is deleted.',
],
],
'create' => [
'$description' => 'This event triggers when a user is created.'
],
@@ -237,6 +251,54 @@ return [
'$description' => 'This event triggers when a function is updated.',
]
],
'messages' => [
'$model' => Response::MODEL_MESSAGE,
'$resource' => true,
'$description' => 'This event triggers on any messaging event.',
'create' => [
'$description' => 'This event triggers when a message is created.',
],
'update' => [
'$description' => 'This event triggers when a message is updated.',
],
'topics' => [
'$model' => Response::MODEL_TOPIC,
'$resource' => true,
'$description' => 'This event triggers on any topic event.',
'create' => [
'$description' => 'This event triggers when a provider is created.',
],
'delete' => [
'$description' => 'This event triggers when a provider is deleted.'
],
'subscribers' => [
'$model' => Response::MODEL_SUBSCRIBER,
'$resource' => true,
'$description' => 'This event triggers on any subscriber event.',
'create' => [
'$description' => 'This event triggers when a subscriber is created.',
],
'delete' => [
'$description' => 'This event triggers when a subscriber is deleted.'
],
],
],
],
'providers' => [
'$model' => Response::MODEL_PROVIDER,
'$resource' => true,
'$description' => 'This event triggers on any provider event.',
'create' => [
'$description' => 'This event triggers when a provider is created.',
],
'update' => [
'$description' => 'This event triggers when a provider is updated.',
],
'delete' => [
'$description' => 'This event triggers when a provider is deleted.'
],
],
'rules' => [
'$model' => Response::MODEL_PROXY_RULE,
'$resource' => true,
+14
View File
@@ -21,6 +21,10 @@ $member = [
'avatars.read',
'execution.read',
'execution.write',
'targets.read',
'targets.write',
'subscribers.write',
'subscribers.read',
'assistant.read',
];
@@ -60,6 +64,16 @@ $admins = [
'migrations.write',
'vcs.read',
'vcs.write',
'targets.read',
'targets.write',
'providers.write',
'providers.read',
'messages.write',
'messages.read',
'topics.write',
'topics.read',
'subscribers.write',
'subscribers.read'
];
return [
+30
View File
@@ -76,6 +76,36 @@ return [ // List of publicly visible scopes
'health.read' => [
'description' => 'Access to read your project\'s health status',
],
'providers.read' => [
'description' => 'Access to read your project\'s providers',
],
'providers.write' => [
'description' => 'Access to create, update, and delete your project\'s providers',
],
'messages.read' => [
'description' => 'Access to read your project\'s messages',
],
'messages.write' => [
'description' => 'Access to create, update, and delete your project\'s messages',
],
'topics.read' => [
'description' => 'Access to read your project\'s topics',
],
'topics.write' => [
'description' => 'Access to create, update, and delete your project\'s topics',
],
'subscribers.read' => [
'description' => 'Access to read your project\'s subscribers',
],
'subscribers.write' => [
'description' => 'Access to create, update, and delete your project\'s subscribers',
],
'targets.read' => [
'description' => 'Access to read your project\'s targets',
],
'targets.write' => [
'description' => 'Access to create, update, and delete your project\'s targets',
],
'rules.read' => [
'description' => 'Access to read your project\'s proxy rules',
],
+13
View File
@@ -251,4 +251,17 @@ return [
'optional' => true,
'icon' => '/images/services/migrations.png',
],
'messaging' => [
'key' => 'messaging',
'name' => 'Messaging',
'subtitle' => 'The Messaging service allows you to send messages to any provider type (SMTP, push notification, SMS, etc.).',
'description' => '/docs/services/messaging.md',
'controller' => 'api/messaging.php',
'sdk' => true,
'docs' => true,
'docsUrl' => 'https://appwrite.io/docs/server/messaging',
'tests' => true,
'optional' => true,
'icon' => '/images/services/messaging.png',
]
];
+74 -19
View File
@@ -8,7 +8,6 @@ use Appwrite\Auth\Validator\Phone;
use Appwrite\Detector\Detector;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Phone as EventPhone;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Utopia\Validator\Host;
@@ -45,6 +44,7 @@ use Utopia\Validator\WhiteList;
use Appwrite\Auth\Validator\PasswordHistory;
use Appwrite\Auth\Validator\PasswordDictionary;
use Appwrite\Auth\Validator\PersonalData;
use Appwrite\Event\Messaging;
$oauthDefaultSuccess = '/auth/oauth2/success';
$oauthDefaultFailure = '/auth/oauth2/failure';
@@ -298,7 +298,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
->label('sdk.methodType', 'webAuth')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('providers'), fn($node) => (!$node['mock'])))) . '.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('authProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('authProviders'), fn($node) => (!$node['mock'])))) . '.')
->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
@@ -357,7 +357,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->label('scope', 'public')
->label('docs', false)
->param('projectId', '', new Text(1024), 'Project ID.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('authProviders')), true), 'OAuth2 provider.')
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
->param('state', '', new Text(2048), 'Login state params.', true)
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
@@ -390,7 +390,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->label('origin', '*')
->label('docs', false)
->param('projectId', '', new Text(1024), 'Project ID.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('authProviders')), true), 'OAuth2 provider.')
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
->param('state', '', new Text(2048), 'Login state params.', true)
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
@@ -429,7 +429,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->label('docs', false)
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('usage.params', ['provider:{request.provider}'])
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('authProviders')), true), 'OAuth2 provider.')
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
->param('state', '', new Text(2048), 'OAuth2 state params.', true)
->param('error', '', new Text(2048, 0), 'Error code returned from the OAuth2 provider.', true)
@@ -457,7 +457,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$providers = Config::getParam('providers');
$providers = Config::getParam('authProviders');
$providerName = $providers[$provider]['name'] ?? '';
/** @var Appwrite\Auth\OAuth2 $oauth2 */
@@ -1238,9 +1238,12 @@ App::post('/v1/account/sessions/phone')
->inject('events')
->inject('messaging')
->inject('locale')
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $events, EventPhone $messaging, Locale $locale) {
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $events, Messaging $messaging, Locale $locale) {
$provider = Authorization::skip(fn () => $dbForProject->findOne('providers', [
Query::equal('default', [true]),
Query::equal('type', ['sms'])
]));
if ($provider === false || $provider->isEmpty()) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -1326,9 +1329,34 @@ App::post('/v1/account/sessions/phone')
$message = $message->setParam('{{token}}', $secret);
$message = $message->render();
$target = $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
Query::equal('providerInternalId', [$provider->getInternalId()])
]);
if (!$target) {
$target = $dbForProject->createDocument('targets', new Document([
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
'identifier' => $phone,
]));
}
$messageDoc = $dbForProject->createDocument('messages', new Document([
'$id' => $token->getId(),
'to' => [$target->getId()],
'data' => [
'content' => $message,
],
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
]));
$messaging
->setRecipient($phone)
->setMessage($message)
->setMessageId($messageDoc->getId())
->setProject($project)
->trigger();
$events->setPayload(
@@ -2885,10 +2913,13 @@ App::post('/v1/account/verification/phone')
->inject('messaging')
->inject('project')
->inject('locale')
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $events, EventPhone $messaging, Document $project, Locale $locale) {
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED);
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $events, Messaging $messaging, Document $project, Locale $locale) {
$provider = Authorization::skip(fn () => $dbForProject->findOne('providers', [
Query::equal('default', [true]),
Query::equal('type', ['sms'])
]));
if ($provider === false || $provider->isEmpty()) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
if (empty($user->getAttribute('phone'))) {
@@ -2933,11 +2964,35 @@ App::post('/v1/account/verification/phone')
$message = $message->setParam('{{token}}', $secret);
$message = $message->render();
$target = $dbForProject->findOne('targets', [
Query::equal('identifier', [$user->getAttribute('phone')]),
Query::equal('providerInternalId', [$provider->getInternalId()])
]);
if (!$target) {
$target = $dbForProject->createDocument('targets', new Document([
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
'identifier' => $user->getAttribute('phone'),
]));
}
$messageDoc = $dbForProject->createDocument('messages', new Document([
'$id' => $verification->getId(),
'to' => [$target->getId()],
'data' => [
'content' => $message,
],
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
]));
$messaging
->setRecipient($user->getAttribute('phone'))
->setMessage($message)
->trigger()
;
->setMessageId($messageDoc->getId())
->setProject($project)
->trigger();
$events
->setParam('userId', $user->getId())
+5 -2
View File
@@ -2489,8 +2489,11 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
'orders' => $orders,
]);
$validator = new IndexValidator($dbForProject->getAdapter()->getMaxIndexLength());
if (!$validator->isValid($collection->setAttribute('indexes', $index, Document::SET_TYPE_APPEND))) {
$validator = new IndexValidator(
$collection->getAttribute('attributes'),
$dbForProject->getAdapter()->getMaxIndexLength()
);
if (!$validator->isValid($index)) {
throw new Exception(Exception::INDEX_INVALID, $validator->getDescription());
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -570,7 +570,7 @@ App::patch('/v1/projects/:projectId/oauth2')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'Provider Name')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('authProviders')), true), 'Provider Name')
->param('appId', null, new Text(256), 'Provider app ID. Max length: 256 chars.', true)
->param('secret', null, new text(512), 'Provider secret key. Max length: 512 chars.', true)
->param('enabled', null, new Boolean(), 'Provider status. Set to \'false\' to disable new session creation.', true)
+34 -4
View File
@@ -6,7 +6,7 @@ use Appwrite\Detector\Detector;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Phone as EventPhone;
use Appwrite\Event\Messaging;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Utopia\Validator\Host;
@@ -380,6 +380,7 @@ App::post('/v1/teams/:teamId/memberships')
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
->param('from', '', new Text(128), 'Sender of the message. It can be alphanumeric (Ex: MyCompany20). Restrictions may apply depending of the destination.', true)
->inject('response')
->inject('project')
->inject('user')
@@ -388,7 +389,7 @@ App::post('/v1/teams/:teamId/memberships')
->inject('mails')
->inject('messaging')
->inject('events')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $mails, EventPhone $messaging, Event $events) {
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, string $from, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $mails, Messaging $messaging, Event $events) {
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -632,6 +633,15 @@ App::post('/v1/teams/:teamId/memberships')
->trigger()
;
} elseif (!empty($phone)) {
$provider = Authorization::skip(fn () => $dbForProject->findOne('providers', [
Query::equal('default', [true, false]),
Query::equal('type', ['sms'])
]));
if ($provider === false || $provider->isEmpty()) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.invitation-' . $locale->default] ?? [];
@@ -642,9 +652,29 @@ App::post('/v1/teams/:teamId/memberships')
$message = $message->setParam('{{token}}', $url);
$message = $message->render();
$target = $dbForProject->createDocument('targets', new Document([
'userId' => $invitee->getId(),
'userInternalId' => $invitee->getInternalId(),
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
'identifier' => $phone,
]));
$messageDoc = $dbForProject->createDocument('messages', new Document([
// Here membership ID is used as message ID so that it can be used in test cases to verify the message
'$id' => $membership->getId(),
'to' => [$target->getId()],
'data' => [
'content' => $message,
],
'providerId' => $provider->getId(),
'providerInternalId' => $provider->getInternalId(),
'deliveryTime' => Datetime::now(),
]));
$messaging
->setRecipient($phone)
->setMessage($message)
->setMessageId($messageDoc->getId())
->setProject($project)
->trigger();
}
}
+212 -1
View File
@@ -380,6 +380,64 @@ App::post('/v1/users/scrypt-modified')
->dynamic($user, Response::MODEL_USER);
});
App::post('/v1/users/:userId/targets')
->desc('Create User Target')
->groups(['api', 'users'])
->label('audits.event', 'users.targets.create')
->label('audits.resource', 'target/response.$id')
->label('scope', 'targets.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'users')
->label('sdk.method', 'createTarget')
->label('sdk.description', '/docs/references/users/create-target.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->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('providerId', '', new UID(), 'Provider ID.')
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
->inject('response')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $targetId, string $providerId, string $identifier, Response $response, Database $dbForProject, Event $events) {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$target = $dbForProject->getDocument('targets', $targetId);
if (!$target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
try {
$target = $dbForProject->createDocument('targets', new Document([
'$id' => $targetId,
'providerId' => $providerId,
'providerInternalId' => $provider->getInternalId(),
'userId' => $userId,
'userInternalId' => $user->getInternalId(),
'identifier' => $identifier,
]));
} catch (Duplicate) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($target, Response::MODEL_TARGET);
});
App::get('/v1/users')
->desc('List users')
->groups(['api', 'users'])
@@ -483,6 +541,38 @@ App::get('/v1/users/:userId/prefs')
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
});
App::get('/v1/users/:userId/targets/:targetId')
->desc('Get User Target')
->groups(['api', 'users'])
->label('scope', 'targets.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'users')
->label('sdk.method', 'getTarget')
->label('sdk.description', '/docs/references/users/get-user-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.')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $targetId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$target = $user->find('$id', $targetId, 'targets');
if (empty($target)) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
$response->dynamic($target, Response::MODEL_TARGET);
});
App::get('/v1/users/:userId/sessions')
->desc('List user sessions')
->groups(['api', 'users'])
@@ -647,6 +737,35 @@ App::get('/v1/users/:userId/logs')
]), Response::MODEL_LOG_LIST);
});
App::get('/v1/users/:userId/targets')
->desc('List User Targets')
->groups(['api', 'users'])
->label('scope', 'targets.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'users')
->label('sdk.method', 'listTargets')
->label('sdk.description', '/docs/references/users/list-user-targets.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TARGET_LIST)
->param('userId', '', new UID(), 'User ID.')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$targets = $user->getAttribute('targets', []);
$response->dynamic(new Document([
'targets' => $targets,
'total' => \count($targets),
]), Response::MODEL_TARGET_LIST);
});
App::get('/v1/users/identities')
->desc('List Identities')
->groups(['api', 'users'])
@@ -1081,6 +1200,56 @@ 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')
->groups(['api', 'users'])
->label('audits.event', 'users.targets.update')
->label('audits.resource', 'target/{response.$id}')
->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.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.)')
->inject('response')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $targetId, string $identifier, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$target = $dbForProject->getDocument('targets', $targetId);
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
if ($user->getId() !== $target->getAttribute('userId')) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
$target->setAttribute('identifier', $identifier);
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
$dbForProject->deleteCachedDocument('users', $user->getId());
$events
->setParam('userId', $userId)
->setParam('targetId', $targetId);
$response
->dynamic($target, Response::MODEL_TARGET);
});
App::delete('/v1/users/:userId/sessions/:sessionId')
->desc('Delete user session')
->groups(['api', 'users'])
@@ -1211,6 +1380,48 @@ App::delete('/v1/users/:userId')
$response->noContent();
});
App::delete('/v1/users/:userId/targets/:targetId')
->desc('Delete user target')
->groups(['api', 'users'])
->label('audits.event', 'users.targets.delete')
->label('audits.resource', 'target/{request.$targetId}')
->label('scope', 'targets.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'users')
->label('sdk.method', 'deleteTarget')
->label('sdk.description', '/docs/references/users/delete-target.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->inject('response')
->inject('dbForProject')
->inject('events')
->action(function (string $userId, string $targetId, Response $response, Database $dbForProject, Event $events) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$target = $dbForProject->getDocument('targets', $targetId);
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
if ($user->getId() !== $target->getAttribute('userId')) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
$target = $dbForProject->deleteDocument('targets', $target->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
$response->noContent();
});
App::delete('/v1/users/identities/:identityId')
->desc('Delete Identity')
->groups(['api', 'users'])
@@ -1254,7 +1465,7 @@ App::get('/v1/users/usage')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn ($value) => "oauth-" . $value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn ($value) => "oauth-" . $value, \array_keys(Config::getParam('authProviders', [])))), true), 'Provider Name.', true)
->inject('response')
->inject('dbForProject')
->inject('register')
+36 -3
View File
@@ -25,7 +25,7 @@ use Appwrite\Event\Audit;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Phone;
use Appwrite\Event\Messaging;
use Appwrite\Event\Delete;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Validator\Email;
@@ -169,6 +169,7 @@ const DELETE_TYPE_SESSIONS = 'sessions';
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
const DELETE_TYPE_SCHEDULES = 'schedules';
const DELETE_TYPE_SUBSCRIBERS = 'subscribers';
// Compression type
const COMPRESSION_TYPE_NONE = 'none';
const COMPRESSION_TYPE_GZIP = 'gzip';
@@ -233,7 +234,7 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
Config::load('events', __DIR__ . '/config/events.php');
Config::load('auth', __DIR__ . '/config/auth.php');
Config::load('errors', __DIR__ . '/config/errors.php');
Config::load('providers', __DIR__ . '/config/providers.php');
Config::load('authProviders', __DIR__ . '/config/authProviders.php');
Config::load('platforms', __DIR__ . '/config/platforms.php');
Config::load('collections', __DIR__ . '/config/collections.php');
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
@@ -530,6 +531,38 @@ Database::addFilter(
}
);
Database::addFilter(
'subQueryTargets',
function (mixed $value) {
return null;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
->find('targets', [
Query::equal('userInternalId', [$document->getInternalId()]),
]));
}
);
Database::addFilter(
'subQueryTopicTargets',
function (mixed $value) {
return null;
},
function (mixed $value, Document $document, Database $database) {
$targetIds = Authorization::skip(fn () => \array_map(
fn ($document) => $document->getAttribute('targetId'),
$database
->find('subscribers', [
Query::equal('topicInternalId', [$document->getInternalId()]),
])
));
if (\count($targetIds) > 0) {
return $database->find('targets', [Query::equal('$id', $targetIds)]);
}
return [];
}
);
/**
* DB Formats
*/
@@ -892,7 +925,7 @@ App::setResource('audits', fn() => new Audit());
App::setResource('mails', fn() => new Mail());
App::setResource('deletes', fn() => new Delete());
App::setResource('database', fn() => new EventDatabase());
App::setResource('messaging', fn() => new Phone());
App::setResource('messaging', fn() => new Messaging());
App::setResource('queue', function (Group $pools) {
return $pools->get('queue')->pop()->getResource();
}, ['pools']);
+22
View File
@@ -127,6 +127,10 @@ class DeletesV1 extends Worker
case DELETE_TYPE_SCHEDULES:
$this->deleteSchedules($this->args['datetime']);
break;
case DELETE_TYPE_SUBSCRIBERS:
$topic = new Document($this->args['document'] ?? []);
$this->deleteSubscribers($project, $topic);
break;
default:
Console::error('No delete operation for type: ' . $type);
break;
@@ -170,6 +174,24 @@ class DeletesV1 extends Worker
);
}
/**
* @param Document $project
* @param Document $topic
* @throws Exception
*/
protected function deleteSubscribers(Document $project, Document $topic)
{
if ($topic->isEmpty()) {
Console::error('Failed to delete subscribers. Topic not found');
return;
}
$dbForProject = $this->getProjectDB($project);
$this->deleteByGroup('subscribers', [
Query::equal('topicInternalId', [$topic->getInternalId()])
], $dbForProject);
}
/**
* @param Document $project
* @param string $resource
+189 -36
View File
@@ -1,17 +1,30 @@
<?php
use Appwrite\Resque\Worker;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\DSN\DSN;
use Utopia\Messaging\Adapter;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Messaging\Adapters\SMS as SMSAdapter;
use Utopia\Messaging\Adapters\SMS\Mock;
use Utopia\Messaging\Adapters\SMS\Msg91;
use Utopia\Messaging\Adapters\SMS\Telesign;
use Utopia\Messaging\Adapters\SMS\TextMagic;
use Utopia\Messaging\Adapters\SMS\Twilio;
use Utopia\Messaging\Adapters\SMS\Vonage;
use Utopia\Messaging\Adapters\Push as PushAdapter;
use Utopia\Messaging\Adapters\Push\APNS;
use Utopia\Messaging\Adapters\Push\FCM;
use Utopia\Messaging\Adapters\Email as EmailAdapter;
use Utopia\Messaging\Adapters\Email\Mailgun;
use Utopia\Messaging\Adapters\Email\SendGrid;
use Utopia\Messaging\Messages\Email;
use Utopia\Messaging\Messages\Push;
use Utopia\Messaging\Messages\SMS;
use Appwrite\Extend\Exception;
use function Swoole\Coroutine\batch;
require_once __DIR__ . '/../init.php';
@@ -20,59 +33,199 @@ Console::success(APP_NAME . ' messaging worker v1 has started' . "\n");
class MessagingV1 extends Worker
{
protected ?Adapter $sms = null;
protected ?string $from = null;
protected ?SMSAdapter $sms = null;
protected ?PushAdapter $push = null;
protected ?EmailAdapter $email = null;
protected ?Database $dbForProject = null;
public function getName(): string
{
return "mails";
return "messaging";
}
public function init(): void
{
$dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
$user = $dsn->getUser();
$secret = $dsn->getPassword();
$this->sms = match ($dsn->getHost()) {
'mock' => new Mock($user, $secret), // used for tests
'twilio' => new Twilio($user, $secret),
'text-magic' => new TextMagic($user, $secret),
'telesign' => new Telesign($user, $secret),
'msg91' => new Msg91($user, $secret),
'vonage' => new Vonage($user, $secret),
default => null
};
$this->from = App::getEnv('_APP_SMS_FROM');
}
public function run(): void
{
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
Console::info('Skipped sms processing. No Phone provider has been set.');
return;
$project = new Document($this->args['project']);
$this->dbForProject = $this->getProjectDB($project);
$message = $this->dbForProject->getDocument('messages', $this->args['messageId']);
$provider = $this->dbForProject->getDocument('providers', $message->getAttribute('providerId'));
$this->processMessage($message, $provider);
}
private function processMessage(Document $message, Document $provider): void
{
$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)
};
$recipientsId = $message->getAttribute('to');
/**
* @var Document[] $recipients
*/
$recipients = [];
$topics = $this->dbForProject->find('topics', [Query::equal('$id', $recipientsId)]);
foreach ($topics as $topic) {
$recipients = \array_merge($recipients, $topic->getAttribute('targets'));
}
if (empty($this->from)) {
Console::info('Skipped sms processing. No phone number has been set.');
return;
$users = $this->dbForProject->find('users', [Query::equal('$id', $recipientsId)]);
foreach ($users as $user) {
$recipients = \array_merge($recipients, $user->getAttribute('targets'));
}
$message = new SMS(
to: [$this->args['recipient']],
content: $this->args['message'],
from: $this->from,
);
$targets = $this->dbForProject->find('targets', [Query::equal('$id', $recipientsId)]);
$recipients = \array_merge($recipients, $targets);
$recipients = \array_filter($recipients, function (Document $recipient) use ($provider) {
return $recipient->getAttribute('providerId') === $provider->getId();
});
try {
$this->sms->send($message);
} catch (\Exception $error) {
throw new Exception('Error sending message: ' . $error->getMessage(), 500);
$identifiers = \array_map(function (Document $recipient) {
return $recipient->getAttribute('identifier');
}, $recipients);
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
$batches = \array_chunk($identifiers, $maxBatchSize);
$batchIndex = 0;
$results = batch(\array_map(function ($batch) use ($message, $provider, $adapter, $batchIndex) {
return function () use ($batch, $message, $provider, $adapter, $batchIndex) {
$deliveredTo = 0;
$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);
$deliveredTo += \count($batch);
} catch (\Exception $e) {
$deliveryErrors[] = 'Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage();
} finally {
$batchIndex++;
return [
'deliveredTo' => $deliveredTo,
'deliveryErrors' => $deliveryErrors,
];
}
};
}, $batches));
$deliveredTo = 0;
$deliveryErrors = [];
foreach ($results as $result) {
$deliveredTo += $result['deliveredTo'];
$deliveryErrors = \array_merge($deliveryErrors, $result['deliveryErrors']);
}
$message->setAttribute('deliveryErrors', $deliveryErrors);
if (\count($message->getAttribute('deliveryErrors')) > 0) {
$message->setAttribute('status', 'failed');
} else {
$message->setAttribute('status', 'sent');
}
$message->setAttribute('to', $recipientsId);
$message->setAttribute('deliveredTo', $deliveredTo);
$message->setAttribute('deliveredAt', DateTime::now());
$this->dbForProject->updateDocument('messages', $message->getId(), $message);
}
public function shutdown(): void
{
}
private function sms(Document $provider): ?SMSAdapter
{
$credentials = $provider->getAttribute('credentials');
return match ($provider->getAttribute('provider')) {
'mock' => new Mock('username', 'password'),
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
'text-magic' => new TextMagic($credentials['username'], $credentials['apiKey']),
'telesign' => new Telesign($credentials['username'], $credentials['password']),
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey']),
'vonage' => new Vonage($credentials['apiKey'], $credentials['apiSecret']),
default => null
};
}
private function push(Document $provider): ?PushAdapter
{
$credentials = $provider->getAttribute('credentials');
return match ($provider->getAttribute('provider')) {
'apns' => new APNS(
$credentials['authKey'],
$credentials['authKeyId'],
$credentials['teamId'],
$credentials['bundleId'],
$credentials['endpoint']
),
'fcm' => new FCM($credentials['serverKey']),
default => null
};
}
private function email(Document $provider): ?EmailAdapter
{
$credentials = $provider->getAttribute('credentials');
return match ($provider->getAttribute('provider')) {
'mailgun' => new Mailgun($credentials['apiKey'], $credentials['domain'], $credentials['isEuRegion']),
'sendgrid' => new SendGrid($credentials['apiKey']),
default => null
};
}
private function buildEmailMessage(Document $message, Document $provider): Email
{
$from = $provider['options']['from'];
$to = $message['to'];
$subject = $message['data']['subject'];
$content = $message['data']['content'];
$html = $message['data']['html'];
return new Email($to, $subject, $content, $from, null, $html);
}
private function buildSMSMessage(Document $message, Document $provider): SMS
{
$to = $message['to'];
$content = $message['data']['content'];
$from = $provider['options']['from'];
return new SMS($to, $content, $from);
}
private function buildPushMessage(Document $message): Push
{
$to = $message['to'];
$title = $message['data']['title'];
$body = $message['data']['body'];
$data = $message['data']['data'];
$action = $message['data']['action'];
$sound = $message['data']['sound'];
$icon = $message['data']['icon'];
$color = $message['data']['color'];
$tag = $message['data']['tag'];
$badge = $message['data']['badge'];
return new Push($to, $title, $body, $data, $action, $sound, $icon, $color, $tag, $badge);
}
}
+10
View File
@@ -0,0 +1,10 @@
#!/bin/sh
if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ]
then
export REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
else
export REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
fi
INTERVAL=1 php /usr/src/code/vendor/bin/resque-scheduler -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php
+4 -4
View File
@@ -43,20 +43,20 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.13.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.31.*",
"utopia-php/abuse": "0.32.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.33.*",
"utopia-php/audit": "0.34.*",
"utopia-php/cache": "0.8.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.43.*",
"utopia-php/database": "0.44.*",
"utopia-php/domains": "0.3.*",
"utopia-php/dsn": "0.1.*",
"utopia-php/framework": "0.31.0",
"utopia-php/image": "0.5.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.3.*",
"utopia-php/messaging": "0.1.*",
"utopia-php/messaging": "0.2.*",
"utopia-php/migration": "0.3.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.4.*",
Generated
+37 -37
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "13a3bdc7c1dec5756bf58ec73a49753d",
"content-hash": "ee4518740e581a9a4889936fb584a5a4",
"packages": [
{
"name": "adhocore/jwt",
@@ -156,11 +156,11 @@
},
{
"name": "appwrite/php-runtimes",
"version": "0.13.0",
"version": "0.13.1",
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "5ab496b3908992b39275994a23783701c4b3de84"
"reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610"
},
"require": {
"php": ">=8.0",
@@ -195,7 +195,7 @@
"php",
"runtimes"
],
"time": "2023-09-12T19:38:43+00:00"
"time": "2023-10-16T15:39:53+00:00"
},
{
"name": "chillerlan/php-qrcode",
@@ -1050,16 +1050,16 @@
},
{
"name": "matomo/device-detector",
"version": "6.1.5",
"version": "6.1.6",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
"reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4"
"reference": "5cbea85106e561c7138d03603eb6e05128480409"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/40ca2990dba2c1719e5c62168e822e0b86c167d4",
"reference": "40ca2990dba2c1719e5c62168e822e0b86c167d4",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/5cbea85106e561c7138d03603eb6e05128480409",
"reference": "5cbea85106e561c7138d03603eb6e05128480409",
"shasum": ""
},
"require": {
@@ -1115,7 +1115,7 @@
"source": "https://github.com/matomo-org/matomo",
"wiki": "https://dev.matomo.org/"
},
"time": "2023-08-17T16:17:41+00:00"
"time": "2023-10-02T10:01:54+00:00"
},
{
"name": "mongodb/mongodb",
@@ -1861,23 +1861,23 @@
},
{
"name": "utopia-php/abuse",
"version": "0.31.1",
"version": "0.32.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd"
"reference": "9717ffb2d7711f3fd621bb6df3edf5724c08ea78"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/b2ad372d1070f55f9545cb811b6ed2d40094e6dd",
"reference": "b2ad372d1070f55f9545cb811b6ed2d40094e6dd",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/9717ffb2d7711f3fd621bb6df3edf5724c08ea78",
"reference": "9717ffb2d7711f3fd621bb6df3edf5724c08ea78",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/database": "0.43.*"
"utopia-php/database": "0.44.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1904,9 +1904,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.31.1"
"source": "https://github.com/utopia-php/abuse/tree/0.32.0"
},
"time": "2023-08-29T11:07:46+00:00"
"time": "2023-10-18T07:28:55+00:00"
},
{
"name": "utopia-php/analytics",
@@ -1956,21 +1956,21 @@
},
{
"name": "utopia-php/audit",
"version": "0.33.1",
"version": "0.34.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd"
"reference": "cf34cc3f9f20da4e574a9be4517e1a11025a858f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd",
"reference": "c117e8e9ce4e3e1b369e8b5b55b2d6ab3138eadd",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/cf34cc3f9f20da4e574a9be4517e1a11025a858f",
"reference": "cf34cc3f9f20da4e574a9be4517e1a11025a858f",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.43.*"
"utopia-php/database": "0.44.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1997,9 +1997,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.33.1"
"source": "https://github.com/utopia-php/audit/tree/0.34.0"
},
"time": "2023-08-29T11:07:40+00:00"
"time": "2023-10-18T07:43:25+00:00"
},
{
"name": "utopia-php/cache",
@@ -2152,16 +2152,16 @@
},
{
"name": "utopia-php/database",
"version": "0.43.4",
"version": "0.44.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c"
"reference": "591cadbc2c622a3304aae9a16ebfdbe75ef33a06"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c",
"reference": "cabdd02e8dc1732eb0b22007c511e7bb3caa5c8c",
"url": "https://api.github.com/repos/utopia-php/database/zipball/591cadbc2c622a3304aae9a16ebfdbe75ef33a06",
"reference": "591cadbc2c622a3304aae9a16ebfdbe75ef33a06",
"shasum": ""
},
"require": {
@@ -2202,9 +2202,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.43.4"
"source": "https://github.com/utopia-php/database/tree/0.44.2"
},
"time": "2023-09-28T09:00:05+00:00"
"time": "2023-10-19T07:39:00+00:00"
},
{
"name": "utopia-php/domains",
@@ -2516,16 +2516,16 @@
},
{
"name": "utopia-php/messaging",
"version": "0.1.1",
"version": "0.2.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "a75d66ddd59b834ab500a4878a2c084e6572604a"
"reference": "2d0f474a106bb1da285f85e105c29b46085d3a43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a",
"reference": "a75d66ddd59b834ab500a4878a2c084e6572604a",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/2d0f474a106bb1da285f85e105c29b46085d3a43",
"reference": "2d0f474a106bb1da285f85e105c29b46085d3a43",
"shasum": ""
},
"require": {
@@ -2534,8 +2534,8 @@
},
"require-dev": {
"laravel/pint": "^1.2",
"phpmailer/phpmailer": "6.6.*",
"phpunit/phpunit": "9.5.*"
"phpmailer/phpmailer": "6.8.*",
"phpunit/phpunit": "9.6.*"
},
"type": "library",
"autoload": {
@@ -2558,9 +2558,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.1.1"
"source": "https://github.com/utopia-php/messaging/tree/0.2.0"
},
"time": "2023-02-07T05:42:46+00:00"
"time": "2023-09-14T20:48:42+00:00"
},
{
"name": "utopia-php/migration",
+39 -3
View File
@@ -188,7 +188,17 @@ services:
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_ASSISTANT_OPENAI_API_KEY
- _APP_MESSAGE_SMS_PROVIDER_MSG91_SENDER_ID
- _APP_MESSAGE_SMS_PROVIDER_MSG91_AUTH_KEY
- _APP_MESSAGE_SMS_PROVIDER_MSG91_FROM
- _APP_MESSAGE_SMS_PROVIDER_MSG91_TO
- _APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_API_KEY
- _APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_DOMAIN
- _APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_FROM
- _APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_RECEIVER_EMAIL
- _APP_MESSAGE_EMAIL_PROVIDER_MAILGUN_IS_EU_REGION
- _APP_MESSAGE_PUSH_PROVIDER_FCM_SERVERY_KEY
- _APP_MESSAGE_PUSH_PROVIDER_FCM_RECEIVER_TOKEN
appwrite-realtime:
entrypoint: realtime
<<: *x-logging
@@ -571,8 +581,34 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_SMS_PROVIDER
- _APP_SMS_FROM
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-resque-scheduler:
entrypoint: resque-scheduler
<<: *x-logging
container_name: appwrite-resque-scheduler
restart: unless-stopped
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
+3 -3
View File
@@ -37,7 +37,7 @@ Finally, you will need to create a `feat-XXX-YYY-oauth` branch based on the `mas
The first step in adding a new OAuth2 provider is to add it to the list of providers located at:
```
app/config/providers.php
app/config/authProviders.php
```
Make sure to fill in all data needed and that your provider array key name:
@@ -45,7 +45,7 @@ Make sure to fill in all data needed and that your provider array key name:
- is in [`camelCase`](https://en.wikipedia.org/wiki/Camel_case) format for sentence, but lowercase for names. `github` must be all lowercased, but `paypalSandbox` should have uppercase S
- has no spaces or special characters
> Please make sure to keep the list of providers in `providers.php` in the alphabetical order A-Z.
> Please make sure to keep the list of providers in `authProviders.php` in the alphabetical order A-Z.
### 2.2 Add Provider Logo
@@ -199,7 +199,7 @@ If you need any help with the contribution, feel free to head over to [our Disco
If your OAuth provider requires special configuration apart from `clientId` and `clientSecret` you can create a custom form. Currently this is being realized through putting all custom fields as JSON into the `clientSecret` field to keep the project API stable. You can implement your custom form following these steps:
1. Add your custom form in `app/views/console/users/oauth/[PROVIDER].phtml`. Below is a template you can use. Add the filename to `app/config/providers.php`.
1. Add your custom form in `app/views/console/users/oauth/[PROVIDER].phtml`. Below is a template you can use. Add the filename to `app/config/authProviders.php`.
```php
<?php
+8
View File
@@ -0,0 +1,8 @@
{
"private": true,
"name": "@appwrite.io/repo",
"repository": {
"type": "git",
"url": "git+https://github.com/appwrite/appwrite.git"
}
}
+1
View File
@@ -32,6 +32,7 @@
<directory>./tests/e2e/Services/Teams</directory>
<directory>./tests/e2e/Services/Users</directory>
<directory>./tests/e2e/Services/Webhooks</directory>
<directory>./tests/e2e/Services/Messaging</directory>
<file>./tests/e2e/Services/Functions/FunctionsBase.php</file>
<file>./tests/e2e/Services/Functions/FunctionsCustomServerTest.php</file>
<file>./tests/e2e/Services/Functions/FunctionsCustomClientTest.php</file>
+107
View File
@@ -0,0 +1,107 @@
<?php
namespace Appwrite\Event;
use Resque;
use ResqueScheduler;
use Utopia\Database\Document;
class Messaging extends Event
{
protected ?string $messageId = null;
private ?string $deliveryTime = null;
public function __construct()
{
parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME);
}
/**
* Sets message ID for the messaging event.
*
* @param string $message
* @return self
*/
public function setMessageId(string $messageId): self
{
$this->messageId = $messageId;
return $this;
}
/**
* Returns set message ID for the messaging event.
*
* @return string
*/
public function getMessageId(): string
{
return $this->messageId;
}
/**
* Sets Delivery time for the messaging event.
*
* @param string $deliveryTime
* @return self
*/
public function setDeliveryTime(string $deliveryTime): self
{
$this->deliveryTime = $deliveryTime;
return $this;
}
/**
* Returns set Delivery Time for the messaging event.
*
* @return string
*/
public function getDeliveryTime(): string
{
return $this->deliveryTime;
}
/**
* Set project for this event.
*
* @param Document $project
* @return self
*/
public function setProject(Document $project): self
{
$this->project = $project;
return $this;
}
/**
* Executes the event and sends it to the messaging worker.
* @return string|bool
* @throws \InvalidArgumentException
*/
public function trigger(): string | bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->project,
'user' => $this->user,
'messageId' => $this->messageId,
]);
}
/**
* Schedules the messaging event and schedules it in the messaging worker queue.
*
* @return void
* @throws \Resque_Exception
* @throws \ResqueScheduler_InvalidTimestampException
*/
public function schedule(): void
{
ResqueScheduler::enqueueAt(new \DateTime($this->deliveryTime, new \DateTimeZone('UTC')), $this->queue, $this->class, [
'project' => $this->project,
'user' => $this->user,
'messageId' => $this->messageId,
]);
}
}
-80
View File
@@ -1,80 +0,0 @@
<?php
namespace Appwrite\Event;
use Resque;
class Phone extends Event
{
protected string $recipient = '';
protected string $message = '';
public function __construct()
{
parent::__construct(Event::MESSAGING_QUEUE_NAME, Event::MESSAGING_CLASS_NAME);
}
/**
* Sets recipient for the messaging event.
*
* @param string $recipient
* @return self
*/
public function setRecipient(string $recipient): self
{
$this->recipient = $recipient;
return $this;
}
/**
* Returns set recipient for this messaging event.
*
* @return string
*/
public function getRecipient(): string
{
return $this->recipient;
}
/**
* Sets url for the messaging event.
*
* @param string $message
* @return self
*/
public function setMessage(string $message): self
{
$this->message = $message;
return $this;
}
/**
* Returns set url for the messaging event.
*
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
/**
* Executes the event and sends it to the messaging worker.
*
* @return string|bool
* @throws \InvalidArgumentException
*/
public function trigger(): string|bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->project,
'user' => $this->user,
'payload' => $this->payload,
'recipient' => $this->recipient,
'message' => $this->message,
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
]);
}
}
+19 -1
View File
@@ -84,7 +84,8 @@ class Exception extends \Exception
public const USER_OAUTH2_BAD_REQUEST = 'user_oauth2_bad_request';
public const USER_OAUTH2_UNAUTHORIZED = 'user_oauth2_unauthorized';
public const USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error';
public const USER_TARGET_NOT_FOUND = 'user_target_not_found';
public const USER_TARGET_ALREADY_EXISTS = 'user_target_already_exists';
/** Teams */
public const TEAM_NOT_FOUND = 'team_not_found';
public const TEAM_INVITE_ALREADY_EXISTS = 'team_invite_already_exists';
@@ -224,6 +225,23 @@ class Exception extends \Exception
public const MIGRATION_ALREADY_EXISTS = 'migration_already_exists';
public const MIGRATION_IN_PROGRESS = 'migration_in_progress';
/** Provider */
public const PROVIDER_NOT_FOUND = 'provider_not_found';
public const PROVIDER_ALREADY_EXISTS = 'provider_already_exists';
public const PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type';
/** Topic */
public const TOPIC_NOT_FOUND = 'topic_not_found';
public const TOPIC_ALREADY_EXISTS = 'topic_already_exists';
/** Subscriber */
public const SUBSCRIBER_NOT_FOUND = 'subscriber_not_found';
public const SUBSCRIBER_ALREADY_EXISTS = 'subscriber_already_exists';
/** Message */
public const MESSAGE_NOT_FOUND = 'message_not_found';
public const MESSAGE_ALREADY_SENT = 'message_already_sent';
protected $type = '';
protected $errors = [];
+1 -1
View File
@@ -34,7 +34,7 @@ class V15 extends Migration
['email', 'anonymous'],
\array_map(
fn ($value) => "oauth-" . $value,
\array_keys(Config::getParam('providers', []))
\array_keys(Config::getParam('authProviders', []))
)
);
+1 -1
View File
@@ -126,7 +126,7 @@ class V16 extends Migration
*/
$authProviders = $document->getAttribute('authProviders', []);
foreach (Config::getParam('providers') as $provider => $value) {
foreach (Config::getParam('authProviders') as $provider => $value) {
if (!$value['enabled']) {
continue;
}
@@ -0,0 +1,27 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Messages extends Base
{
public const ALLOWED_ATTRIBUTES = [
'to',
'providerId',
'deliveredAt',
'deliveredTo',
'deliveryErrors',
'status',
'description',
'data',
'search'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('messages', self::ALLOWED_ATTRIBUTES);
}
}
@@ -0,0 +1,23 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Providers extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'provider',
'type',
'default',
'enabled',
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('providers', self::ALLOWED_ATTRIBUTES);
}
}
@@ -0,0 +1,20 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Subscribers extends Base
{
public const ALLOWED_ATTRIBUTES = [
'targetId',
'topicId'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('subscribers', self::ALLOWED_ATTRIBUTES);
}
}
@@ -0,0 +1,20 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Topics extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'description',
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('topics', self::ALLOWED_ATTRIBUTES);
}
}
+31 -4
View File
@@ -31,6 +31,7 @@ use Appwrite\Utopia\Response\Model\AttributeIP;
use Appwrite\Utopia\Response\Model\AttributeURL;
use Appwrite\Utopia\Response\Model\AttributeDatetime;
use Appwrite\Utopia\Response\Model\AttributeRelationship;
use Appwrite\Utopia\Response\Model\AuthProvider;
use Appwrite\Utopia\Response\Model\BaseList;
use Appwrite\Utopia\Response\Model\Branch;
use Appwrite\Utopia\Response\Model\Collection;
@@ -80,8 +81,12 @@ use Appwrite\Utopia\Response\Model\HealthVersion;
use Appwrite\Utopia\Response\Model\Installation;
use Appwrite\Utopia\Response\Model\LocaleCode;
use Appwrite\Utopia\Response\Model\Provider;
use Appwrite\Utopia\Response\Model\Message;
use Appwrite\Utopia\Response\Model\Subscriber;
use Appwrite\Utopia\Response\Model\Topic;
use Appwrite\Utopia\Response\Model\ProviderRepository;
use Appwrite\Utopia\Response\Model\Runtime;
use Appwrite\Utopia\Response\Model\Target;
use Appwrite\Utopia\Response\Model\TemplateSMS;
use Appwrite\Utopia\Response\Model\UsageBuckets;
use Appwrite\Utopia\Response\Model\UsageCollection;
@@ -191,6 +196,18 @@ class Response extends SwooleResponse
public const MODEL_PHONE = 'phone';
public const MODEL_PHONE_LIST = 'phoneList';
// Messaging
public const MODEL_PROVIDER = 'provider';
public const MODEL_PROVIDER_LIST = 'providerList';
public const MODEL_MESSAGE = 'message';
public const MODEL_MESSAGE_LIST = 'messageList';
public const MODEL_TOPIC = 'topic';
public const MODEL_TOPIC_LIST = 'topicList';
public const MODEL_SUBSCRIBER = 'subscriber';
public const MODEL_SUBSCRIBER_LIST = 'subscriberList';
public const MODEL_TARGET = 'target';
public const MODEL_TARGET_LIST = 'targetList';
// Teams
public const MODEL_TEAM = 'team';
public const MODEL_TEAM_LIST = 'teamList';
@@ -238,8 +255,8 @@ class Response extends SwooleResponse
public const MODEL_WEBHOOK_LIST = 'webhookList';
public const MODEL_KEY = 'key';
public const MODEL_KEY_LIST = 'keyList';
public const MODEL_PROVIDER = 'provider';
public const MODEL_PROVIDER_LIST = 'providerList';
public const MODEL_AUTH_PROVIDER = 'authProvider';
public const MODEL_AUTH_PROVIDER_LIST = 'authProviderList';
public const MODEL_PLATFORM = 'platform';
public const MODEL_PLATFORM_LIST = 'platformList';
public const MODEL_VARIABLE = 'variable';
@@ -316,7 +333,7 @@ class Response extends SwooleResponse
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false))
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false))
->setModel(new BaseList('Providers List', self::MODEL_PROVIDER_LIST, 'platforms', self::MODEL_PROVIDER, true, false))
->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false))
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false))
->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY))
->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT))
@@ -328,6 +345,11 @@ class Response extends SwooleResponse
->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS))
->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE))
->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE))
->setModel(new BaseList('Provider list', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER))
->setModel(new BaseList('Message list', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE))
->setModel(new BaseList('Topic list', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC))
->setModel(new BaseList('Subscriber list', self::MODEL_SUBSCRIBER_LIST, 'subscribers', self::MODEL_SUBSCRIBER))
->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET))
->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION))
->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT))
// Entities
@@ -380,7 +402,7 @@ class Response extends SwooleResponse
->setModel(new Project())
->setModel(new Webhook())
->setModel(new Key())
->setModel(new Provider())
->setModel(new AuthProvider())
->setModel(new Platform())
->setModel(new Variable())
->setModel(new Country())
@@ -408,6 +430,11 @@ class Response extends SwooleResponse
->setModel(new TemplateSMS())
->setModel(new TemplateEmail())
->setModel(new ConsoleVariables())
->setModel(new Provider())
->setModel(new Message())
->setModel(new Topic())
->setModel(new Subscriber())
->setModel(new Target())
->setModel(new Migration())
->setModel(new MigrationReport())
->setModel(new MigrationFirebaseProject())
+3 -3
View File
@@ -88,9 +88,9 @@ class V16 extends Filter
protected function parseProject(array $content)
{
foreach ($content['providers'] ?? [] as $i => $provider) {
$content['providers'][$i]['name'] = \ucfirst($provider['key']);
unset($content['providers'][$i]['key']);
foreach ($content['authProviders'] ?? [] as $i => $provider) {
$content['authProviders'][$i]['name'] = \ucfirst($provider['key']);
unset($content['authProviders'][$i]['key']);
}
$content['domains'] = [];
@@ -0,0 +1,69 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class AuthProvider extends Model
{
/**
* @var bool
*/
protected bool $public = false;
public function __construct()
{
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Auth Provider.',
'default' => '',
'example' => 'github',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Auth Provider name.',
'default' => '',
'example' => 'GitHub',
])
->addRule('appId', [
'type' => self::TYPE_STRING,
'description' => 'OAuth 2.0 application ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('secret', [
'type' => self::TYPE_STRING,
'description' => 'OAuth 2.0 application secret. Might be JSON string if provider requires extra configuration.',
'default' => '',
'example' => 'Bpw_g9c2TGXxfgLshDbSaL8tsCcqgczQ',
])
->addRule('enabled', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Auth Provider is active and can be used to create session.',
'example' => '',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'AuthProvider';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_AUTH_PROVIDER;
}
}
@@ -0,0 +1,104 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use Utopia\Database\DateTime;
class Message extends Any
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Message ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('providerId', [
'type' => self::TYPE_STRING,
'description' => 'Provider ID for the message.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('to', [
'type' => self::TYPE_STRING,
'description' => 'Message recipients.',
'default' => '',
'array' => true,
'example' => ['user-1'],
])
->addRule('deliveryTime', [
'type' => self::TYPE_DATETIME,
'description' => 'The scheduled time for message.',
'required' => false,
'default' => DateTime::now(),
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('deliveredAt', [
'type' => self::TYPE_DATETIME,
'description' => 'The time when the message was delivered.',
'required' => false,
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('deliveryErrors', [
'type' => self::TYPE_STRING,
'description' => 'Delivery errors if any.',
'required' => false,
'default' => '',
'array' => true,
'example' => ['Failed to send message to target 5e5ea5c16897e: Credentials not valid.'],
])
->addRule('deliveredTo', [
'type' => self::TYPE_INTEGER,
'description' => 'Number of recipients the message was delivered to.',
'default' => 0,
'example' => 1,
])
->addRule('data', [
'type' => self::TYPE_JSON,
'description' => 'Data of the message.',
'default' => [],
'example' => [
'subject' => 'Welcome to Appwrite',
'content' => 'Hi there, welcome to Appwrite family.',
],
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Status of delivery.',
'default' => 'processing',
'example' => 'Message status can be one of the following: processing, sent, failed.',
])
->addRule('description', [
'type' => self::TYPE_STRING,
'description' => 'Message description.',
'required' => false,
'default' => '',
'example' => 'Welcome Email.',
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Message';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_MESSAGE;
}
}
@@ -138,9 +138,9 @@ class Project extends Model
'default' => false,
'example' => true,
])
->addRule('providers', [
'type' => Response::MODEL_PROVIDER,
'description' => 'List of Providers.',
->addRule('authProviders', [
'type' => Response::MODEL_AUTH_PROVIDER,
'description' => 'List of Auth Providers.',
'default' => [],
'example' => [new \stdClass()],
'array' => true,
@@ -329,7 +329,7 @@ class Project extends Model
}
// Providers
$providers = Config::getParam('providers', []);
$providers = Config::getParam('authProviders', []);
$providerValues = $document->getAttribute('authProviders', []);
$projectProviders = [];
@@ -348,7 +348,7 @@ class Project extends Model
]);
}
$document->setAttribute("providers", $projectProviders);
$document->setAttribute('authProviders', $projectProviders);
return $document;
}
+39 -21
View File
@@ -7,44 +7,62 @@ use Appwrite\Utopia\Response\Model;
class Provider extends Model
{
/**
* @var bool
*/
protected bool $public = false;
public function __construct()
{
$this
->addRule('key', [
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Provider.',
'description' => 'Provider ID.',
'default' => '',
'example' => 'github',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Provider name.',
'description' => 'The name for the provider instance.',
'default' => '',
'example' => 'GitHub',
'example' => 'Mailgun',
])
->addRule('appId', [
->addRule('provider', [
'type' => self::TYPE_STRING,
'description' => 'OAuth 2.0 application ID.',
'description' => 'The name of the provider service.',
'default' => '',
'example' => '259125845563242502',
'example' => 'mailgun',
])
->addRule('secret', [
'type' => self::TYPE_STRING,
'description' => 'OAuth 2.0 application secret. Might be JSON string if provider requires extra configuration.',
'default' => '',
'example' => 'Bpw_g9c2TGXxfgLshDbSaL8tsCcqgczQ',
->addRule('default', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is this a pre-configured provider instance?',
'default' => false,
'example' => true,
])
->addRule('enabled', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Provider is active and can be used to create session.',
'example' => '',
'description' => 'Is provider enabled?',
'default' => true,
'example' => true,
])
;
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Type of provider.',
'default' => '',
'example' => 'sms',
])
->addRule('credentials', [
'type' => self::TYPE_JSON,
'description' => 'Provider credentials.',
'default' => [],
'example' => [
'key' => '123456789'
],
])
->addRule('options', [
'type' => self::TYPE_JSON,
'description' => 'Provider options.',
'default' => [],
'required' => false,
'example' => [
'from' => 'sender-email@mydomain'
],
]);
}
/**
@@ -0,0 +1,52 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Subscriber extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Subscriber ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('targetId', [
'type' => self::TYPE_STRING,
'description' => 'Target ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('topicId', [
'type' => self::TYPE_STRING,
'description' => 'Topic ID.',
'default' => '',
'example' => '259125845563242502',
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Subscriber';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_SUBSCRIBER;
}
}
@@ -0,0 +1,59 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Target extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Target ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('userId', [
'type' => self::TYPE_STRING,
'description' => 'User ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('providerId', [
'type' => self::TYPE_STRING,
'description' => 'Provider ID.',
'required' => false,
'default' => '',
'example' => '259125845563242502',
])
->addRule('identifier', [
'type' => self::TYPE_STRING,
'description' => 'The target identifier.',
'default' => '',
'example' => 'token',
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Target';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_TARGET;
}
}
@@ -0,0 +1,59 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Topic extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Topic ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('providerId', [
'type' => self::TYPE_STRING,
'description' => 'Provider ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'The name of the topic.',
'default' => '',
'example' => 'events',
])
->addRule('description', [
'type' => self::TYPE_STRING,
'description' => 'Description of the topic.',
'default' => '',
'required' => false,
'example' => 'All events related messages will be sent to this topic.',
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Topic';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_TOPIC;
}
}
@@ -120,6 +120,13 @@ class User extends Model
'default' => new \stdClass(),
'example' => ['theme' => 'pink', 'timezone' => 'UTC'],
])
->addRule('targets', [
'type' => Response::MODEL_TARGET,
'description' => 'A user-owned message receiver. A single user may have multiple e.g. emails, phones, and a browser. Each target is registered with a single provider.',
'default' => [],
'array' => true,
'example' => [],
])
->addRule('accessedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_USER_ACCCESS / 60 / 60 . ' hours.',
+10
View File
@@ -81,6 +81,16 @@ trait ProjectCustom
'locale.read',
'avatars.read',
'health.read',
'targets.read',
'targets.write',
'providers.read',
'providers.write',
'messages.read',
'messages.write',
'topics.write',
'topics.read',
'subscribers.write',
'subscribers.read'
],
]);
@@ -2,13 +2,12 @@
namespace Tests\E2E\Services\Account;
use Appwrite\Extend\Exception;
use Appwrite\SMS\Adapter\Mock;
use Appwrite\Tests\Retry;
use Tests\E2E\Client;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\SideClient;
use Utopia\App;
use Utopia\Database\DateTime;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
@@ -744,8 +743,29 @@ class AccountCustomClientTest extends Scope
public function testCreatePhone(): array
{
$number = '+123456789';
$to = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_TO');
$from = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_FROM');
$authKey = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_AUTH_KEY');
$senderId = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_SENDER_ID');
if (empty($to) || empty($from) || empty($authKey) || empty($senderId)) {
$this->markTestSkipped('SMS provider not configured');
}
$number = $to;
$response = $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' => 'Sms provider',
'senderId' => $senderId,
'authKey' => $authKey,
'default' => true,
'from' => $from,
]);
$this->assertEquals(201, $response['headers']['status-code']);
/**
* Test for SUCCESS
*/
@@ -756,6 +776,7 @@ class AccountCustomClientTest extends Scope
]), [
'userId' => ID::unique(),
'phone' => $number,
'from' => $from,
]);
$this->assertEquals(201, $response['headers']['status-code']);
@@ -764,6 +785,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
$userId = $response['body']['userId'];
$messageId = $response['body']['$id'];
/**
* Test for FAILURE
@@ -780,17 +802,19 @@ class AccountCustomClientTest extends Scope
\sleep(5);
$smsRequest = $this->getLastRequest();
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $messageId, [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals('http://request-catcher:5000/mock-sms', $smsRequest['url']);
$this->assertEquals('Appwrite Mock Message Sender', $smsRequest['headers']['User-Agent']);
$this->assertEquals('username', $smsRequest['headers']['X-Username']);
$this->assertEquals('password', $smsRequest['headers']['X-Key']);
$this->assertEquals('POST', $smsRequest['method']);
$this->assertEquals('+123456789', $smsRequest['data']['from']);
$this->assertEquals($number, $smsRequest['data']['to']);
$this->assertEquals(200, $message['headers']['status-code']);
$this->assertEquals(1, $message['body']['deliveredTo']);
$this->assertEquals(0, \count($message['body']['deliveryErrors']));
$data['token'] = $smsRequest['data']['message'];
$data['token'] = $message['body']['data']['content'];
$data['id'] = $userId;
$data['number'] = $number;
@@ -999,19 +1023,28 @@ class AccountCustomClientTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
]), ['from' => App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_FROM')]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEmpty($response['body']['secret']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
\sleep(2);
\sleep(3);
$smsRequest = $this->getLastRequest();
$message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $response['body']['$id'], [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $message['headers']['status-code']);
$this->assertEquals(1, $message['body']['deliveredTo']);
$this->assertEquals(0, \count($message['body']['deliveryErrors']));
return \array_merge($data, [
'token' => $smsRequest['data']['message']
'token' => $message['body']['data']['content']
]);
}
@@ -6,6 +6,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
use Utopia\App;
use Utopia\Database\Helpers\ID;
class AccountTest extends Scope
@@ -122,7 +123,35 @@ class AccountTest extends Scope
*/
public function testCreatePhoneVerification(): array
{
$to = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_TO');
$from = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_FROM');
$authKey = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_AUTH_KEY');
$senderId = App::getEnv('_APP_MESSAGE_SMS_PROVIDER_MSG91_SENDER_ID');
if (empty($to) || empty($from) || empty($authKey) || empty($senderId)) {
$this->markTestSkipped('SMS provider not configured');
}
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$CREATE_MSG91_PROVIDER);
$graphQLPayload = [
'query' => $query,
'variables' => [
'providerId' => ID::unique(),
'name' => 'Sms Provider',
'from' => $from,
'senderId' => $senderId,
'authKey' => $authKey,
'default' => true,
],
];
$response = $this->client->call(Client::METHOD_POST, '/graphql', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $this->getProject()['apiKey'],
], $graphQLPayload);
$query = $this->getQuery(self::$CREATE_PHONE_VERIFICATION);
$graphQLPayload = [
'query' => $query,
+519
View File
@@ -107,6 +107,11 @@ trait Base
public static string $DELETE_USER_SESSIONS = 'delete_user_sessions';
public static string $DELETE_USER_SESSION = 'delete_user_session';
public static string $DELETE_USER = 'delete_user';
public static string $CREATE_USER_TARGET = 'create_user_target';
public static string $LIST_USER_TARGETS = 'list_user_targets';
public static string $GET_USER_TARGET = 'get_user_target';
public static string $UPDATE_USER_TARGET = 'update_user_target';
public static string $DELETE_USER_TARGET = 'delete_user_target';
// Teams
public static string $GET_TEAM = 'get_team';
@@ -197,6 +202,53 @@ trait Base
public static string $GET_QRCODE = 'get_qrcode';
public static string $GET_USER_INITIALS = 'get_user_initials';
// Providers
public static string $CREATE_MAILGUN_PROVIDER = 'create_mailgun_provider';
public static string $CREATE_SENDGRID_PROVIDER = 'create_sendgrid_provider';
public static string $CREATE_TWILIO_PROVIDER = 'create_twilio_provider';
public static string $CREATE_TELESIGN_PROVIDER = 'create_telesign_provider';
public static string $CREATE_TEXTMAGIC_PROVIDER = 'create_textmagic_provider';
public static string $CREATE_MSG91_PROVIDER = 'create_msg91_provider';
public static string $CREATE_VONAGE_PROVIDER = 'create_vonage_provider';
public static string $CREATE_FCM_PROVIDER = 'create_fcm_provider';
public static string $CREATE_APNS_PROVIDER = 'create_apns_provider';
public static string $LIST_PROVIDERS = 'list_providers';
public static string $GET_PROVIDER = 'get_provider';
public static string $UPDATE_MAILGUN_PROVIDER = 'update_mailgun_provider';
public static string $UPDATE_SENDGRID_PROVIDER = 'update_sendgrid_provider';
public static string $UPDATE_TWILIO_PROVIDER = 'update_twilio_provider';
public static string $UPDATE_TELESIGN_PROVIDER = 'update_telesign_provider';
public static string $UPDATE_TEXTMAGIC_PROVIDER = 'update_textmagic_provider';
public static string $UPDATE_MSG91_PROVIDER = 'update_msg91_provider';
public static string $UPDATE_VONAGE_PROVIDER = 'update_vonage_provider';
public static string $UPDATE_FCM_PROVIDER = 'update_fcm_provider';
public static string $UPDATE_APNS_PROVIDER = 'update_apns_provider';
public static string $DELETE_PROVIDER = 'delete_provider';
// Topics
public static string $CREATE_TOPIC = 'create_topic';
public static string $LIST_TOPICS = 'list_topics';
public static string $GET_TOPIC = 'get_topic';
public static string $UPDATE_TOPIC = 'update_topic';
public static string $DELETE_TOPIC = 'delete_topic';
// Subscriptions
public static string $CREATE_SUBSCRIBER = 'create_subscriber';
public static string $LIST_SUBSCRIBERS = 'list_subscribers';
public static string $GET_SUBSCRIBER = 'get_subscriber';
public static string $DELETE_SUBSCRIBER = 'delete_subscriber';
// Messages
public static string $CREATE_EMAIL = 'create_email';
public static string $CREATE_SMS = 'create_sms';
public static string $CREATE_PUSH_NOTIFICATION = 'create_push_notification';
public static string $LIST_MESSAGES = 'list_messages';
public static string $GET_MESSAGE = 'get_message';
public static string $UPDATE_EMAIL = 'update_email';
public static string $UPDATE_SMS = 'update_sms';
public static string $UPDATE_PUSH_NOTIFICATION = 'update_push_notification';
// Complex queries
public static string $COMPLEX_QUERY = 'complex_query';
@@ -879,6 +931,51 @@ trait Base
status
}
}';
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) {
_id
userId
providerId
identifier
}
}';
case self::$LIST_USER_TARGETS:
return 'query listUserTargets($userId: String!) {
usersListTargets(userId: $userId) {
total
targets {
_id
userId
providerId
identifier
}
}
}';
case self::$GET_USER_TARGET:
return 'query getUserTarget($userId: String!, $targetId: String!) {
usersGetTarget(userId: $userId, targetId: $targetId) {
_id
userId
providerId
identifier
}
}';
case self::$UPDATE_USER_TARGET:
return 'mutation updateUserTarget($userId: String!, $targetId: String!, $identifier: String!){
usersUpdateTargetIdentifier(userId: $userId, targetId: $targetId, identifier: $identifier) {
_id
userId
providerId
identifier
}
}';
case self::$DELETE_USER_TARGET:
return 'mutation deleteUserTarget($userId: String!, $targetId: String!){
usersDeleteTarget(userId: $userId, targetId: $targetId) {
status
}
}';
case self::$GET_LOCALE:
return 'query getLocale {
localeGet {
@@ -1686,6 +1783,428 @@ trait Base
status
}
}';
case self::$CREATE_MAILGUN_PROVIDER:
return 'mutation createMailgunProvider($providerId: String!, $name: String!, $domain: String!, $apiKey: String!, $from: String!) {
messagingCreateMailgunProvider(providerId: $providerId, name: $name, domain: $domain, apiKey: $apiKey, from: $from) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_SENDGRID_PROVIDER:
return 'mutation createSendgridProvider($providerId: String!, $name: String!, $apiKey: String!) {
messagingCreateSendgridProvider(providerId: $providerId, name: $name, apiKey: $apiKey) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_TWILIO_PROVIDER:
return 'mutation createTwilioProvider($providerId: String!, $name: String!, $accountSid: String!, $authToken: String!) {
messagingCreateTwilioProvider(providerId: $providerId, name: $name, accountSid: $accountSid, authToken: $authToken) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_TELESIGN_PROVIDER:
return 'mutation createTelesignProvider($providerId: String!, $name: String!, $username: String!, $password: String!) {
messagingCreateTelesignProvider(providerId: $providerId, name: $name, username: $username, password: $password) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_TEXTMAGIC_PROVIDER:
return 'mutation createTextmagicProvider($providerId: String!, $name: String!, $username: String!, $apiKey: String!) {
messagingCreateTextmagicProvider(providerId: $providerId, name: $name, username: $username, apiKey: $apiKey) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_MSG91_PROVIDER:
return 'mutation createMsg91Provider($providerId: String!, $name: String!, $from: String!, $senderId: String!, $authKey: String!, $default: Boolean, $enabled: Boolean) {
messagingCreateMsg91Provider(providerId: $providerId, name: $name, from: $from, senderId: $senderId, authKey: $authKey, default: $default, enabled: $enabled) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_VONAGE_PROVIDER:
return 'mutation createVonageProvider($providerId: String!, $name: String!, $apiKey: String!, $apiSecret: String!) {
messagingCreateVonageProvider(providerId: $providerId, name: $name, apiKey: $apiKey, apiSecret: $apiSecret) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_FCM_PROVIDER:
return 'mutation createFcmProvider($providerId: String!, $name: String!, $serverKey: String!) {
messagingCreateFcmProvider(providerId: $providerId, name: $name, serverKey: $serverKey) {
_id
name
provider
type
default
enabled
}
}';
case self::$CREATE_APNS_PROVIDER:
return 'mutation createApnsProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!, $endpoint: String!) {
messagingCreateApnsProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId, endpoint: $endpoint) {
_id
name
provider
type
default
enabled
}
}';
case self::$LIST_PROVIDERS:
return 'query listProviders {
messagingListProviders {
total
providers {
_id
name
provider
type
default
enabled
}
}
}';
case self::$GET_PROVIDER:
return 'query getProvider($providerId: String!) {
messagingGetProvider(providerId: $providerId) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_MAILGUN_PROVIDER:
return 'mutation updateMailgunProvider($providerId: String!, $name: String!, $domain: String!, $apiKey: String!, $isEuRegion: Boolean, $enabled: Boolean) {
messagingUpdateMailgunProvider(providerId: $providerId, name: $name, domain: $domain, apiKey: $apiKey, isEuRegion: $isEuRegion, enabled: $enabled) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_SENDGRID_PROVIDER:
return 'mutation messagingUpdateSendgridProvider($providerId: String!, $name: String!, $apiKey: String!) {
messagingUpdateSendgridProvider(providerId: $providerId, name: $name, apiKey: $apiKey) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_TWILIO_PROVIDER:
return 'mutation updateTwilioProvider($providerId: String!, $name: String!, $accountSid: String!, $authToken: String!) {
messagingUpdateTwilioProvider(providerId: $providerId, name: $name, accountSid: $accountSid, authToken: $authToken) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_TELESIGN_PROVIDER:
return 'mutation updateTelesignProvider($providerId: String!, $name: String!, $username: String!, $password: String!) {
messagingUpdateTelesignProvider(providerId: $providerId, name: $name, username: $username, password: $password) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_TEXTMAGIC_PROVIDER:
return 'mutation updateTextmagicProvider($providerId: String!, $name: String!, $username: String!, $apiKey: String!) {
messagingUpdateTextmagicProvider(providerId: $providerId, name: $name, username: $username, apiKey: $apiKey) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_MSG91_PROVIDER:
return 'mutation updateMsg91Provider($providerId: String!, $name: String!, $senderId: String!, $authKey: String!) {
messagingUpdateMsg91Provider(providerId: $providerId, name: $name, senderId: $senderId, authKey: $authKey) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_VONAGE_PROVIDER:
return 'mutation updateVonageProvider($providerId: String!, $name: String!, $apiKey: String!, $apiSecret: String!) {
messagingUpdateVonageProvider(providerId: $providerId, name: $name, apiKey: $apiKey, apiSecret: $apiSecret) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_FCM_PROVIDER:
return 'mutation updateFcmProvider($providerId: String!, $name: String!, $serverKey: String!) {
messagingUpdateFcmProvider(providerId: $providerId, name: $name, serverKey: $serverKey) {
_id
name
provider
type
default
enabled
}
}';
case self::$UPDATE_APNS_PROVIDER:
return 'mutation updateApnsProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!, $endpoint: String!) {
messagingUpdateApnsProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId, endpoint: $endpoint) {
_id
name
provider
type
default
enabled
}
}';
case self::$DELETE_PROVIDER:
return 'mutation deleteProvider($providerId: String!) {
messagingDeleteProvider(providerId: $providerId) {
status
}
}';
case self::$CREATE_TOPIC:
return 'mutation createTopic($providerId: String!, $topicId: String!, $name: String!, $description: String!) {
messagingCreateTopic(providerId: $providerId, topicId: $topicId, name: $name, description: $description) {
_id
name
providerId
description
}
}';
case self::$LIST_TOPICS:
return 'query listTopics {
messagingListTopics {
total
topics {
_id
name
providerId
description
}
}
}';
case self::$GET_TOPIC:
return 'query getTopic($topicId: String!) {
messagingGetTopic(topicId: $topicId) {
_id
name
providerId
description
}
}';
case self::$UPDATE_TOPIC:
return 'mutation updateTopic($topicId: String!, $name: String!, $description: String!) {
messagingUpdateTopic(topicId: $topicId, name: $name, description: $description) {
_id
name
providerId
description
}
}';
case self::$DELETE_TOPIC:
return 'mutation deleteTopic($topicId: String!) {
messagingDeleteTopic(topicId: $topicId) {
status
}
}';
case self::$CREATE_SUBSCRIBER:
return 'mutation createSubscriber($subscriberId: String!, $targetId: String!, $topicId: String!) {
messagingCreateSubscriber(subscriberId: $subscriberId, targetId: $targetId, topicId: $topicId) {
_id
targetId
topicId
}
}';
case self::$LIST_SUBSCRIBERS:
return 'query listSubscribers($topicId: String!) {
messagingListSubscribers(topicId: $topicId) {
total
subscribers {
_id
targetId
topicId
}
}
}';
case self::$GET_SUBSCRIBER:
return 'query getSubscriber($topicId: String!, $subscriberId: String!) {
messagingGetSubscriber(topicId: $topicId, subscriberId: $subscriberId) {
_id
targetId
topicId
}
}';
case self::$DELETE_SUBSCRIBER:
return 'mutation deleteSubscriber($topicId: String!, $subscriberId: String!) {
messagingDeleteSubscriber(topicId: $topicId, subscriberId: $subscriberId) {
status
}
}';
case self::$CREATE_EMAIL:
return 'mutation createEmail($messageId: String!, $providerId: String!, $to: [String!]!, $subject: String!, $content: String!, $status: String, $description: String, $html: Boolean, $deliveryTime: String) {
messagingCreateEmail(messageId: $messageId, providerId: $providerId, to: $to, subject: $subject, content: $content, status: $status, description: $description, html: $html, deliveryTime: $deliveryTime) {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}';
case self::$CREATE_SMS:
return 'mutation createSMS($messageId: String!, $providerId: String!, $to: [String!]!, $content: String!, $status: String, $description: String, $deliveryTime: String) {
messagingCreateSMS(messageId: $messageId, providerId: $providerId, to: $to, content: $content, status: $status, description: $description, deliveryTime: $deliveryTime) {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}';
case self::$CREATE_PUSH_NOTIFICATION:
return 'mutation createPushNotification($messageId: String!, $providerId: String!, $to: [String!]!, $title: String!, $body: String!, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $deliveryTime: String) {
messagingCreatePushNotification(messageId: $messageId, providerId: $providerId, to: $to, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, deliveryTime: $deliveryTime) {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}';
case self::$LIST_MESSAGES:
return 'query listMessages {
messagingListMessages {
total
messages {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}
}';
case self::$GET_MESSAGE:
return 'query getMessage($messageId: String!) {
messagingGetMessage(messageId: $messageId) {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}';
case self::$UPDATE_EMAIL:
return 'mutation updateEmail($messageId: String!, $to: [String!], $subject: String, $content: String, $status: String, $description: String, $html: Boolean, $deliveryTime: String) {
messagingUpdateEmail(messageId: $messageId, to: $to, subject: $subject, content: $content, status: $status, description: $description, html: $html, deliveryTime: $deliveryTime) {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}';
case self::$UPDATE_SMS:
return 'mutation updateSMS($messageId: String!, $to: [String!], $content: String, $status: String, $description: String, $deliveryTime: String) {
messagingUpdateSMS(messageId: $messageId, to: $to, content: $content, status: $status, description: $description, deliveryTime: $deliveryTime) {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}';
case self::$UPDATE_PUSH_NOTIFICATION:
return 'mutation updatePushNotification($messageId: String!, $to: [String!], $title: String, $body: String, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $deliveryTime: String) {
messagingUpdatePushNotification(messageId: $messageId, to: $to, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, deliveryTime: $deliveryTime) {
_id
providerId
to
deliveryTime
deliveredAt
deliveryErrors
deliveredTo
status
description
}
}';
case self::$COMPLEX_QUERY:
return 'mutation complex($databaseId: String!, $databaseName: String!, $collectionId: String!, $collectionName: String!, $documentSecurity: Boolean!, $collectionPermissions: [String!]!) {
databasesCreate(databaseId: $databaseId, name: $databaseName) {
File diff suppressed because it is too large Load Diff
+145
View File
@@ -45,6 +45,55 @@ class UsersTest extends Scope
return $user;
}
/**
* @depends testCreateUser
*/
public function testCreateUserTarget(array $user)
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$CREATE_MAILGUN_PROVIDER);
$graphQLPayload = [
'query' => $query,
'variables' => [
'providerId' => ID::unique(),
'name' => 'Mailgun1',
'apiKey' => 'api-key',
'domain' => 'domain',
'from' => 'from@domain',
'isEuRegion' => false,
],
];
$provider = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$providerId = $provider['body']['data']['messagingCreateMailgunProvider']['_id'];
$this->assertEquals(200, $provider['headers']['status-code']);
$query = $this->getQuery(self::$CREATE_USER_TARGET);
$graphQLPayload = [
'query' => $query,
'variables' => [
'targetId' => ID::unique(),
'userId' => $user['_id'],
'providerId' => $providerId,
'identifier' => 'identifier',
]
];
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(200, $target['headers']['status-code']);
$this->assertEquals('identifier', $target['body']['data']['usersCreateTarget']['identifier']);
return $target['body']['data']['usersCreateTarget'];
}
public function testGetUsers()
{
$projectId = $this->getProject()['$id'];
@@ -176,6 +225,54 @@ class UsersTest extends Scope
$this->assertIsArray($user['body']['data']['usersListLogs']);
}
/**
* @depends testCreateUserTarget
*/
public function testListUserTargets(array $target)
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$LIST_USER_TARGETS);
$graphQLPayload = [
'query' => $query,
'variables' => [
'userId' => $target['userId'],
]
];
$targets = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(200, $targets['headers']['status-code']);
$this->assertIsArray($targets['body']['data']['usersListTargets']);
$this->assertCount(1, $targets['body']['data']['usersListTargets']['targets']);
}
/**
* @depends testCreateUserTarget
*/
public function testGetUserTarget(array $target)
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$GET_USER_TARGET);
$graphQLPayload = [
'query' => $query,
'variables' => [
'userId' => $target['userId'],
'targetId' => $target['_id'],
]
];
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(200, $target['headers']['status-code']);
$this->assertEquals('identifier', $target['body']['data']['usersGetTarget']['identifier']);
}
public function testUpdateUserStatus()
{
$projectId = $this->getProject()['$id'];
@@ -360,6 +457,31 @@ class UsersTest extends Scope
$this->assertEquals('{"key":"value"}', $user['body']['data']['usersUpdatePrefs']['data']);
}
/**
* @depends testCreateUserTarget
*/
public function testUpdateUserTarget(array $target)
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$UPDATE_USER_TARGET);
$graphQLPayload = [
'query' => $query,
'variables' => [
'userId' => $target['userId'],
'targetId' => $target['_id'],
'identifier' => 'newidentifier',
],
];
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(200, $target['headers']['status-code']);
$this->assertEquals('newidentifier', $target['body']['data']['usersUpdateTargetIdentifier']['identifier']);
}
public function testDeleteUserSessions()
{
$projectId = $this->getProject()['$id'];
@@ -407,6 +529,29 @@ class UsersTest extends Scope
$this->getUser();
}
/**
* @depends testCreateUserTarget
*/
public function testDeleteUserTarget(array $target)
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$DELETE_USER_TARGET);
$graphQLPayload = [
'query' => $query,
'variables' => [
'userId' => $target['userId'],
'targetId' => $target['_id'],
]
];
$target = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()), $graphQLPayload);
$this->assertEquals(204, $target['headers']['status-code']);
}
public function testDeleteUser()
{
$projectId = $this->getProject()['$id'];
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
<?php
namespace Tests\E2E\Services\Messaging;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideConsole;
class MessagingConsoleClientTest extends Scope
{
use MessagingBase;
use ProjectCustom;
use SideConsole;
}
@@ -0,0 +1,14 @@
<?php
namespace Tests\E2E\Services\Messaging;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
class MessagingCustomClientTest extends Scope
{
use MessagingBase;
use ProjectCustom;
use SideClient;
}
@@ -0,0 +1,14 @@
<?php
namespace Tests\E2E\Services\Messaging;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
class MessagingCustomServerTest extends Scope
{
use MessagingBase;
use ProjectCustom;
use SideServer;
}
@@ -794,7 +794,7 @@ class ProjectsConsoleClientTest extends Scope
public function testUpdateProjectOAuth($data): array
{
$id = $data['projectId'] ?? '';
$providers = require('app/config/providers.php');
$providers = require('app/config/authProviders.php');
/**
* Test for SUCCESS
@@ -825,7 +825,7 @@ class ProjectsConsoleClientTest extends Scope
foreach ($providers as $key => $provider) {
$asserted = false;
foreach ($response['body']['providers'] as $responseProvider) {
foreach ($response['body']['authProviders'] as $responseProvider) {
if ($responseProvider['key'] === $key) {
$this->assertEquals('AppId-' . ucfirst($key), $responseProvider['appId']);
$this->assertEquals('Secret-' . ucfirst($key), $responseProvider['secret']);
@@ -867,7 +867,7 @@ class ProjectsConsoleClientTest extends Scope
$i = 0;
foreach ($providers as $key => $provider) {
$asserted = false;
foreach ($response['body']['providers'] as $responseProvider) {
foreach ($response['body']['authProviders'] as $responseProvider) {
if ($responseProvider['key'] === $key) {
// On first provider, test enabled=false
$this->assertEquals($i !== 0, $responseProvider['enabled']);
+88
View File
@@ -1223,6 +1223,94 @@ trait UsersBase
$this->assertEquals($response['headers']['status-code'], 400);
}
/**
* @depends testGetUser
*/
public function testCreateUserTarget(array $data): array
{
$provider = $this->client->call(Client::METHOD_POST, '/messaging/providers/sendgrid', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'providerId' => 'unique()',
'name' => 'Sengrid1',
'apiKey' => 'my-apikey'
]);
$this->assertEquals(201, $provider['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/targets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'targetId' => ID::unique(),
'providerId' => $provider['body']['$id'],
'identifier' => 'my-token',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals($provider['body']['$id'], $response['body']['providerId']);
$this->assertEquals('my-token', $response['body']['identifier']);
return $response['body'];
}
/**
* @depends testCreateUserTarget
*/
public function testUpdateUserTarget(array $data): array
{
$response = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/targets/' . $data['$id'] . '/identifier', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'identifier' => 'my-updated-token',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('my-updated-token', $response['body']['identifier']);
return $response['body'];
}
/**
* @depends testUpdateUserTarget
*/
public function testListUserTarget(array $data)
{
$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(1, \count($response['body']['targets']));
}
/**
* @depends testUpdateUserTarget
*/
public function testGetUserTarget(array $data)
{
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/targets/' . $data['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($data['$id'], $response['body']['$id']);
}
/**
* @depends testUpdateUserTarget
*/
public function testDeleteUserTarget(array $data)
{
$response = $this->client->call(Client::METHOD_DELETE, '/users/' . $data['userId'] . '/targets/' . $data['$id'], array_merge([
'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']);
}
/**
* @depends testGetUser
*/
@@ -154,9 +154,9 @@ class V16Test extends TestCase
public function projectProvider(): array
{
return [
'providers' => [
'authProviders' => [
[
'providers' => [
'authProviders' => [
[
'key' => 'github',
'name' => 'GitHub',
@@ -167,7 +167,7 @@ class V16Test extends TestCase
],
],
[
'providers' => [
'authProviders' => [
[
'name' => 'Github',
'appId' => 'client_id',