Compare commits

...
Author SHA1 Message Date
Matej Bačo 2889f00192 Use new console 2023-12-07 07:57:36 +01:00
Khushboo Verma 3436924c09 Removed empty properties from email template 2023-12-07 00:16:03 +01:00
Khushboo Verma 43d6515f04 Merge branch 'feat-1.5-auth' of github.com:appwrite/appwrite into feat-1.5-auth 2023-12-07 00:08:00 +01:00
Khushboo Verma c2c1163501 Fix endpoint 2023-12-07 00:07:40 +01:00
Matej Bačo 5709d0e457 Fix lengthy sessions 2023-12-06 23:57:58 +01:00
Matej Bačo f60e844ce3 Fix bugs 2023-12-06 23:56:48 +01:00
Khushboo Verma f7b3d4dfa8 Add notify to phone and oauth 2023-12-06 23:55:31 +01:00
Khushboo Verma 57035f35f8 Updated the arrays 2023-12-06 23:36:15 +01:00
Khushboo Verma 31cc6a4ca3 Updated array 2023-12-06 23:19:35 +01:00
Khushboo Verma 9c4d329ceb Fix the order 2023-12-06 23:00:05 +01:00
Khushboo Verma 998642a875 Merge branch 'feat-1.5-auth' of github.com:appwrite/appwrite into feat-1.5-auth 2023-12-06 22:38:59 +01:00
Khushboo Verma d7041c0efd WIP: Add security phrase to magic URL 2023-12-06 22:38:38 +01:00
Matej Bačo ea5de9bde3 Fix notify feature 2023-12-06 18:40:18 +01:00
Khushboo Verma 7abafbb09c Alert on new session creation 2023-12-06 17:35:09 +01:00
Khushboo Verma ea2302238d Alert on new session creation 2023-12-06 17:30:37 +01:00
Matej Bačo a2df1932cd Merge branch 'feat-1.5-auth' of https://github.com/appwrite/appwrite into feat-1.5-auth 2023-12-06 12:52:18 +01:00
Matej Bačo 5dab541d76 Auth features 2023-12-06 12:51:34 +01:00
Khushboo Verma 22b1002305 Add checks for 100k, 1m, 10m password dictionary 2023-12-06 12:02:38 +01:00
Khushboo Verma bb1357f178 Resolved merge conflicts 2023-12-05 14:09:20 +01:00
Khushboo Verma 14afed5eb2 Password dictionary WIP 2023-12-05 14:07:17 +01:00
Matej Bačo 50d6dcc745 Add auth features 2023-12-05 13:57:28 +01:00
Matej Bačo 6459abeea2 Add ai password 2023-12-05 12:58:36 +01:00
Khushboo Verma d822878fe6 Update projects endpoint 2023-12-05 12:57:26 +01:00
Khushboo VermaandGitHub 34d3e4fbfd Add files via upload 2023-12-05 11:59:42 +01:00
Matej BačoandGitHub a989b181fd Merge pull request #7239 from appwrite/feat-session-extension 2023-12-05 11:56:55 +01:00
Khushboo Verma c64798d1c3 Logout from all sessions except current 2023-12-05 11:47:39 +01:00
Matej Bačo d85cb572b0 Implement session extension 2023-12-04 17:13:14 +01:00
Jake BarnbyandGitHub 396ff87fbc Merge pull request #7219 from appwrite/feat-add-message-provider-type
Feat add message provider type
2023-11-29 23:05:46 +13:00
prateek banga 739cc36fdc updated providerType wherever left to update 2023-11-29 15:22:26 +05:30
prateek banga fa8dc133b4 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-add-message-provider-type 2023-11-29 15:01:47 +05:30
Jake BarnbyandGitHub 82874a1100 Merge pull request #7217 from appwrite/target-changes
adds target when creating user via server endpoint
2023-11-29 21:19:58 +13:00
prateek banga 43b8fc2c31 review changes 2023-11-29 13:47:44 +05:30
Jake Barnby 042c1a725f Message extends Model instead of Any so rules are applied since data is not hoisted 2023-11-29 17:10:53 +13:00
Jake Barnby 4b58d08fd8 Add providerType to Message 2023-11-29 17:09:44 +13:00
Jake Barnby 82c86c0ae2 deliveryTime -> scheduledAt 2023-11-29 17:08:25 +13:00
Jake Barnby 4bc23afc65 Add constants for message types 2023-11-29 17:05:37 +13:00
prateek banga ee4c2d0e0d adds target when creating user via server endpoint 2023-11-28 18:42:34 +05:30
Jake BarnbyandGitHub eb4083641f Merge pull request #7195 from appwrite/feat-optional-providers-creation
makes provider creation fields optional and adds target info in subsc…
2023-11-24 19:47:12 +13:00
prateek banga e3f60f4c78 fix tests 2023-11-23 23:14:02 +05:30
prateek banga 858175fe00 adds target object in subscriber model 2023-11-23 14:35:16 +05:30
Prateek Banga d9826cdce8 makes provider creation fields optional and adds target info in subscriber model 2023-11-23 00:39:55 +05:30
Jake BarnbyandGitHub 90c47ea241 Merge pull request #7188 from appwrite/refactor-textmagic-change
changes TextMagic to Textmagic in all places and uses email validator
2023-11-21 23:03:36 +13:00
prateek banga 9c6cd8512f review changes 2023-11-21 15:19:19 +05:30
prateek banga 88f228c106 review changes 2023-11-21 15:11:09 +05:30
prateek banga 3394218cb0 adds validation for phone and email identifier 2023-11-21 15:00:02 +05:30
prateek banga 520d19e335 changes TextMagic to Textmagic in all places and uses email validator 2023-11-21 12:35:36 +05:30
Jake Barnby 0b01f7140f Merge remote-tracking branch 'origin/main' into 1.5.x
# Conflicts:
#	app/config/errors.php
#	composer.lock
#	src/Appwrite/Extend/Exception.php
2023-11-21 00:19:17 +13:00
Jake BarnbyandGitHub 8caf61bcd1 Merge pull request #7177 from appwrite/feat-subscriber-username
adds user name in subscriber response model
2023-11-20 23:53:49 +13:00
prateek banga b1eaf97806 review changes 2023-11-20 16:21:06 +05:30
prateek banga 5c1cf3a526 adds user name in subscriber response model 2023-11-20 12:34:50 +05:30
Jake BarnbyandGitHub 7eb6b05e05 Merge pull request #7152 from appwrite/feat-account-target
Feat account target
2023-11-17 00:54:54 +13:00
f0247930da Update app/config/collections.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-11-16 17:15:08 +05:30
prateek banga 7536d8eb0f review changes 2023-11-16 17:09:08 +05:30
prateek banga bf95736da0 review changes 2023-11-16 16:47:36 +05:30
prateek banga 14e8234889 Merge branch 'feat-account-target' of https://github.com/appwrite/appwrite into feat-account-target 2023-11-16 16:27:30 +05:30
prateek banga f854622705 review changes 2023-11-16 16:26:36 +05:30
Prateek BangaandGitHub 893b65ce8f Merge branch '1.5.x' into feat-account-target 2023-11-16 14:17:27 +05:30
Jake BarnbyandGitHub 23078c43e2 Merge pull request #7151 from appwrite/feat-remove-internal-provider
removes internal provider
2023-11-16 21:45:38 +13:00
prateek banga c1869bb0ca adds messaging in test workflow 2023-11-16 11:20:39 +05:30
Prateek BangaandGitHub 6a52dd6a20 Merge branch '1.5.x' into feat-remove-internal-provider 2023-11-16 11:03:45 +05:30
Prateek BangaandGitHub 6a3fa7d5c5 Merge branch '1.5.x' into feat-account-target 2023-11-16 11:03:38 +05:30
prateek banga a282ab38a6 adds target endpoint in account controller for push tokensl 2023-11-16 02:12:06 +05:30
prateek banga c728d9bc8e lint fix 2023-11-16 01:36:22 +05:30
prateek banga 549dcc493d lint fix 2023-11-16 01:33:05 +05:30
prateek banga c24664f5d9 removes internal provider 2023-11-16 01:30:47 +05:30
Jake BarnbyandGitHub 71b5e1194f Merge pull request #7132 from appwrite/feat-target-provider-type
Feat target provider type
2023-11-15 20:25:43 +13:00
prateek banga 2aa8391c5e review fixes 2023-11-15 12:52:27 +05:30
Jake Barnby db9a946b0c Merge remote-tracking branch 'origin/1.5.x' into feat-target-provider-type 2023-11-15 19:08:59 +13:00
Jake BarnbyandGitHub a3c40fcf95 Merge pull request #7129 from appwrite/more_review_changes
More review changes
2023-11-15 19:08:00 +13:00
prateek banga fdca55b0de fix test cases 2023-11-15 01:24:55 +05:30
prateek banga adc76c5797 adds target when account is created or email or phone is updated 2023-11-14 22:46:20 +05:30
prateek banga 1870524be5 fixes error name 2023-11-14 21:34:17 +05:30
prateek banga 7ae614fe13 adds provider type in target 2023-11-14 18:14:07 +05:30
prateek banga c5aaa670a9 adds provider details in message search attribute 2023-11-14 15:20:21 +05:30
prateek banga f84a00dded adds search attribute filter 2023-11-14 14:07:52 +05:30
prateek banga 8f53301209 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into more_review_changes 2023-11-14 12:26:35 +05:30
Jake BarnbyandGitHub e2aa06ed84 Merge pull request #7120 from appwrite/chore-sync-main
Chore sync main
2023-11-13 17:09:45 +13:00
Jake Barnby 458c2d1446 Merge remote-tracking branch 'origin/main' into chore-sync-main 2023-11-13 16:46:05 +13:00
Jake BarnbyandGitHub 162fac5724 Merge pull request #7115 from appwrite/chore-sync-main-1.5.x
Chore sync main 1.5.x
2023-11-13 13:43:18 +13:00
Prateek BangaandGitHub 4f681cfe10 Merge branch '1.5.x' into chore-sync-main-1.5.x 2023-11-10 23:12:40 +05:30
Torsten DittmannandGitHub 4c084afa1e Merge pull request #7069 from appwrite/feat-logs-messaging-api
Feat logs messaging api
2023-11-10 14:24:15 +01:00
Prateek Banga 1b99f47f21 lint fix 2023-11-10 13:28:39 +05:30
Prateek Banga 3ade88897c Merge branch 'main' of https://github.com/appwrite/appwrite into chore-sync-main-1.5.x 2023-11-10 12:49:05 +05:30
Prateek Banga 3b5379a0cb added more assertions in logs test and moved them to console 2023-11-10 12:31:21 +05:30
Prateek Banga 76ed15f2a1 adds more assertion in logs tests 2023-11-10 02:20:55 +05:30
Prateek Banga 4106dd5bbd Merge branch 'feat-logs-messaging-api' of https://github.com/appwrite/appwrite into feat-logs-messaging-api 2023-11-09 14:53:53 +05:30
Prateek Banga 3654799cc7 links ISO 8601, move params in messaging controllers 2023-11-09 14:53:42 +05:30
Prateek BangaandGitHub 4c970cbc46 Merge branch '1.5.x' into feat-logs-messaging-api 2023-11-09 13:42:51 +05:30
Jake BarnbyandGitHub 2365626270 Merge pull request #7058 from appwrite/event-label-messaging
Event label messaging
2023-11-09 21:08:36 +13:00
Prateek Banga bd39c5ed2e Merge branch '1.5.x' of https://github.com/appwrite/appwrite into feat-logs-messaging-api 2023-11-09 12:40:05 +05:30
Prateek Banga 8d5bfdabc6 revert wrong aduits.event label 2023-11-09 12:29:16 +05:30
Jake Barnby 1487897583 Merge remote-tracking branch 'origin/1.5.x' into event-label-messaging 2023-11-09 19:49:08 +13:00
Jake BarnbyandGitHub a9c7b640ec Merge pull request #7048 from appwrite/1.5.x_review_changes
review changes
2023-11-09 19:47:09 +13:00
prateek banga b9de4a0169 added tests for logs endpoint 2023-11-08 23:26:46 +05:30
Prateek Banga f97d1fa9e3 adds log api in messaging controller 2023-11-02 18:00:17 +05:30
Prateek Banga 61d46bee5c Merge branch '1.5.x_review_changes' of https://github.com/appwrite/appwrite into event-label-messaging 2023-11-02 16:53:55 +05:30
Prateek Banga 270e6e7c15 review changes 2023-11-02 16:43:24 +05:30
Prateek Banga 48744f66c8 Merge branch '1.5.x_review_changes' of https://github.com/appwrite/appwrite into event-label-messaging 2023-11-02 12:21:17 +05:30
Prateek Banga 76d7369314 fixes description for messaging service models 2023-11-02 12:20:15 +05:30
prateek banga fda4e6a8bd fixes typo in event label 2023-11-01 14:33:13 +05:30
Prateek Banga 2aa5c5742b remove unnecessary injection 2023-11-01 12:09:00 +05:30
prateek banga f7529140bd adds event label to remaining messaging api 2023-10-31 23:53:46 +05:30
Prateek Banga a719c3f46b adds event label to create providers endpoint 2023-10-31 23:17:58 +05:30
Prateek Banga a58f473b29 adds createdAt and updatedAt in messaging models 2023-10-31 14:01:42 +05:30
prateek banga a4c8d852a5 adds test skip condition if dsn not provided 2023-10-31 02:34:18 +05:30
prateek banga 0dafc59cc2 converted env vars to dsn format 2023-10-31 02:05:50 +05:30
prateek banga a01d361886 review changes 2023-10-31 01:12:31 +05:30
prateek banga 06daa544e8 review changes 2023-10-30 23:37:57 +05:30
Jake BarnbyandGitHub 26ecad1a6f Merge pull request #7014 from appwrite/feat-remove-provider-from-topics
removes provider from topics
2023-10-27 22:34:21 +13:00
Prateek Banga bc6df4bab7 add recipients array check in API instead of worker 2023-10-27 13:42:27 +05:30
Prateek Banga 5a9b1f0c24 removes provider from topics 2023-10-26 19:44:06 +05:30
Jake BarnbyandGitHub 92062a5463 Merge pull request #7010 from appwrite/review-changes-1.5.x
made review changes
2023-10-26 21:45:39 +13:00
prateek banga c2cd544948 review changes 2023-10-26 13:46:45 +05:30
Prateek Banga 34c96e4672 Merge branch '1.5.x' of https://github.com/appwrite/appwrite into review-changes-1.5.x 2023-10-26 12:37:51 +05:30
Prateek Banga 34b3ed68ee adds total count for subscribers in topics model 2023-10-26 12:37:39 +05:30
Jake BarnbyandGitHub ea76bc6680 Merge pull request #6991 from appwrite/providers-from-attribute
Providers from attribute
2023-10-26 19:52:02 +13:00
Prateek BangaandGitHub 9545e4ac72 Merge branch '1.5.x' into providers-from-attribute 2023-10-26 02:11:12 +05:30
prateek banga 456654c165 Merge branch 'providers-from-attribute' of https://github.com/appwrite/appwrite into review-changes-1.5.x 2023-10-26 00:03:13 +05:30
prateek banga f5aa226b57 fix test 2023-10-26 00:02:22 +05:30
prateek banga f6f4397aab Merge branch 'providers-from-attribute' of https://github.com/appwrite/appwrite into review-changes-1.5.x 2023-10-25 23:55:45 +05:30
prateek banga 887eeef585 review changes 2023-10-25 23:09:38 +05:30
prateek banga fd4a81c4fc made review changes 2023-10-25 23:03:23 +05:30
prateek banga 2435f722ca fixes test cases 2023-10-25 21:45:55 +05:30
Jake BarnbyandGitHub cbae797f38 Merge pull request #6965 from appwrite/sync-1.4.x-to-1.5.x
Sync 1.4.x to 1.5.x
2023-10-24 20:12:11 +13:00
Jake Barnby d5b6421285 Merge remote-tracking branch 'origin/1.4.x' into sync-1.4.x-to-1.5.x
# Conflicts:
#	composer.lock
2023-10-24 19:46:57 +13:00
prateek banga b60cbcada5 adds from field in remaining providers 2023-10-24 01:05:46 +05:30
prateek banga af40c63357 review changes 2023-10-20 17:02:13 +05:30
prateek banga e040ecba36 fixed lint error 2023-10-20 15:03:56 +05:30
prateek banga 6c260223ba Merge branch '1.5.x' of https://github.com/appwrite/appwrite into sync-1.4.x-to-1.5.x 2023-10-20 15:00:22 +05:30
prateek banga 161bbfb822 Merge branch '1.4.x' of https://github.com/appwrite/appwrite into sync-1.4.x-to-1.5.x 2023-10-20 14:55:17 +05:30
Jake BarnbyandGitHub 8ed37fb445 Merge pull request #6950 from appwrite/feat-sms-push-controllers
Feat sms push controllers
2023-10-20 21:49:20 +13:00
Prateek Banga d714760eb8 review changeS 2023-10-20 13:51:13 +05:30
prateek banga 24b2d70441 review changes 2023-10-20 13:33:27 +05:30
prateek banga e4d2d15f70 review changes 2023-10-20 13:31:56 +05:30
prateek banga db9e7b0199 review changes 2023-10-20 13:28:23 +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
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
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
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
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
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
79 changed files with 1110146 additions and 468 deletions
+7 -2
View File
@@ -87,7 +87,7 @@ _APP_LOGGING_PROVIDER=
_APP_LOGGING_CONFIG=
_APP_GRAPHQL_MAX_BATCH_SIZE=10
_APP_GRAPHQL_MAX_COMPLEXITY=250
_APP_GRAPHQL_MAX_DEPTH=3
_APP_GRAPHQL_MAX_DEPTH=4
_APP_DOCKER_HUB_USERNAME=
_APP_DOCKER_HUB_PASSWORD=
_APP_VCS_GITHUB_APP_NAME=
@@ -98,4 +98,9 @@ _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=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
_APP_MESSAGE_SMS_TEST_DSN=
_APP_MESSAGE_EMAIL_TEST_DSN=
_APP_MESSAGE_PUSH_TEST_DSN=
_APP_CONSOLE_SUPABASE_APP_ID=d64cd75f-2786-4b94-aeda-eae01e165941
_APP_CONSOLE_SUPABASE_SECRET=sba_d5a802fbdcdf86d27f7d071a2778bff0419040de
+1
View File
@@ -99,6 +99,7 @@ jobs:
Users,
Webhooks,
VCS,
Messaging,
]
steps:
+1
View File
@@ -13,3 +13,4 @@ debug/
app/sdks
dev/yasd_init.php
.phpunit.result.cache
Makefile
+1 -1
View File
@@ -466,7 +466,7 @@
## Features
- Added Phone Authentication by @TorstenDittmann in https://github.com/appwrite/appwrite/pull/3357
- Added Twilio Support
- Added TextMagic Support
- Added Textmagic Support
- Added Telesign Support
- Added Endpoint to create Phone Session (`POST:/v1/account/sessions/phone`)
- Added Endpoint to confirm Phone Session (`PUT:/v1/account/sessions/phone`)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+579 -3
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('oAuthProviders', []);
$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,
@@ -670,6 +681,17 @@ $commonCollections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('expire'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
],
'indexes' => [
[
@@ -1366,7 +1388,561 @@ $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('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' => '',
'array' => false,
'filters' => ['providerSearch'],
],
],
'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_enabled_type'),
'type' => Database::INDEX_KEY,
'attributes' => ['enabled','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('providerType'),
'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('topics'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 21845,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('users'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 21845,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('targets'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 21845,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('scheduledAt'),
'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('deliveredTotal'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => 0,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => ['messageSearch'],
],
],
'indexes' => [
[
'$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('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('total'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => 0,
'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' => '',
'array' => false,
'filters' => ['topicSearch'],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'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('providerType'),
'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' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('providerInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'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' => [],
],
[
'$id' => ID::custom('name'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'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_UNIQUE,
'attributes' => ['identifier'],
'lengths' => [],
'orders' => [],
],
],
],
];
$projectCollections = array_merge([
'databases' => [
@@ -3412,7 +3988,7 @@ $consoleCollections = array_merge([
'filters' => ['json'],
],
[
'$id' => ID::custom('authProviders'),
'$id' => ID::custom('oAuthProviders'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
+94
View File
@@ -103,6 +103,16 @@ return [
'description' => 'This method was not fully implemented yet. If you believe this is a mistake, please upgrade your Appwrite server version.',
'code' => 405,
],
Exception::GENERAL_INVALID_EMAIL => [
'name' => Exception::GENERAL_INVALID_EMAIL,
'description' => 'Value must be a valid email address.',
'code' => 400,
],
Exception::GENERAL_INVALID_PHONE => [
'name' => Exception::GENERAL_INVALID_PHONE,
'description' => 'Value must be a valid phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.',
'code' => 400,
],
/** User Errors */
Exception::USER_COUNT_EXCEEDED => [
@@ -190,6 +200,16 @@ return [
'description' => 'The password you are trying to use contains references to your name, email, phone or userID. For your security, please choose a different password and try again.',
'code' => 400,
],
Exception::USER_PASSWORD_DICTIONARY => [
'name' => Exception::USER_PASSWORD_DICTIONARY,
'description' => 'Password must be at least 8 characters and should not be one of the commonly used password.',
'code' => 400,
],
Exception::USER_PASSWORD_AI => [
'name' => Exception::USER_PASSWORD_AI,
'description' => 'As per AI, your password is not strong enough. Please choose a different password and try again.',
'code' => 400,
],
Exception::USER_SESSION_NOT_FOUND => [
'name' => Exception::USER_SESSION_NOT_FOUND,
'description' => 'The current user session could not be found.',
@@ -250,6 +270,16 @@ return [
'description' => 'User phone is already verified',
'code' => 409
],
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 => [
@@ -764,4 +794,68 @@ return [
'description' => 'An error occurred on the provider\'s side. Please try again later.',
'code' => 400,
],
/** 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_MISSING_TARGET => [
'name' => Exception::MESSAGE_MISSING_TARGET,
'description' => 'Message with the requested ID is missing a target (Topics or Users or Targets).',
'code' => 400,
],
Exception::MESSAGE_ALREADY_SENT => [
'name' => Exception::MESSAGE_ALREADY_SENT,
'description' => 'Message with the requested ID has already been sent.',
'code' => 400,
],
Exception::MESSAGE_ALREADY_SCHEDULED => [
'name' => Exception::MESSAGE_ALREADY_SCHEDULED,
'description' => 'Message with the requested ID has already been scheduled for delivery.',
'code' => 400,
],
];
+64
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,56 @@ 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 topic is created.',
],
'update' => [
'$description' => 'This event triggers when a topic is updated.',
],
'delete' => [
'$description' => 'This event triggers when a topic 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,
@@ -6,6 +6,10 @@
<a href="{{redirect}}" target="_blank">{{redirect}}</a>
<b>{{code}}</b>
<i>{{securityPhrase}}</i>
<p>{{footer}}</p>
<br>
+7
View File
@@ -12,9 +12,16 @@
"emails.magicSession.subject": "Login",
"emails.magicSession.hello": "Hey,",
"emails.magicSession.body": "Follow this link to login.",
"emails.magicSession.codeBody": "Enter this code to login.",
"emails.magicSession.footer": "If you didnt ask to login using this email, you can ignore this message.",
"emails.magicSession.thanks": "Thanks",
"emails.magicSession.signature": "{{project}} team",
"emails.authNotify.subject": "New session alert for {{project}}",
"emails.authNotify.hello": "Hey {{user}},",
"emails.authNotify.body": "We're writing to inform you that a new session has been initiated on your {{project}} account on {{date}} at {{time}}. <br><hr><br>Here are the details: <br>Device: {{device}}<br>Location: {{country}}<br>IP Address: {{ip}} <br><hr><br>If you did not initiate this session, we strongly recommend that you review your account settings.",
"emails.authNotify.footer": "If you recognize this activity, no further action is required.",
"emails.authNotify.thanks": "Thanks",
"emails.authNotify.signature": "{{project}} team",
"emails.recovery.subject": "Password Reset",
"emails.recovery.hello": "Hello {{user}}",
"emails.recovery.body": "Follow this link to reset your {{project}} password.",
@@ -152,6 +152,16 @@ return [
'beta' => false,
'mock' => false,
],
'supabase' => [
'name' => 'Supabase',
'developers' => 'https://supabase.com/docs',
'icon' => 'icon-supabase',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'gitlab' => [
'name' => 'GitLab',
'developers' => 'https://docs.gitlab.com/ee/api/',
+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' => false,
'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',
]
];
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -440,7 +440,7 @@ return [
'variables' => [
[
'name' => '_APP_SMS_PROVIDER',
'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'.\n\nEnsure `[USER]` and `[SECRET]` are URL encoded if they contain any non-alphanumeric characters.\n\nAvailable providers are twilio, text-magic, telesign, msg91, and vonage.",
'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'.\n\nEnsure `[USER]` and `[SECRET]` are URL encoded if they contain any non-alphanumeric characters.\n\nAvailable providers are twilio, Textmagic, telesign, msg91, and vonage.",
'introduction' => '0.15.0',
'default' => '',
'required' => false,
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -84,8 +84,8 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
$accessTokenExpiry = $gitHubSession->getAttribute('providerAccessTokenExpiry');
$refreshToken = $gitHubSession->getAttribute('providerRefreshToken');
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
+1 -1
View File
@@ -1571,7 +1571,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new DatetimeValidator(), 'Default value for the attribute in ISO 8601 format. Cannot be set when attribute is required.', true)
->param('default', null, new DatetimeValidator(), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
->inject('response')
->inject('dbForProject')
File diff suppressed because it is too large Load Diff
+78 -10
View File
@@ -85,7 +85,7 @@ App::post('/v1/projects')
}
$auth = Config::getParam('auth', []);
$auths = ['limit' => 0, 'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT, 'passwordHistory' => 0, 'passwordDictionary' => false, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, 'personalDataCheck' => false];
$auths = ['limit' => 0, 'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT, 'passwordHistory' => 0, 'passwordAi' => false, 'renewal' => false, 'passwordDictionary' => false, 'passwordDictionaryLength' => '10k', 'notify' => false, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, 'personalDataCheck' => false];
foreach ($auth as $index => $method) {
$auths[$method['key'] ?? ''] = true;
}
@@ -159,7 +159,7 @@ App::post('/v1/projects')
'legalTaxId' => ID::custom($legalTaxId),
'services' => new stdClass(),
'platforms' => null,
'authProviders' => [],
'oAuthProviders' => [],
'webhooks' => null,
'keys' => null,
'auths' => $auths,
@@ -598,7 +598,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('oAuthProviders')), 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)
@@ -612,7 +612,7 @@ App::patch('/v1/projects/:projectId/oauth2')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$providers = $project->getAttribute('authProviders', []);
$providers = $project->getAttribute('oAuthProviders', []);
if ($appId !== null) {
$providers[$provider . 'Appid'] = $appId;
@@ -626,7 +626,7 @@ App::patch('/v1/projects/:projectId/oauth2')
$providers[$provider . 'Enabled'] = $enabled;
}
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('authProviders', $providers));
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('oAuthProviders', $providers));
$response->dynamic($project, Response::MODEL_PROJECT);
});
@@ -674,9 +674,10 @@ App::patch('/v1/projects/:projectId/auth/duration')
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('duration', 31536000, new Range(0, 31536000), 'Project session length in seconds. Max length: 31536000 seconds.')
->param('renewal', false, new Boolean(), 'Automatic session refresh. If enabled, sessions are automatically extended to session duration on every request.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, int $duration, Response $response, Database $dbForConsole) {
->action(function (string $projectId, int $duration, bool $renewal, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -686,6 +687,7 @@ App::patch('/v1/projects/:projectId/auth/duration')
$auths = $project->getAttribute('auths', []);
$auths['duration'] = $duration;
$auths['renewal'] = $renewal;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
@@ -759,7 +761,7 @@ App::patch('/v1/projects/:projectId/auth/password-history')
});
App::patch('/v1/projects/:projectId/auth/password-dictionary')
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password')
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dictionary check for user password')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -770,6 +772,39 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('enabled', false, new Boolean(false), 'Set whether or not to enable checking user\'s password against most commonly used passwords. Default is false.')
->param('length', '10k', new WhiteList(['10k', '100k', '1m', '10m'], true), 'Set the length of the password dictionary to use', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $enabled, string $length, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['passwordDictionary'] = $enabled;
$auths['passwordDictionaryLength'] = $length;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/password-ai')
->desc('Update authentication password ai status.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateAuthPasswordAi')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('enabled', false, new Boolean(false), 'Set whether or not to enable checking user\'s password against AI opinion. Default is false.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
@@ -781,7 +816,40 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
}
$auths = $project->getAttribute('auths', []);
$auths['passwordDictionary'] = $enabled;
$auths['passwordAi'] = $enabled;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/notify')
->desc('Update authentication notify status. Enable or disable notifications for new session creations.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateAuthNotify')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('enabled', false, new Boolean(false), 'Set whether or not to enable notifications for new session creations. Default is false.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) {
if (!empty($projectId)) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
}
$auths = $project->getAttribute('auths', []);
$auths['notify'] = $enabled;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
@@ -1151,7 +1219,7 @@ App::post('/v1/projects/:projectId/keys')
->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
@@ -1268,7 +1336,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true)
->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) {
+17 -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;
@@ -388,7 +388,7 @@ App::post('/v1/teams/:teamId/memberships')
->inject('queueForMails')
->inject('queueForMessaging')
->inject('queueForEvents')
->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 $queueForMails, EventPhone $queueForMessaging, Event $queueForEvents) {
->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 $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) {
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -628,6 +628,10 @@ App::post('/v1/teams/:teamId/memberships')
->trigger()
;
} elseif (!empty($phone)) {
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
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] ?? [];
@@ -638,9 +642,18 @@ App::post('/v1/teams/:teamId/memberships')
$message = $message->setParam('{{token}}', $url);
$message = $message->render();
$messageDoc = new Document([
'$id' => ID::unique(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setRecipient($phone)
->setMessage($message)
->setMessage($messageDoc)
->setRecipients([$phone])
->setProviderType('SMS')
->setProject($project)
->trigger();
}
}
+448 -5
View File
@@ -2,6 +2,7 @@
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\Password;
use Appwrite\Auth\Validator\PasswordAi;
use Appwrite\Auth\Validator\Phone;
use Appwrite\Detector\Detector;
use Appwrite\Event\Delete;
@@ -9,6 +10,7 @@ use Appwrite\Event\Event;
use Appwrite\Network\Validator\Email;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Identities;
use Appwrite\Utopia\Database\Validator\Queries\Targets;
use Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Users;
use Utopia\Database\Validator\Query\Limit;
@@ -70,6 +72,13 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
}
}
if ($project->getAttribute('auths', [])['passwordAi'] ?? false) {
$passwordAiValidator = new PasswordAi();
if (!$passwordAiValidator->isValid($password)) {
throw new Exception(Exception::USER_PASSWORD_AI);
}
}
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
$user = $dbForProject->createDocument('users', new Document([
'$id' => $userId,
@@ -98,6 +107,42 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
'memberships' => null,
'search' => implode(' ', [$userId, $email, $phone, $name]),
]));
if ($email) {
try {
$target = $dbForProject->createDocument('targets', new Document([
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => 'email',
'identifier' => $email,
]));
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
} catch (Duplicate) {
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
}
}
if ($phone) {
try {
$target = $dbForProject->createDocument('targets', new Document([
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'providerType' => 'sms',
'identifier' => $phone,
]));
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
} catch (Duplicate) {
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]);
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
}
}
$dbForProject->deleteCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
@@ -125,13 +170,35 @@ App::post('/v1/users')
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('email', null, new Email(), 'User email.', true)
->param('phone', null, new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'Plain text user password. Must be at least 8 chars.', true, ['project', 'passwordsDictionary'])
->param('password', '', fn ($project, $passwordsDictionary10k) => new PasswordDictionary($passwordsDictionary10k, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'Plain text user password. Must be at least 8 chars.', true, ['project', 'passwordsDictionary10k'])
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) {
->inject('passwordsDictionary10k')
->inject('passwordsDictionary100k')
->inject('passwordsDictionary1M')
->inject('passwordsDictionary10M')
->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents, array $passwordsDictionary10k, array $passwordsDictionary100k, array $passwordsDictionary1M, array $passwordsDictionary10M) {
if ($project->getAttribute('auths', [])['passwordDictionary'] ?? false) {
$passwordDictionaryLength = $project->getAttribute('auths', [])['passwordDictionaryLength'] ?? '10k';
if ($passwordDictionaryLength == '10k') {
$passwordDictionary = $passwordsDictionary10k;
} elseif ($passwordDictionaryLength == '100k') {
$passwordDictionary = $passwordsDictionary100k;
} elseif ($passwordDictionaryLength == '1m') {
$passwordDictionary = $passwordsDictionary1M;
} elseif ($passwordDictionaryLength == '10m') {
$passwordDictionary = $passwordsDictionary10M;
} else {
throw new Exception('Password dictionary length is not supported');
}
$passwordDictionaryValidator = new PasswordDictionary($passwordDictionary, true);
if (!$passwordDictionaryValidator->isValid($password)) {
throw new Exception(Exception::USER_PASSWORD_DICTIONARY);
}
}
$user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $queueForEvents);
$response
@@ -379,6 +446,98 @@ 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', 'target.create')
->label('audits.resource', 'target/response.$id')
->label('event', 'users.[userId].targets.[targetId].create')
->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('targetId', '', new CustomId(), 'Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('userId', '', new UID(), 'User ID.')
->param('providerType', '', new WhiteList([MESSAGE_TYPE_EMAIL, MESSAGE_TYPE_SMS, MESSAGE_TYPE_PUSH]), 'The target provider type. Can be one of the following: `email`, `sms` or `push`.')
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
->param('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
->inject('queueForEvents')
->inject('response')
->inject('dbForProject')
->action(function (string $targetId, string $userId, string $providerType, string $identifier, string $providerId, string $name, Event $queueForEvents, Response $response, Database $dbForProject) {
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
$provider = new Document();
if ($providerType === MESSAGE_TYPE_PUSH) {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
}
switch ($providerType) {
case 'email':
$validator = new Email();
if (!$validator->isValid($identifier)) {
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
break;
case MESSAGE_TYPE_SMS:
$validator = new Phone();
if (!$validator->isValid($identifier)) {
throw new Exception(Exception::GENERAL_INVALID_PHONE);
}
break;
case MESSAGE_TYPE_PUSH:
break;
default:
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
}
$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 ?? null,
'providerInternalId' => $provider->getInternalId() ?? null,
'providerType' => $providerType,
'userId' => $userId,
'userInternalId' => $user->getInternalId(),
'identifier' => $identifier,
'name' => ($name !== '') ? $name : null,
]));
} catch (Duplicate) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
->setParam('targetId', $target->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($target, Response::MODEL_TARGET);
});
App::get('/v1/users')
->desc('List users')
->groups(['api', 'users'])
@@ -482,6 +641,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'])
@@ -646,6 +837,53 @@ 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.')
->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Users::ALLOWED_ATTRIBUTES), true)
->inject('response')
->inject('dbForProject')
->action(function (string $userId, array $queries, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
$queries[] = Query::equal('userId', [$userId]);
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]);
$cursor = reset($cursor);
if ($cursor) {
$targetId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('targets', $targetId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Target '{$targetId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$response->dynamic(new Document([
'targets' => $dbForProject->find('targets', $queries),
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_TARGET_LIST);
});
App::get('/v1/users/identities')
->desc('List Identities')
->groups(['api', 'users'])
@@ -861,12 +1099,16 @@ App::patch('/v1/users/:userId/password')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User ID.')
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->param('password', '', fn ($project, $passwordsDictionary10k) => new PasswordDictionary($passwordsDictionary10k, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary10k'])
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $userId, string $password, Response $response, Document $project, Database $dbForProject, Event $queueForEvents) {
->inject('passwordsDictionary10k')
->inject('passwordsDictionary100k')
->inject('passwordsDictionary1M')
->inject('passwordsDictionary10M')
->action(function (string $userId, string $password, Response $response, Document $project, Database $dbForProject, Event $queueForEvents, array $passwordsDictionary10k, array $passwordsDictionary100k, array $passwordsDictionary1M, array $passwordsDictionary10M) {
$user = $dbForProject->getDocument('users', $userId);
@@ -880,6 +1122,32 @@ App::patch('/v1/users/:userId/password')
throw new Exception(Exception::USER_PASSWORD_PERSONAL_DATA);
}
}
if ($project->getAttribute('auths', [])['passwordAi'] ?? false) {
$passwordAiValidator = new PasswordAi();
if (!$passwordAiValidator->isValid($password)) {
throw new Exception(Exception::USER_PASSWORD_AI);
}
}
if ($project->getAttribute('auths', [])['passwordDictionary'] ?? false) {
$passwordDictionaryLength = $project->getAttribute('auths', [])['passwordDictionaryLength'] ?? '10k';
if ($passwordDictionaryLength == '10k') {
$passwordDictionary = $passwordsDictionary10k;
} elseif ($passwordDictionaryLength == '100k') {
$passwordDictionary = $passwordsDictionary100k;
} elseif ($passwordDictionaryLength == '1m') {
$passwordDictionary = $passwordsDictionary1M;
} elseif ($passwordDictionaryLength == '10m') {
$passwordDictionary = $passwordsDictionary10M;
} else {
throw new Exception('Password dictionary length is not supported');
}
$passwordDictionaryValidator = new PasswordDictionary($passwordDictionary, true);
if (!$passwordDictionaryValidator->isValid($password)) {
throw new Exception(Exception::USER_PASSWORD_DICTIONARY);
}
}
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
@@ -949,6 +1217,16 @@ App::patch('/v1/users/:userId/email')
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
$target = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
if ($target instanceof Document && !$target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
$oldEmail = $user->getAttribute('email');
$user
->setAttribute('email', $email)
->setAttribute('emailVerification', false)
@@ -957,6 +1235,15 @@ App::patch('/v1/users/:userId/email')
try {
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
/**
* @var Document $oldTarget
*/
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email));
}
$dbForProject->deleteCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
}
@@ -994,13 +1281,32 @@ App::patch('/v1/users/:userId/phone')
throw new Exception(Exception::USER_NOT_FOUND);
}
$oldPhone = $user->getAttribute('phone');
$user
->setAttribute('phone', $number)
->setAttribute('phoneVerification', false)
;
$target = $dbForProject->findOne('targets', [
Query::equal('identifier', [$number]),
]);
if ($target instanceof Document && !$target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
}
try {
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
/**
* @var Document $oldTarget
*/
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $number));
}
$dbForProject->deleteCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS);
}
@@ -1080,6 +1386,96 @@ App::patch('/v1/users/:userId/prefs')
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
});
App::patch('/v1/users/:userId/targets/:targetId')
->desc('Update User target')
->groups(['api', 'users'])
->label('audits.event', 'target.update')
->label('audits.resource', 'target/{response.$id}')
->label('event', 'users.[userId].targets.[targetId].update')
->label('scope', 'targets.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'users')
->label('sdk.method', 'updateTarget')
->label('sdk.description', '/docs/references/users/update-target.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TARGET)
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)', true)
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
->param('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
->inject('queueForEvents')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $targetId, string $identifier, string $providerId, string $name, Event $queueForEvents, Response $response, Database $dbForProject) {
$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);
}
if ($identifier) {
$providerType = $target->getAttribute('providerType');
switch ($providerType) {
case 'email':
$validator = new Email();
if (!$validator->isValid($identifier)) {
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
break;
case MESSAGE_TYPE_SMS:
$validator = new Phone();
if (!$validator->isValid($identifier)) {
throw new Exception(Exception::GENERAL_INVALID_PHONE);
}
break;
case MESSAGE_TYPE_PUSH:
break;
default:
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
}
$target->setAttribute('identifier', $identifier);
}
if ($providerId) {
$provider = $dbForProject->getDocument('providers', $providerId);
if ($provider->isEmpty()) {
throw new Exception(Exception::PROVIDER_NOT_FOUND);
}
$target->setAttribute('providerId', $provider->getId());
$target->setAttribute('providerInternalId', $provider->getInternalId());
}
if ($name) {
$target->setAttribute('name', $name);
}
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
$dbForProject->deleteCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
->setParam('targetId', $target->getId());
$response
->dynamic($target, Response::MODEL_TARGET);
});
App::delete('/v1/users/:userId/sessions/:sessionId')
->desc('Delete user session')
->groups(['api', 'users'])
@@ -1210,6 +1606,53 @@ App::delete('/v1/users/:userId')
$response->noContent();
});
App::delete('/v1/users/:userId/targets/:targetId')
->desc('Delete user target')
->groups(['api', 'users'])
->label('audits.event', 'target.delete')
->label('audits.resource', 'target/{request.$targetId}')
->label('event', 'users.[userId].targets.[targetId].delete')
->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('queueForEvents')
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $targetId, Event $queueForEvents, Response $response, Database $dbForProject) {
$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);
}
$dbForProject->deleteDocument('targets', $target->getId());
$dbForProject->deleteCachedDocument('users', $user->getId());
$queueForEvents
->setParam('userId', $user->getId())
->setParam('targetId', $target->getId());
$response->noContent();
});
App::delete('/v1/users/identities/:identityId')
->desc('Delete Identity')
->groups(['api', 'users'])
@@ -1251,7 +1694,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('oAuthProviders', [])))), true), 'Provider Name.', true)
->inject('response')
->inject('dbForProject')
->inject('register')
+29 -10
View File
@@ -154,8 +154,7 @@ App::init()
$response
->addHeader('X-RateLimit-Limit', $limit)
->addHeader('X-RateLimit-Remaining', $remaining)
->addHeader('X-RateLimit-Reset', $time)
;
->addHeader('X-RateLimit-Reset', $time);
}
$enabled = App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
@@ -170,6 +169,27 @@ App::init()
}
}
/*
* Session refresh
*/
if ($project->getAttribute('auths', [])['renewal'] ?? false) {
if ($user && !$user->isEmpty()) {
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$currentSessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration);
if ($currentSessionId) {
$currentSession = $dbForProject->getDocument('sessions', $currentSessionId);
if (!$currentSession->isEmpty()) {
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $authDuration));
$currentSession = $currentSession->setAttribute('expire', $expire);
$dbForProject->updateDocument('sessions', $currentSession->getId(), $currentSession);
$dbForProject->deleteCachedDocument('users', $user->getId());
}
}
}
}
/*
* Background Jobs
*/
@@ -240,7 +260,7 @@ App::init()
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
}
if ($file->isEmpty()) {
@@ -252,8 +272,7 @@ App::init()
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($data['contentType'])
->send(base64_decode($data['payload']))
;
->send(base64_decode($data['payload']));
} else {
$response->addHeader('X-Appwrite-Cache', 'miss');
}
@@ -495,7 +514,7 @@ App::shutdown()
'resource' => $resource,
'contentType' => $response->getContentType(),
'payload' => base64_encode($data['payload']),
]) ;
]);
$signature = md5($data);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
@@ -503,10 +522,10 @@ App::shutdown()
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'accessedAt' => $now,
'signature' => $signature,
'$id' => $key,
'resource' => $resource,
'accessedAt' => $now,
'signature' => $signature,
])));
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
+158 -28
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;
@@ -47,13 +47,7 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Locale\Locale;
use Utopia\DSN\DSN;
use Utopia\Messaging\Adapters\SMS\Mock;
use Appwrite\GraphQL\Promises\Adapter\Swoole;
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\Registry\Registry;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Backblaze;
@@ -83,6 +77,7 @@ use Utopia\Validator\Range;
use Utopia\Validator\IP;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
use Utopia\CLI\Console;
const APP_NAME = 'Appwrite';
const APP_DOMAIN = 'appwrite.io';
@@ -103,6 +98,7 @@ const APP_LIMIT_COMPRESSION = 20000000; //20MB
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
const APP_LIMIT_SUBQUERY = 1000;
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1000000;
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
@@ -173,11 +169,14 @@ 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_TOPIC = 'topic';
// Compression type
const COMPRESSION_TYPE_NONE = 'none';
const COMPRESSION_TYPE_GZIP = 'gzip';
const COMPRESSION_TYPE_ZSTD = 'zstd';
// Mail Types
const MAIL_TYPE_VERIFICATION_URL = 'verificationUrl';
const MAIL_TYPE_VERIFICATION_CODE = 'verificationCode';
const MAIL_TYPE_VERIFICATION = 'verification';
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
const MAIL_TYPE_RECOVERY = 'recovery';
@@ -193,6 +192,10 @@ const MAX_OUTPUT_CHUNK_SIZE = 2 * 1024 * 1024; // 2MB
// Function headers
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
// Message types
const MESSAGE_TYPE_EMAIL = 'email';
const MESSAGE_TYPE_SMS = 'sms';
const MESSAGE_TYPE_PUSH = 'push';
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
@@ -237,7 +240,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('oAuthProviders', __DIR__ . '/config/oAuthProviders.php');
Config::load('platforms', __DIR__ . '/config/platforms.php');
Config::load('collections', __DIR__ . '/config/collections.php');
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
@@ -526,6 +529,107 @@ 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()]),
Query::limit(APP_LIMIT_SUBQUERY)
]));
}
);
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()]),
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
])
));
if (\count($targetIds) > 0) {
return $database->find('targets', [Query::equal('$id', $targetIds)]);
}
return [];
}
);
Database::addFilter(
'providerSearch',
function (mixed $value, Document $provider) {
$searchValues = [
$provider->getId(),
$provider->getAttribute('name', ''),
$provider->getAttribute('provider', ''),
$provider->getAttribute('type', '')
];
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'topicSearch',
function (mixed $value, Document $topic) {
$searchValues = [
$topic->getId(),
$topic->getAttribute('name', ''),
$topic->getAttribute('description', ''),
];
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'messageSearch',
function (mixed $value, Document $message) {
$searchValues = [
$message->getId(),
$message->getAttribute('description', ''),
$message->getAttribute('status', ''),
];
$data = \json_decode($message->getAttribute('data', []), true);
$providerType = $message->getAttribute('providerType', '');
if ($providerType === MESSAGE_TYPE_EMAIL) {
$searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]);
} elseif ($providerType === MESSAGE_TYPE_SMS) {
$searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]);
} else {
$searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]);
}
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
/**
* DB Formats
*/
@@ -823,12 +927,34 @@ $register->set('smtp', function () {
$register->set('geodb', function () {
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2023-01.mmdb');
});
$register->set('passwordsDictionary', function () {
$register->set('passwordsDictionary10k', function () {
$content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$register->set('passwordsDictionary100k', function () {
$content = \file_get_contents(__DIR__ . '/assets/security/100k-common-passwords.txt');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$register->set('passwordsDictionary1M', function () {
$content = \file_get_contents(__DIR__ . '/assets/security/1m-common-passwords.txt');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$register->set('passwordsDictionary10M', function () {
$content = \file_get_contents(__DIR__ . '/assets/security/1m-common-passwords.txt');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$register->set('promiseAdapter', function () {
return new Swoole();
});
@@ -887,7 +1013,7 @@ App::setResource('queue', function (Group $pools) {
return $pools->get('queue')->pop()->getResource();
}, ['pools']);
App::setResource('queueForMessaging', function (Connection $queue) {
return new Phone($queue);
return new Messaging($queue);
}, ['queue']);
App::setResource('queueForMails', function (Connection $queue) {
return new Mail($queue);
@@ -1100,13 +1226,17 @@ App::setResource('console', function () {
'invites' => App::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
'limit' => (App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
'notify' => true,
],
'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
'authProviders' => [
'oAuthProviders' => [
'githubEnabled' => true,
'githubSecret' => App::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', ''),
'supabaseEnabled' => true,
'supabaseSecret' => App::getEnv('_APP_CONSOLE_SUPABASE_SECRET', ''),
'supabaseAppid' => App::getEnv('_APP_CONSOLE_SUPABASE_APP_ID', ''),
],
]);
}, []);
@@ -1322,26 +1452,26 @@ App::setResource('geodb', function ($register) {
return $register->get('geodb');
}, ['register']);
App::setResource('passwordsDictionary', function ($register) {
App::setResource('passwordsDictionary10k', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary');
return $register->get('passwordsDictionary10k');
}, ['register']);
App::setResource('sms', function () {
$dsn = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
$user = $dsn->getUser();
$secret = $dsn->getPassword();
return 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
};
});
App::setResource('passwordsDictionary100k', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary100k');
}, ['register']);
App::setResource('passwordsDictionary1M', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary1M');
}, ['register']);
App::setResource('passwordsDictionary10M', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary10M');
}, ['register']);
App::setResource('servers', function () {
$platforms = Config::getParam('platforms');
+1 -2
View File
@@ -12,7 +12,6 @@ use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Migration;
use Appwrite\Event\Phone;
use Appwrite\Platform\Appwrite;
use Appwrite\Usage\Stats;
use Swoole\Runtime;
@@ -129,7 +128,7 @@ Server::setResource('queueForDatabase', function (Connection $queue) {
return new EventDatabase($queue);
}, ['queue']);
Server::setResource('queueForMessaging', function (Connection $queue) {
return new Phone($queue);
return new Messaging($queue);
}, ['queue']);
Server::setResource('queueForMails', function (Connection $queue) {
return new Mail($queue);
Generated
+39 -38
View File
@@ -156,11 +156,11 @@
},
{
"name": "appwrite/php-runtimes",
"version": "0.13.1",
"version": "0.13.2",
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "b584d19cdcd82737d0ee5c34d23de791f5ed3610"
"reference": "214a37c2c66e0f2bc9c30fdfde66955d9fd084a1"
},
"require": {
"php": ">=8.0",
@@ -195,7 +195,7 @@
"php",
"runtimes"
],
"time": "2023-10-16T15:39:53+00:00"
"time": "2023-11-22T15:36:00+00:00"
},
{
"name": "chillerlan/php-qrcode",
@@ -1465,7 +1465,7 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.3.0",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
@@ -1512,7 +1512,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
},
"funding": [
{
@@ -2217,16 +2217,16 @@
},
{
"name": "utopia-php/logger",
"version": "0.3.1",
"version": "0.3.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/logger.git",
"reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc"
"reference": "ba763c10688fe2ed715ad2bed3f13d18dfec6253"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc",
"reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc",
"url": "https://api.github.com/repos/utopia-php/logger/zipball/ba763c10688fe2ed715ad2bed3f13d18dfec6253",
"reference": "ba763c10688fe2ed715ad2bed3f13d18dfec6253",
"shasum": ""
},
"require": {
@@ -2264,9 +2264,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/logger/issues",
"source": "https://github.com/utopia-php/logger/tree/0.3.1"
"source": "https://github.com/utopia-php/logger/tree/0.3.2"
},
"time": "2023-02-10T15:52:50+00:00"
"time": "2023-11-22T14:45:43+00:00"
},
{
"name": "utopia-php/messaging",
@@ -3136,16 +3136,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.35.2",
"version": "0.35.3",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b"
"reference": "4c431d5324a8f8cd2cab9a5515c170a5b427d44c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2dfe0430a64ffd2a07078d83b20144b871acac3b",
"reference": "2dfe0430a64ffd2a07078d83b20144b871acac3b",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/4c431d5324a8f8cd2cab9a5515c170a5b427d44c",
"reference": "4c431d5324a8f8cd2cab9a5515c170a5b427d44c",
"shasum": ""
},
"require": {
@@ -3181,9 +3181,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.35.2"
"source": "https://github.com/appwrite/sdk-generator/tree/0.35.3"
},
"time": "2023-09-14T14:59:50+00:00"
"time": "2023-11-12T05:56:27+00:00"
},
{
"name": "doctrine/deprecations",
@@ -3890,16 +3890,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.24.2",
"version": "1.24.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "bcad8d995980440892759db0c32acae7c8e79442"
"reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442",
"reference": "bcad8d995980440892759db0c32acae7c8e79442",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6bd0c26f3786cd9b7c359675cb789e35a8e07496",
"reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496",
"shasum": ""
},
"require": {
@@ -3931,9 +3931,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.4"
},
"time": "2023-09-26T12:28:12+00:00"
"time": "2023-11-26T18:29:22+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -5676,16 +5676,16 @@
},
{
"name": "theseer/tokenizer",
"version": "1.2.1",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"shasum": ""
},
"require": {
@@ -5714,7 +5714,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
"source": "https://github.com/theseer/tokenizer/tree/1.2.1"
"source": "https://github.com/theseer/tokenizer/tree/1.2.2"
},
"funding": [
{
@@ -5722,30 +5722,31 @@
"type": "github"
}
],
"time": "2021-07-28T10:34:58+00:00"
"time": "2023-11-20T00:12:19+00:00"
},
{
"name": "twig/twig",
"version": "v3.7.1",
"version": "v3.8.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554"
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
"reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.3"
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
},
"type": "library",
"autoload": {
@@ -5781,7 +5782,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.7.1"
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
},
"funding": [
{
@@ -5793,7 +5794,7 @@
"type": "tidelift"
}
],
"time": "2023-08-28T11:09:02+00:00"
"time": "2023-11-21T18:54:41+00:00"
}
],
"aliases": [],
@@ -5822,5 +5823,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.3.0"
}
+13 -3
View File
@@ -188,7 +188,11 @@ services:
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_ASSISTANT_OPENAI_API_KEY
- _APP_MESSAGE_SMS_TEST_DSN
- _APP_MESSAGE_EMAIL_TEST_DSN
- _APP_MESSAGE_PUSH_TEST_DSN
- _APP_CONSOLE_SUPABASE_SECRET
- _APP_CONSOLE_SUPABASE_APP_ID
appwrite-realtime:
entrypoint: realtime
<<: *x-logging
@@ -568,14 +572,20 @@ services:
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _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
- _APP_SMS_FROM
- _APP_SMS_PROVIDER
appwrite-worker-migrations:
entrypoint: worker-migrations
+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/oAuthProviders.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 `oAuthProviders.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/oAuthProviders.php`.
```php
<?php
+1
View File
@@ -32,6 +32,7 @@
<directory>./tests/e2e/Services/Projects</directory>
<directory>./tests/e2e/Services/Storage</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>
+1 -1
View File
@@ -363,7 +363,7 @@ class Auth
$session->isSet('secret') &&
$session->isSet('provider') &&
$session->getAttribute('secret') === self::hash($secret) &&
DateTime::formatTz(DateTime::addSeconds(new \DateTime($session->getCreatedAt()), $expires)) >= DateTime::formatTz(DateTime::now())
DateTime::formatTz($session->getAttribute('expire')) >= DateTime::formatTz(DateTime::now())
) {
return $session->getId();
}
+182
View File
@@ -0,0 +1,182 @@
<?php
namespace Appwrite\Auth\OAuth2;
use Appwrite\Auth\OAuth2;
// Reference Material
// https://supabase.com/docs/guides/platform/oauth-apps/build-a-supabase-integration
class Supabase extends OAuth2
{
/**
* @var string
*/
private string $endpoint = 'https://api.supabase.com/v1';
/**
* @var array
*/
protected array $user = [];
/**
* @var array
*/
protected array $tokens = [];
/**
* @return string
*/
public function getName(): string
{
return 'supabase';
}
/**
* @return string
*/
public function getLoginURL(): string
{
$url = $this->endpoint . '/oauth/authorize?' .
\http_build_query([
'response_type' => 'code',
'client_id' => $this->appID,
'state' => \json_encode($this->state),
'redirect_uri' => $this->callback
]);
return $url;
}
/**
* @param string $code
*
* @return array
*/
protected function getTokens(string $code): array
{
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->callback,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
])
), true);
}
return $this->tokens;
}
/**
* @param string $refreshToken
*
* @return array
*/
public function refreshTokens(string $refreshToken): array
{
$this->tokens = \json_decode($this->request(
'POST',
$this->endpoint . '/oauth/token',
['Content-Type: application/x-www-form-urlencoded'],
\http_build_query([
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $this->appID,
'client_secret' => $this->appSecret,
])
), true);
if (empty($this->tokens['refresh_token'])) {
$this->tokens['refresh_token'] = $refreshToken;
}
return $this->tokens;
}
/**
* @param string $accessToken
*
* @return string
*/
public function getUserID(string $accessToken): string
{
$user = $this->getUser($accessToken);
return $user['id'] ?? '';
}
/**
* @param string $accessToken
*
* @return string
*/
public function getUserEmail(string $accessToken): string
{
$user = $this->getUser($accessToken);
return $user['email'] ?? '';
}
/**
* Check if the OAuth email is verified
*
* @link https://discord.com/developers/docs/resources/user
*
* @param string $accessToken
*
* @return bool
*/
public function isEmailVerified(string $accessToken): bool
{
$user = $this->getUser($accessToken);
if ($user['verified'] ?? false) {
return true;
}
return false;
}
/**
* @param string $accessToken
*
* @return string
*/
public function getUserName(string $accessToken): string
{
$user = $this->getUser($accessToken);
return $user['username'] ?? '';
}
/**
* @param string $accessToken
*
* @return array
*/
protected function getUser(string $accessToken): array
{
if (empty($this->user)) {
$user = $this->request(
'GET',
$this->endpoint . '/organizations',
['Authorization: Bearer ' . \urlencode($accessToken)]
);
$this->user = \json_decode($user, true);
$this->user = [
'username' => $this->user[0]['name'],
'verified' => false,
'email' => $this->user[0]['name'] . '@supabase.internal',
'id' => $this->user[0]['id']
];
}
return $this->user;
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
namespace Appwrite\Auth\Validator;
/**
* Password.
*
* Validates user password string
*/
class PasswordAi extends Password
{
public function __construct()
{
}
/**
* Get Description.
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return 'Password must be at least 8 characters and must be approved by AI.';
}
/**
* Is valid.
*
* @param mixed $value
*
* @return bool
*/
public function isValid($value): bool
{
if (!parent::isValid($value)) {
return false;
}
$prompt = 'My password is "' . $value . '". Check my password and tell me if it could include day, year, month, date, name, surname, animal name, location, city, animal breed, or state in any shape or form. Answer "Yes" if it does, and "no" if it doesnt. Only answer with 1 word.';
$answer = '';
$ch = curl_init('http://appwrite-assistant:3003/');
$responseHeaders = [];
$query = json_encode(['prompt' => $prompt]);
$headers = ['accept: text/event-stream'];
$handleEvent = function ($ch, $data) use ($answer) {
$answer .= $data;
return \strlen($data);
};
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $handleEvent);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 9000);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) {
$len = strlen($header);
$header = explode(':', $header, 2);
if (count($header) < 2) { // ignore invalid headers
return $len;
}
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
return $len;
});
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
curl_exec($ch);
curl_close($ch);
if ($answer !== 'Yes') {
return false;
}
return true;
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_STRING;
}
}
+173
View File
@@ -0,0 +1,173 @@
<?php
namespace Appwrite\Event;
use Utopia\Database\Document;
use Utopia\Queue\Connection;
use Utopia\Queue\Client;
class Messaging extends Event
{
protected ?string $messageId = null;
protected ?Document $message = null;
protected ?array $recipients = null;
protected ?string $scheduledAt = null;
protected ?string $providerType = null;
public function __construct(protected Connection $connection)
{
parent::__construct($connection);
$this
->setQueue(Event::MESSAGING_QUEUE_NAME)
->setClass(Event::MESSAGING_CLASS_NAME);
}
/**
* Sets recipient for the messaging event.
*
* @param string[] $recipients
* @return self
*/
public function setRecipients(array $recipients): self
{
$this->recipients = $recipients;
return $this;
}
/**
* Returns set recipient for messaging event.
*
* @return string[]
*/
public function getRecipient(): array
{
return $this->recipients;
}
/**
* Sets message document for the messaging event.
*
* @param Document $message
* @return self
*/
public function setMessage(Document $message): self
{
$this->message = $message;
return $this;
}
/**
* Returns message document for the messaging event.
*
* @return string
*/
public function getMessage(): Document
{
return $this->message;
}
/**
* 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 provider type for the messaging event.
*
* @param string $providerType
* @return self
*/
public function setProviderType(string $providerType): self
{
$this->providerType = $providerType;
return $this;
}
/**
* Returns set provider type for the messaging event.
*
* @return string
*/
public function getProviderType(): string
{
return $this->providerType;
}
/**
* Sets Scheduled delivery time for the messaging event.
*
* @param string $scheduledAt
* @return self
*/
public function setScheduledAt(string $scheduledAt): self
{
$this->scheduledAt = $scheduledAt;
return $this;
}
/**
* Returns set Delivery Time for the messaging event.
*
* @return string
*/
public function getScheduledAt(): string
{
return $this->scheduledAt;
}
/**
* 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
{
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
'project' => $this->project,
'user' => $this->user,
'messageId' => $this->messageId,
'message' => $this->message,
'recipients' => $this->recipients,
'providerType' => $this->providerType,
]);
}
}
-87
View File
@@ -1,87 +0,0 @@
<?php
namespace Appwrite\Event;
use Utopia\Queue\Client;
use Utopia\Queue\Connection;
class Phone extends Event
{
protected string $recipient = '';
protected string $message = '';
public function __construct(protected Connection $connection)
{
parent::__construct($connection);
$this
->setQueue(Event::MESSAGING_QUEUE_NAME)
->setClass(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
{
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
'project' => $this->project,
'user' => $this->user,
'payload' => $this->payload,
'recipient' => $this->recipient,
'message' => $this->message,
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
]);
}
}
+26
View File
@@ -55,6 +55,8 @@ class Exception extends \Exception
public const GENERAL_CODES_DISABLED = 'general_codes_disabled';
public const GENERAL_USAGE_DISABLED = 'general_usage_disabled';
public const GENERAL_NOT_IMPLEMENTED = 'general_not_implemented';
public const GENERAL_INVALID_EMAIL = 'general_invalid_email';
public const GENERAL_INVALID_PHONE = 'general_invalid_phone';
/** Users */
public const USER_COUNT_EXCEEDED = 'user_count_exceeded';
@@ -72,6 +74,8 @@ class Exception extends \Exception
public const USER_NOT_FOUND = 'user_not_found';
public const USER_PASSWORD_RECENTLY_USED = 'password_recently_used';
public const USER_PASSWORD_PERSONAL_DATA = 'password_personal_data';
public const USER_PASSWORD_DICTIONARY = 'password_dictionary';
public const USER_PASSWORD_AI = 'password_ai';
public const USER_EMAIL_ALREADY_EXISTS = 'user_email_already_exists';
public const USER_PASSWORD_MISMATCH = 'user_password_mismatch';
public const USER_SESSION_NOT_FOUND = 'user_session_not_found';
@@ -86,6 +90,8 @@ class Exception extends \Exception
public const USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error';
public const USER_EMAIL_ALREADY_VERIFIED = 'user_email_alread_verified';
public const USER_PHONE_ALREADY_VERIFIED = 'user_phone_already_verified';
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';
@@ -233,6 +239,26 @@ class Exception extends \Exception
public const REALTIME_TOO_MANY_MESSAGES = 'realtime_too_many_messages';
public const REALTIME_POLICY_VIOLATION = 'realtime_policy_violation';
/** 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';
public const PROVIDER_INTERNAL_UPDATE_DISABLED = 'provider_internal_update_disabled';
/** 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_MISSING_TARGET = 'message_missing_target';
public const MESSAGE_ALREADY_SENT = 'message_already_sent';
public const MESSAGE_ALREADY_SCHEDULED = 'message_already_scheduled';
protected string $type = '';
protected array $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('oAuthProviders', []))
)
);
+6 -6
View File
@@ -124,23 +124,23 @@ class V16 extends Migration
/**
* Enable OAuth providers with data
*/
$authProviders = $document->getAttribute('authProviders', []);
$oAuthProviders = $document->getAttribute('oAuthProviders', []);
foreach (Config::getParam('providers') as $provider => $value) {
foreach (Config::getParam('oAuthProviders') as $provider => $value) {
if (!$value['enabled']) {
continue;
}
if (($authProviders[$provider . 'Appid'] ?? false) && ($authProviders[$provider . 'Secret'] ?? false)) {
if (array_key_exists($provider . 'Enabled', $authProviders)) {
if (($oAuthProviders[$provider . 'Appid'] ?? false) && ($oAuthProviders[$provider . 'Secret'] ?? false)) {
if (array_key_exists($provider . 'Enabled', $oAuthProviders)) {
continue;
}
$authProviders[$provider . 'Enabled'] = true;
$oAuthProviders[$provider . 'Enabled'] = true;
}
}
$document->setAttribute('authProviders', $authProviders);
$document->setAttribute('oAuthProviders', $oAuthProviders);
break;
}
+23
View File
@@ -2,6 +2,7 @@
namespace Appwrite\Platform\Workers;
use Appwrite\Auth\Auth;
use Executor\Executor;
use Throwable;
use Utopia\Abuse\Abuse;
@@ -149,6 +150,9 @@ class Deletes extends Action
case DELETE_TYPE_SCHEDULES:
$this->deleteSchedules($dbForConsole, $getProjectDB, $datetime);
break;
case DELETE_TYPE_TOPIC:
$this->deleteTopic($project, $getProjectDB, $document);
break;
default:
Console::error('No delete operation for type: ' . $type);
break;
@@ -193,6 +197,25 @@ class Deletes extends Action
);
}
/**
* @param Document $project
* @param callable $getProjectDB
* @param Document $topic
* @throws Exception
*/
protected function deleteTopic(Document $project, callable $getProjectDB, Document $topic)
{
if ($topic->isEmpty()) {
Console::error('Failed to delete subscribers. Topic not found');
return;
}
$dbForProject = $getProjectDB($project);
$this->deleteByGroup('subscribers', [
Query::equal('topicInternalId', [$topic->getInternalId()])
], $dbForProject);
}
/**
* @param Document $project
* @param callable $getProjectDB
+329 -55
View File
@@ -2,30 +2,41 @@
namespace Appwrite\Platform\Workers;
use Exception;
use Appwrite\Extend\Exception;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Database\Helpers\ID;
use Utopia\DSN\DSN;
use Utopia\Messaging\Messages\Sms;
use Utopia\Platform\Action;
use Utopia\Queue\Message;
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\Textmagic;
use Utopia\Messaging\Adapters\SMS\Twilio;
use Utopia\Messaging\Adapters\SMS\Vonage;
use Utopia\Platform\Action;
use Utopia\Queue\Message;
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 function Swoole\Coroutine\batch;
class Messaging extends Action
{
private ?DSN $dsn = null;
private string $user = '';
private string $secret = '';
private string $provider = '';
public static function getName(): string
{
return 'messaging';
return "messaging";
}
/**
@@ -33,25 +44,20 @@ class Messaging extends Action
*/
public function __construct()
{
$this->provider = App::getEnv('_APP_SMS_PROVIDER', '');
if (!empty($this->provider)) {
$this->dsn = new DSN($this->provider);
$this->user = $this->dsn->getUser();
$this->secret = $this->dsn->getPassword();
}
$this
->desc('Messaging worker')
->inject('message')
->callback(fn($message) => $this->action($message));
->inject('dbForProject')
->callback(fn(Message $message, Database $dbForProject) => $this->action($message, $dbForProject));
}
/**
* @param Message $message
* @param Database $dbForProject
* @return void
* @throws Exception
*/
public function action(Message $message): void
public function action(Message $message, Database $dbForProject): void
{
$payload = $message->getPayload() ?? [];
@@ -60,48 +66,316 @@ class Messaging extends Action
return;
}
if (empty($payload['recipient'])) {
Console::error('Recipient arg not found');
if (!\is_null($payload['message']) && !\is_null($payload['recipients'])) {
if ($payload['providerType'] === MESSAGE_TYPE_SMS) {
$this->processInternalSMSMessage(new Document($payload['message']), $payload['recipients']);
}
} else {
$message = $dbForProject->getDocument('messages', $payload['messageId']);
$this->processMessage($dbForProject, $message);
}
}
private function processMessage(Database $dbForProject, Document $message): void
{
$topicsId = $message->getAttribute('topics', []);
$targetsId = $message->getAttribute('targets', []);
$usersId = $message->getAttribute('users', []);
/**
* @var Document[] $recipients
*/
$recipients = [];
if (\count($topicsId) > 0) {
$topics = $dbForProject->find('topics', [Query::equal('$id', $topicsId)]);
foreach ($topics as $topic) {
$recipients = \array_merge($recipients, $topic->getAttribute('targets'));
}
}
if (\count($usersId) > 0) {
$users = $dbForProject->find('users', [Query::equal('$id', $usersId)]);
foreach ($users as $user) {
$recipients = \array_merge($recipients, $user->getAttribute('targets'));
}
}
if (\count($targetsId) > 0) {
$targets = $dbForProject->find('targets', [Query::equal('$id', $targetsId)]);
$recipients = \array_merge($recipients, $targets);
}
$primaryProvider = $dbForProject->findOne('providers', [
Query::equal('enabled', [true]),
Query::equal('type', [$recipients[0]->getAttribute('providerType')]),
]);
/**
* @var array<string, array<string>> $identifiersByProviderId
*/
$identifiersByProviderId = [];
/**
* @var Document[] $providers
*/
$providers = [];
foreach ($recipients as $recipient) {
$providerId = $recipient->getAttribute('providerId');
if (!$providerId && $primaryProvider instanceof Document && !$primaryProvider->isEmpty()) {
$providerId = $primaryProvider->getId();
}
if ($providerId) {
if (!isset($identifiersByProviderId[$providerId])) {
$identifiersByProviderId[$providerId] = [];
}
$identifiersByProviderId[$providerId][] = $recipient->getAttribute('identifier');
}
}
/**
* @var array[] $results
*/
$results = batch(\array_map(function ($providerId) use ($identifiersByProviderId, $providers, $primaryProvider, $message, $dbForProject) {
return function () use ($providerId, $identifiersByProviderId, $providers, $primaryProvider, $message, $dbForProject) {
$provider = new Document();
if ($primaryProvider->getId() === $providerId) {
$provider = $primaryProvider;
} else {
$provider = $dbForProject->getDocument('providers', $providerId, [Query::equal('enabled', [true])]);
if ($provider->isEmpty()) {
$provider = $primaryProvider;
}
}
$providers[] = $provider;
$identifiers = $identifiersByProviderId[$providerId];
$adapter = match ($provider->getAttribute('type')) {
MESSAGE_TYPE_SMS => $this->sms($provider),
MESSAGE_TYPE_PUSH => $this->push($provider),
MESSAGE_TYPE_EMAIL => $this->email($provider),
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
};
$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) {
$deliveredTotal = 0;
$deliveryErrors = [];
$messageData = clone $message;
$messageData->setAttribute('to', $batch);
$data = match ($provider->getAttribute('type')) {
MESSAGE_TYPE_SMS => $this->buildSMSMessage($messageData, $provider),
MESSAGE_TYPE_PUSH => $this->buildPushMessage($messageData),
MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($messageData, $provider),
default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE)
};
try {
$adapter->send($data);
$deliveredTotal += \count($batch);
} catch (\Exception $e) {
$deliveryErrors[] = 'Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage();
} finally {
$batchIndex++;
return [
'deliveredTotal' => $deliveredTotal,
'deliveryErrors' => $deliveryErrors,
];
}
};
}, $batches));
return $results;
};
}, \array_keys($identifiersByProviderId)));
$results = array_merge(...$results);
$deliveredTotal = 0;
$deliveryErrors = [];
foreach ($results as $result) {
$deliveredTotal += $result['deliveredTotal'];
$deliveryErrors = \array_merge($deliveryErrors, $result['deliveryErrors']);
}
$message->setAttribute('deliveryErrors', $deliveryErrors);
if (\count($message->getAttribute('deliveryErrors')) > 0) {
$message->setAttribute('status', 'failed');
} else {
$message->setAttribute('status', 'sent');
}
$message->removeAttribute('to');
foreach ($providers as $provider) {
$message->setAttribute('search', "{$message->getAttribute('search')} {$provider->getAttribute('name')} {$provider->getAttribute('provider')} {$provider->getAttribute('type')}");
}
$message->setAttribute('deliveredTotal', $deliveredTotal);
$message->setAttribute('deliveredAt', DateTime::now());
$dbForProject->updateDocument('messages', $message->getId(), $message);
}
private function processInternalSMSMessage(Document $message, array $recipients): void
{
if (empty(App::getEnv('_APP_SMS_PROVIDER')) || empty(App::getEnv('_APP_SMS_FROM'))) {
Console::info('Skipped SMS processing. No Phone configuration has been set.');
return;
}
if (empty($payload['message'])) {
Console::error('Message arg not found');
return;
}
$sms = match ($this->dsn->getHost()) {
'mock' => new Mock($this->user, $this->secret), // used for tests
'twilio' => new Twilio($this->user, $this->secret),
'text-magic' => new TextMagic($this->user, $this->secret),
'telesign' => new Telesign($this->user, $this->secret),
'msg91' => new Msg91($this->user, $this->secret),
'vonage' => new Vonage($this->user, $this->secret),
default => null
};
if (empty(App::getEnv('_APP_SMS_PROVIDER'))) {
Console::error('Skipped sms processing. No Phone provider has been set.');
return;
}
$smsDSN = new DSN(App::getEnv('_APP_SMS_PROVIDER'));
$host = $smsDSN->getHost();
$password = $smsDSN->getPassword();
$user = $smsDSN->getUser();
$from = App::getEnv('_APP_SMS_FROM');
if (empty($from)) {
Console::error('Skipped sms processing. No phone number has been set.');
return;
}
$provider = new Document([
'$id' => ID::unique(),
'provider' => $host,
'type' => MESSAGE_TYPE_SMS,
'name' => 'Internal SMS',
'enabled' => true,
'credentials' => match ($host) {
'twilio' => [
'accountSid' => $user,
'authToken' => $password
],
'textmagic' => [
'username' => $user,
'apiKey' => $password
],
'telesign' => [
'username' => $user,
'password' => $password
],
'msg91' => [
'senderId' => $user,
'authKey' => $password
],
'vonage' => [
'apiKey' => $user,
'apiSecret' => $password
],
default => null
},
'options' => [
'from' => $from
]
]);
$message = new SMS(
to: [$payload['recipient']],
content: $payload['message'],
from: $from,
);
$adapter = $this->sms($provider);
try {
$sms->send($message);
} catch (\Exception $error) {
throw new Exception('Error sending message: ' . $error->getMessage(), 500);
}
$maxBatchSize = $adapter->getMaxMessagesPerRequest();
$batches = \array_chunk($recipients, $maxBatchSize);
$batchIndex = 0;
batch(\array_map(function ($batch) use ($message, $provider, $adapter, $batchIndex) {
return function () use ($batch, $message, $provider, $adapter, $batchIndex) {
$message->setAttribute('to', $batch);
$data = $this->buildSMSMessage($message, $provider);
try {
$adapter->send($data);
} catch (\Exception $e) {
Console::error('Failed sending to targets ' . $batchIndex + 1 . '-' . \count($batch) . ' with error: ' . $e->getMessage());
}
};
}, $batches));
}
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']),
'textmagic' => 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);
}
}
@@ -0,0 +1,28 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Messages extends Base
{
public const ALLOWED_ATTRIBUTES = [
'topics',
'users',
'targets',
'providerId',
'deliveredAt',
'deliveredTo',
'deliveryErrors',
'status',
'description',
'data'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('messages', self::ALLOWED_ATTRIBUTES);
}
}
@@ -0,0 +1,22 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Providers extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'provider',
'type',
'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,21 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Targets extends Base
{
public const ALLOWED_ATTRIBUTES = [
'userId',
'providerId',
'identifier',
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('targets', 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);
}
}
@@ -9,6 +9,7 @@ class Users extends Base
'email',
'phone',
'status',
'hash',
'passwordUpdate',
'registration',
'emailVerification',
+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())
+2 -2
View File
@@ -60,8 +60,8 @@ class V13 extends Filter
protected function parseProject($content)
{
$content['providers'] = $content['authProviders'];
unset($content['authProviders']);
$content['providers'] = $content['oAuthProviders'];
unset($content['oAuthProviders']);
return $content;
}
+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['oAuthProviders'] ?? [] as $i => $provider) {
$content['oAuthProviders'][$i]['name'] = \ucfirst($provider['key']);
unset($content['oAuthProviders'][$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,131 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use Utopia\Database\DateTime;
use Utopia\Database\Document as DatabaseDocument;
class Message extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Message ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Message creation time in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Message update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('providerType', [
'type' => self::TYPE_STRING,
'description' => 'Message provider type.',
'default' => '',
'example' => MESSAGE_TYPE_EMAIL,
])
->addRule('topics', [
'type' => self::TYPE_STRING,
'description' => 'Topic IDs set as recipients.',
'default' => '',
'array' => true,
'example' => ['5e5ea5c16897e'],
])
->addRule('users', [
'type' => self::TYPE_STRING,
'description' => 'User IDs set as recipients.',
'default' => '',
'array' => true,
'example' => ['5e5ea5c16897e'],
])
->addRule('targets', [
'type' => self::TYPE_STRING,
'description' => 'Target IDs set as recipients.',
'default' => '',
'array' => true,
'example' => ['5e5ea5c16897e'],
])
->addRule('scheduledAt', [
'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('deliveredTotal', [
'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;
}
}
+50 -13
View File
@@ -132,15 +132,51 @@ class Project extends Model
'default' => false,
'example' => true,
])
->addRule('authPasswordDictionaryLength', [
'type' => self::TYPE_STRING,
'description' => 'How many most commonly used password to check against. Possible values are: 10k, 100k, 1m, 10m',
'default' => '10k',
'example' => '1m',
])
->addRule('authPasswordAi', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to check user\'s password against against AI opinion',
'default' => false,
'example' => true,
])
->addRule('authNotify', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to send notification on new session creation.',
'default' => false,
'example' => true,
])
->addRule('authSessionRefresh', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not sessions are automatically extended to session duration on every request',
'default' => false,
'example' => true,
])
->addRule('authPasswordAi', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to check user\'s password against against AI opinion',
'default' => false,
'example' => true,
])
->addRule('authRenewal', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not sessions are automatically extended to session duration on every request',
'default' => false,
'example' => true,
])
->addRule('authPersonalDataCheck', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to check the user password for similarity with their personal data.',
'default' => false,
'example' => true,
])
->addRule('providers', [
'type' => Response::MODEL_PROVIDER,
'description' => 'List of Providers.',
->addRule('oAuthProviders', [
'type' => Response::MODEL_AUTH_PROVIDER,
'description' => 'List of Auth Providers.',
'default' => [],
'example' => [new \stdClass()],
'array' => true,
@@ -220,8 +256,7 @@ class Project extends Model
'description' => 'SMTP server secure protocol',
'default' => '',
'example' => 'tls',
])
;
]);
$services = Config::getParam('services', []);
$auth = Config::getParam('auth', []);
@@ -236,8 +271,7 @@ class Project extends Model
'description' => $name . ' auth method status',
'example' => true,
'default' => true,
])
;
]);
}
foreach ($services as $service) {
@@ -254,8 +288,7 @@ class Project extends Model
'description' => $name . ' service status',
'example' => true,
'default' => true,
])
;
]);
}
}
@@ -320,6 +353,10 @@ class Project extends Model
$document->setAttribute('authSessionsLimit', $authValues['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT);
$document->setAttribute('authPasswordHistory', $authValues['passwordHistory'] ?? 0);
$document->setAttribute('authPasswordDictionary', $authValues['passwordDictionary'] ?? false);
$document->setAttribute('authPasswordDictionaryLength', $authValues['passwordDictionaryLength'] ?? false);
$document->setAttribute('authPasswordAi', $authValues['passwordAi'] ?? false);
$document->setAttribute('authNotify', $authValues['notify'] ?? false);
$document->setAttribute('authRenewal', $authValues['renewal'] ?? false);
$document->setAttribute('authPersonalDataCheck', $authValues['personalDataCheck'] ?? false);
foreach ($auth as $index => $method) {
@@ -328,9 +365,9 @@ class Project extends Model
$document->setAttribute('auth' . ucfirst($key), $value);
}
// Providers
$providers = Config::getParam('providers', []);
$providerValues = $document->getAttribute('authProviders', []);
// OAuth Providers
$providers = Config::getParam('oAuthProviders', []);
$providerValues = $document->getAttribute('oAuthProviders', []);
$projectProviders = [];
foreach ($providers as $key => $provider) {
@@ -348,7 +385,7 @@ class Project extends Model
]);
}
$document->setAttribute("providers", $projectProviders);
$document->setAttribute('oAuthProviders', $projectProviders);
return $document;
}
+46 -22
View File
@@ -7,44 +7,68 @@ 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('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Provider creation time in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Provider update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->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',
])
->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',
'example' => 'mailgun',
])
->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' => MESSAGE_TYPE_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,85 @@
<?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('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Subscriber creation time in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Subscriber update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('targetId', [
'type' => self::TYPE_STRING,
'description' => 'Target ID.',
'default' => '',
'example' => '259125845563242502',
])
->addRule('target', [
'type' => Response::MODEL_TARGET,
'description' => 'Target.',
'default' => [],
'example' => [
'$id' => '259125845563242502',
'$createdAt' => self::TYPE_DATETIME_EXAMPLE,
'$updatedAt' => self::TYPE_DATETIME_EXAMPLE,
'providerType' => 'email',
'providerId' => '259125845563242502',
'name' => 'ageon-app-email',
'identifier' => 'random-mail@email.org',
'userId' => '5e5ea5c16897e',
],
])
->addRule('userName', [
'type' => self::TYPE_STRING,
'description' => 'User Name.',
'default' => '',
'example' => 'Aegon Targaryen',
])
->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,83 @@
<?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('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Target creation time in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Target update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Target Name.',
'default' => '',
'example' => 'Aegon apple token',
])
->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('providerType', [
'type' => self::TYPE_STRING,
'description' => 'The target provider type. Can be one of the following: `email`, `sms` or `push`.',
'default' => '',
'example' => 'email',
])
->addRule('identifier', [
'type' => self::TYPE_STRING,
'description' => 'The target identifier.',
'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;
}
}
@@ -40,6 +40,12 @@ class Token extends Model
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('securityPhrase', [
'type' => self::TYPE_STRING,
'description' => 'Token security phrase.',
'default' => '',
'example' => 'Tiger Whisper',
])
;
}
@@ -0,0 +1,71 @@
<?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('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Topic creation time in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Topic update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'The name of the topic.',
'default' => '',
'example' => 'events',
])
->addRule('total', [
'type' => self::TYPE_INTEGER,
'description' => 'Total count of subscribers subscribed to topic.',
'default' => 0,
'example' => 100,
])
->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.',
+11 -1
View File
@@ -82,7 +82,17 @@ trait ProjectCustom
'avatars.read',
'health.read',
'rules.read',
'rules.write'
'rules.write',
'targets.read',
'targets.write',
'providers.read',
'providers.write',
'messages.read',
'messages.write',
'topics.write',
'topics.read',
'subscribers.write',
'subscribers.read',
],
]);
+11 -3
View File
@@ -415,7 +415,7 @@ trait AccountBase
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'queries' => [ 'limit(1)' ],
'queries' => ['limit(1)'],
]);
$this->assertEquals($responseLimit['headers']['status-code'], 200);
@@ -432,7 +432,7 @@ trait AccountBase
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'queries' => [ 'offset(1)' ],
'queries' => ['offset(1)'],
]);
$this->assertEquals($responseOffset['headers']['status-code'], 200);
@@ -449,7 +449,7 @@ trait AccountBase
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'queries' => [ 'limit(1)', 'offset(1)' ],
'queries' => ['limit(1)', 'offset(1)'],
]);
$this->assertEquals($responseLimitOffset['headers']['status-code'], 200);
@@ -1115,6 +1115,14 @@ trait AccountBase
$this->assertEquals($response['headers']['status-code'], 204);
$response = $this->client->call(Client::METHOD_GET, '/account/sessions', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 401);
/**
* Test for FAILURE
*/
@@ -2,16 +2,16 @@
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;
use Utopia\DSN\DSN;
use function sleep;
@@ -764,6 +764,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
@@ -6,7 +6,9 @@ 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;
use Utopia\DSN\DSN;
class AccountTest extends Scope
{
@@ -123,6 +125,7 @@ class AccountTest extends Scope
public function testCreatePhoneVerification(): array
{
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$CREATE_PHONE_VERIFICATION);
$graphQLPayload = [
'query' => $query,
+535 -1
View File
@@ -109,6 +109,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';
@@ -199,6 +204,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';
@@ -881,6 +933,55 @@ trait Base
status
}
}';
case self::$CREATE_USER_TARGET:
return 'mutation createUserTarget($userId: String!, $targetId: String!, $providerType: String!, $identifier: String! $providerId: String){
usersCreateTarget(userId: $userId, targetId: $targetId, providerType: $providerType, identifier: $identifier, providerId: $providerId) {
_id
userId
providerType
providerId
identifier
}
}';
case self::$LIST_USER_TARGETS:
return 'query listUserTargets($userId: String!) {
usersListTargets(userId: $userId) {
total
targets {
_id
userId
providerType
providerId
identifier
}
}
}';
case self::$GET_USER_TARGET:
return 'query getUserTarget($userId: String!, $targetId: String!) {
usersGetTarget(userId: $userId, targetId: $targetId) {
_id
userId
providerType
providerId
identifier
}
}';
case self::$UPDATE_USER_TARGET:
return 'mutation updateUserTarget($userId: String!, $targetId: String!, $providerId: String, $identifier: String){
usersUpdateTarget(userId: $userId, targetId: $targetId, providerId: $providerId, identifier: $identifier) {
_id
userId
providerType
providerId
identifier
}
}';
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 {
@@ -1688,6 +1789,439 @@ trait Base
status
}
}';
case self::$CREATE_MAILGUN_PROVIDER:
return 'mutation createMailgunProvider($providerId: String!, $name: String!, $domain: String!, $apiKey: String!, $from: String!, $isEuRegion: Boolean!) {
messagingCreateMailgunProvider(providerId: $providerId, name: $name, domain: $domain, apiKey: $apiKey, from: $from, isEuRegion: $isEuRegion) {
_id
name
provider
type
enabled
}
}';
case self::$CREATE_SENDGRID_PROVIDER:
return 'mutation createSendgridProvider($providerId: String!, $name: String!, $from: String!, $apiKey: String!) {
messagingCreateSendgridProvider(providerId: $providerId, name: $name, from: $from, apiKey: $apiKey) {
_id
name
provider
type
enabled
}
}';
case self::$CREATE_TWILIO_PROVIDER:
return 'mutation createTwilioProvider($providerId: String!, $name: String!, $from: String!, $accountSid: String!, $authToken: String!) {
messagingCreateTwilioProvider(providerId: $providerId, name: $name, from: $from, accountSid: $accountSid, authToken: $authToken) {
_id
name
provider
type
enabled
}
}';
case self::$CREATE_TELESIGN_PROVIDER:
return 'mutation createTelesignProvider($providerId: String!, $name: String!, $from: String!, $username: String!, $password: String!) {
messagingCreateTelesignProvider(providerId: $providerId, name: $name, from: $from, username: $username, password: $password) {
_id
name
provider
type
enabled
}
}';
case self::$CREATE_TEXTMAGIC_PROVIDER:
return 'mutation createTextmagicProvider($providerId: String!, $name: String!, $from: String!, $username: String!, $apiKey: String!) {
messagingCreateTextmagicProvider(providerId: $providerId, name: $name, from: $from, username: $username, apiKey: $apiKey) {
_id
name
provider
type
enabled
}
}';
case self::$CREATE_MSG91_PROVIDER:
return 'mutation createMsg91Provider($providerId: String!, $name: String!, $from: String!, $senderId: String!, $authKey: String!, $enabled: Boolean) {
messagingCreateMsg91Provider(providerId: $providerId, name: $name, from: $from, senderId: $senderId, authKey: $authKey, enabled: $enabled) {
_id
name
provider
type
enabled
}
}';
case self::$CREATE_VONAGE_PROVIDER:
return 'mutation createVonageProvider($providerId: String!, $name: String!, $from: String!, $apiKey: String!, $apiSecret: String!) {
messagingCreateVonageProvider(providerId: $providerId, name: $name, from: $from, apiKey: $apiKey, apiSecret: $apiSecret) {
_id
name
provider
type
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
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
enabled
}
}';
case self::$LIST_PROVIDERS:
return 'query listProviders {
messagingListProviders {
total
providers {
_id
name
provider
type
enabled
}
}
}';
case self::$GET_PROVIDER:
return 'query getProvider($providerId: String!) {
messagingGetProvider(providerId: $providerId) {
_id
name
provider
type
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
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
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
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
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
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
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
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
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
enabled
}
}';
case self::$DELETE_PROVIDER:
return 'mutation deleteProvider($providerId: String!) {
messagingDeleteProvider(providerId: $providerId) {
status
}
}';
case self::$CREATE_TOPIC:
return 'mutation createTopic($topicId: String!, $name: String!, $description: String!) {
messagingCreateTopic(topicId: $topicId, name: $name, description: $description) {
_id
name
description
}
}';
case self::$LIST_TOPICS:
return 'query listTopics {
messagingListTopics {
total
topics {
_id
name
description
}
}
}';
case self::$GET_TOPIC:
return 'query getTopic($topicId: String!) {
messagingGetTopic(topicId: $topicId) {
_id
name
description
}
}';
case self::$UPDATE_TOPIC:
return 'mutation updateTopic($topicId: String!, $name: String!, $description: String!) {
messagingUpdateTopic(topicId: $topicId, name: $name, description: $description) {
_id
name
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
userName
target {
_id
userId
name
providerType
identifier
}
}
}';
case self::$LIST_SUBSCRIBERS:
return 'query listSubscribers($topicId: String!) {
messagingListSubscribers(topicId: $topicId) {
total
subscribers {
_id
targetId
topicId
userName
target {
_id
userId
name
providerType
identifier
}
}
}
}';
case self::$GET_SUBSCRIBER:
return 'query getSubscriber($topicId: String!, $subscriberId: String!) {
messagingGetSubscriber(topicId: $topicId, subscriberId: $subscriberId) {
_id
targetId
topicId
userName
target {
_id
userId
name
providerType
identifier
}
}
}';
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!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String!, $content: String!, $status: String, $description: String, $html: Boolean, $scheduledAt: String) {
messagingCreateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, description: $description, html: $html, scheduledAt: $scheduledAt) {
_id
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$CREATE_SMS:
return 'mutation createSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String!, $status: String, $description: String, $scheduledAt: String) {
messagingCreateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, description: $description, scheduledAt: $scheduledAt) {
_id
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$CREATE_PUSH_NOTIFICATION:
return 'mutation createPushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String!, $body: String!, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $scheduledAt: String) {
messagingCreatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, scheduledAt: $scheduledAt) {
_id
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$LIST_MESSAGES:
return 'query listMessages {
messagingListMessages {
total
messages {
_id
providerType
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
status
description
}
}
}';
case self::$GET_MESSAGE:
return 'query getMessage($messageId: String!) {
messagingGetMessage(messageId: $messageId) {
_id
providerType
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$UPDATE_EMAIL:
return 'mutation updateEmail($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $subject: String, $content: String, $status: String, $description: String, $html: Boolean, $scheduledAt: String) {
messagingUpdateEmail(messageId: $messageId, topics: $topics, users: $users, targets: $targets, subject: $subject, content: $content, status: $status, description: $description, html: $html, scheduledAt: $scheduledAt) {
_id
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$UPDATE_SMS:
return 'mutation updateSMS($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $content: String, $status: String, $description: String, $scheduledAt: String) {
messagingUpdateSMS(messageId: $messageId, topics: $topics, users: $users, targets: $targets, content: $content, status: $status, description: $description, scheduledAt: $scheduledAt) {
_id
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
status
description
}
}';
case self::$UPDATE_PUSH_NOTIFICATION:
return 'mutation updatePushNotification($messageId: String!, $topics: [String!], $users: [String!], $targets: [String!], $title: String, $body: String, $data: Json, $action: String, $icon: String, $sound: String, $color: String, $tag: String, $badge: String, $status: String, $description: String, $scheduledAt: String) {
messagingUpdatePushNotification(messageId: $messageId, topics: $topics, users: $users, targets: $targets, title: $title, body: $body, data: $data, action: $action, icon: $icon, sound: $sound, color: $color, tag: $tag, badge: $badge, status: $status, description: $description, scheduledAt: $scheduledAt) {
_id
topics
users
targets
scheduledAt
deliveredAt
deliveryErrors
deliveredTotal
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) {
@@ -1940,7 +2474,7 @@ trait Base
protected string $stdout = '';
protected string $stderr = '';
protected function packageCode($folder)
protected function packageCode($folder): void
{
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
}
File diff suppressed because it is too large Load Diff
+146
View File
@@ -45,6 +45,56 @@ 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.com',
'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'],
'providerType' => 'email',
'providerId' => $providerId,
'identifier' => 'random-email@mail.org',
]
];
$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('random-email@mail.org', $target['body']['data']['usersCreateTarget']['identifier']);
return $target['body']['data']['usersCreateTarget'];
}
public function testGetUsers()
{
$projectId = $this->getProject()['$id'];
@@ -176,6 +226,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(2, $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('random-email@mail.org', $target['body']['data']['usersGetTarget']['identifier']);
}
public function testUpdateUserStatus()
{
$projectId = $this->getProject()['$id'];
@@ -360,6 +458,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' => 'random-email1@mail.org',
],
];
$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('random-email1@mail.org', $target['body']['data']['usersUpdateTarget']['identifier']);
}
public function testDeleteUserSessions()
{
$projectId = $this->getProject()['$id'];
@@ -407,6 +530,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,413 @@
<?php
namespace Tests\E2E\Services\Messaging;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideConsole;
use Utopia\Database\Helpers\ID;
class MessagingConsoleClientTest extends Scope
{
use MessagingBase;
use ProjectCustom;
use SideConsole;
/**
* @depends testListProviders
*/
public function testGetProviderLogs(array $providers): void
{
/**
* Test for SUCCESS
*/
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $providers[0]['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$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' => ID::unique(),
'name' => 'Sengrid1',
'apiKey' => 'my-apikey',
'from' => 'sender-email@my-domain.com',
]);
$this->assertEquals(201, $provider['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/providers/sendgrid/' . $provider['body']['$id'], \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'sendgrid' => [
'name' => 'Sendgrid2',
]]);
$this->assertEquals(200, $response['headers']['status-code']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$this->assertCount(2, $logs['body']['logs']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'offset(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/providers/' . $provider['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
}
/**
* @depends testListTopic
*/
public function testGetTopicLogs(string $topicId): void
{
/**
* Test for SUCCESS
*/
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topicId . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$topic = $this->client->call(Client::METHOD_POST, '/messaging/topics', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'topicId' => ID::unique(),
'name' => 'my-app',
'description' => 'web app'
]);
$this->assertEquals(201, $topic['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/topics/' . $topic['body']['$id'], \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'description' => 'updated-description'
]);
$this->assertEquals(200, $response['headers']['status-code']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertCount(2, $logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'offset(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
}
/**
* @depends testSendEmail
*/
public function testGetMessageLogs(array $email): void
{
/**
* Test for SUCCESS
*/
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$email = $this->client->call(Client::METHOD_POST, '/messaging/messages/email', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'messageId' => ID::unique(),
'status' => 'draft',
'topics' => [ID::unique()],
'subject' => 'Khali beats Undertaker',
'content' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
]);
$this->assertEquals(201, $email['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/messages/email/' . $email['body']['$id'], \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'subject' => 'Khali beats John Cena!',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$this->assertCount(2, $logs['body']['logs']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertIsNumeric($logs['body']['total']);
$logs = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(1)', 'offset(1)'],
]);
$this->assertEquals($logs['headers']['status-code'], 200);
$this->assertIsArray($logs['body']['logs']);
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
$this->assertIsNumeric($logs['body']['total']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['limit(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['offset(-1)']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['equal("$id", "asdf")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['orderAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
$response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'] . '/logs', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['cursorAsc("$id")']
]);
$this->assertEquals($response['headers']['status-code'], 400);
}
}
@@ -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/oAuthProviders.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']['oAuthProviders'] 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']['oAuthProviders'] as $responseProvider) {
if ($responseProvider['key'] === $key) {
// On first provider, test enabled=false
$this->assertEquals($i !== 0, $responseProvider['enabled']);
+94
View File
@@ -23,6 +23,7 @@ trait UsersBase
'password' => 'password',
'name' => 'Cristiano Ronaldo',
], false);
$this->assertEquals($user['headers']['status-code'], 201);
// Test empty prefs is object not array
$bodyString = $user['body'];
@@ -1223,6 +1224,99 @@ 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' => ID::unique(),
'name' => 'Sengrid1',
'apiKey' => 'my-apikey',
'from' => 'from@domain.com',
]);
$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'],
'providerType' => 'email',
'identifier' => 'random-email@mail.org',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals($provider['body']['$id'], $response['body']['providerId']);
$this->assertEquals('random-email@mail.org', $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'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'identifier' => 'random-email1@mail.org',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('random-email1@mail.org', $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(2, \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(1, $response['body']['total']);
}
/**
* @depends testGetUser
*/
@@ -154,9 +154,9 @@ class V16Test extends TestCase
public function projectProvider(): array
{
return [
'providers' => [
'oAuthProviders' => [
[
'providers' => [
'oAuthProviders' => [
[
'key' => 'github',
'name' => 'GitHub',
@@ -167,7 +167,7 @@ class V16Test extends TestCase
],
],
[
'providers' => [
'oAuthProviders' => [
[
'name' => 'Github',
'appId' => 'client_id',