Compare commits

..
Author SHA1 Message Date
fogelito b46fc77053 paramQueries 2026-02-26 13:07:44 +02:00
HemachandarandGitHub a76a42d2dc Change validation order in delete memberships API (#11410) 2026-02-26 12:25:28 +05:30
HemachandarandGitHub 76965252d8 Move teams API to Modules (#11358)
* Move teams API to Modules

* lint

* Move team prefs & logs API to Modules (#11359)

* Move team prefs & logs API to Modules

* format

* missin desc

* Move team memberships API to Modules (#11362)

* Move team memberships API to Modules

* fix config dir

* Cloud parity

* params

* Cloud conflicts

* refactor

* prop

* refactor

* set teamId

* feedback

* feedback 2

* fix url-encoding
2026-02-26 11:18:29 +05:30
Jake BarnbyandGitHub 0272a7f337 Merge pull request #11404 from appwrite/realtime-channels-tablesdb-fix 2026-02-26 03:43:50 +00:00
Jake BarnbyandGitHub 52d5ae50d9 Merge pull request #11405 from appwrite/patch-text-types-return 2026-02-26 03:33:52 +00:00
HemachandarandGitHub cfddef3706 Upgrade utopia-php/vcs (#11401)
* Upgrade `utopia-php/vcs`

* fix

* bump
2026-02-25 19:54:04 +05:30
ArnabChatterjee20k ca8d613614 updated test 2026-02-25 19:46:09 +05:30
ArnabChatterjee20k 8c579cf88d updated string filtering in the filters 2026-02-25 19:29:03 +05:30
ArnabChatterjee20k a57d970840 Add support for tablesdb in event generation and realtime channels 2026-02-25 18:58:33 +05:30
Atharva DeosthaleandGitHub 5d6e43e01b Merge pull request #11371 from appwrite/add-cursor-plugin 2026-02-25 17:11:20 +05:30
Atharva Deosthale c1e2d4593d Merge remote-tracking branch 'origin/1.8.x' into add-cursor-plugin
# Conflicts:
#	composer.lock
2026-02-25 15:41:05 +05:30
Jake BarnbyandGitHub 32ef15f89a Merge pull request #11398 from appwrite/fix-cast 2026-02-25 09:58:35 +00:00
Jake Barnby 7dc50b43dc Fix method 2026-02-25 22:20:52 +13:00
Jake Barnby a43e94990b Lint 2026-02-25 21:49:31 +13:00
Jake Barnby c91252d94c Switch types 2026-02-25 21:42:28 +13:00
Damodar LohaniandGitHub b4a32c8265 Merge pull request #11397 from appwrite/revert-11387-feat-update-project-usage
Revert "Fix: add missing sites bandwidth to project usage"
2026-02-25 13:35:55 +05:45
Jake Barnby 62d03199b6 Force ID as int 2026-02-25 20:44:44 +13:00
Damodar LohaniandGitHub 244606ff1d Revert "Fix: add missing sites bandwidth to project usage" 2026-02-25 12:56:05 +05:45
Atharva Deosthale 9ee6304513 format 2026-02-25 10:35:32 +05:30
Atharva Deosthale ff4e756958 Merge remote-tracking branch 'origin/1.8.x' into add-cursor-plugin
# Conflicts:
#	composer.lock
#	src/Appwrite/Platform/Tasks/SDKs.php
2026-02-25 10:32:51 +05:30
Atharva Deosthale 19df20de61 composer 2026-02-25 10:18:37 +05:30
Eldad A. FuxandGitHub ac0aa81629 Merge pull request #11378 from appwrite/fix-missing-email-warning
OAuth provider handling fix
2026-02-24 14:36:09 +01:00
Eldad A. FuxandGitHub 0c83fa40e4 Merge pull request #11380 from appwrite/feat-fix-email-key
Feat fix for missing null safety on console redirects
2026-02-24 14:35:50 +01:00
Eldad A. FuxandGitHub f832b96e3b Merge pull request #11384 from appwrite/fix-proper-error-namespace-and-method
Update composer.lock and enhance error logging in HTTP requests
2026-02-24 14:30:47 +01:00
eldadfux 2b5307715a Merge remote 1.8.x
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	composer.lock
2026-02-24 14:25:56 +01:00
Jake BarnbyandGitHub aa7dfd0bd1 Merge pull request #11312 from appwrite/feat-mongodb 2026-02-24 09:32:22 +00:00
Jake BarnbyandClaude Opus 4.6 8dfc4abfe0 fix: cache DatabasesStringTypesTest setup before waiting
Same cache-before-wait pattern as DatabasesBase to prevent
cascading 409 failures when attribute polling times out.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:47:56 +13:00
Damodar LohaniandGitHub b3f7256156 Merge pull request #11387 from appwrite/feat-update-project-usage
Fix: add missing sites bandwidth to project usage
2026-02-24 12:51:39 +05:45
Jake BarnbyandClaude Opus 4.6 3b3177722b fix: cache setup data before waiting to prevent 409 on retry
When setupAttributes() or setupIndexes() timed out during
waitForAllAttributes/waitForAllIndexes, the cache was never populated.
Subsequent test methods calling setup again would find empty cache,
reuse the same collection IDs (from collection cache), and attempt to
re-create existing attributes/indexes, resulting in 409 Conflict errors.

Fix: populate the static cache immediately after successful creation
(202 assertions), before the wait calls. This ensures retries skip
creation and go straight to waiting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:06:20 +13:00
Damodar Lohani 2898d57243 Fix: Add missing sites bandwidth in the project usage 2026-02-24 06:26:52 +00:00
Jake BarnbyandClaude Opus 4.6 3306a03e8e chore: add 5-minute retry wait to reduce CI resource contention
With 120+ jobs starting simultaneously, transient failures are common
due to resource contention. Adding retry_wait_seconds: 300 to all
php-retry action usages gives the CI environment time to settle
before retrying failed tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 19:07:42 +13:00
Jake BarnbyandClaude Opus 4.6 e1791e17c3 fix: increase attribute/index polling timeouts to 240s for shared mode CI
Shared tables mode experiences significant worker queue contention during
parallel test execution. 120s was insufficient for attribute processing
in Shared V1 mode and occasionally for PostgreSQL under load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 17:50:46 +13:00
Jake BarnbyandClaude Opus 4.6 e69c1ddc7f fix: add executor readiness check to Site Screenshots CI jobs
The Site Screenshots CI jobs were missing the "Wait for Open Runtimes"
step that other E2E test jobs have, causing deployments to fail with
"Failed to connect to exc1 port 80" when the executor wasn't ready yet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 17:37:15 +13:00
Jake BarnbyandClaude Opus 4.6 9a97694a19 fix: resolve flaky E2E tests for realtime, avatars, migrations, and GraphQL
- Realtime: Use getLastEmailByAddress with userId probe instead of getLastEmail
  to prevent email race conditions in parallel execution; add status assertions
  on verification/recovery PUT responses for clearer failure diagnostics
- Avatars: Wrap external URL image fetches in assertEventually to retry
  transient network failures from appwrite.io
- Migrations: Increase performMigrationSync timeout from 10s to 60s
- GraphQL: Increase attribute polling timeouts from 60s to 120s with 500ms
  intervals to accommodate PostgreSQL under CI load

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 16:51:22 +13:00
Jake BarnbyandClaude Opus 4.6 7df86a3838 fix: cast attribute defaults after queue deserialization and improve test robustness
- Fix float/int/bool attribute creation failures caused by type loss
  during queue serialization in the Databases worker
- Rework session limit test to retry session creation for cache
  propagation in shared mode
- Increase GraphQL attribute polling timeouts from 30s to 60s
- Increase SchemaPolling waitForAllAttributes timeout to 180s
- Increase Realtime WebSocket client timeout from 30s to 45s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 16:02:23 +13:00
Jake BarnbyandClaude Opus 4.6 07dc088e4e fix: improve E2E test probes and enable function webhook events
- Fix testPasswordRecoveryUrlParams: use URL-based probes instead of
  userId-based probes to distinguish between test cases that share the
  same user
- Enable functions.* webhook events in ProjectCustom for function
  webhook tests that were silently passing by matching stale events
- Fix setupTeamMembership: add email address probe to getLastEmail to
  prevent picking up wrong invitation email
- Fix getLastEmail multi-email race: assert count inside assertEventually
  when requesting multiple emails

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:15:59 +13:00
Jake BarnbyandClaude Opus 4.6 5cb36a0a3a fix: resolve E2E test race conditions in parallel execution
- Add probe callback support to getLastEmail(), getLastEmailByAddress(),
  and getLastRequest() to filter results by content before accepting
- Fix variable name typo in TeamsCustomClientTest ($email vs $lastEmail)
- Add event probes to all 56 webhook test getLastRequest() calls to
  filter by specific event pattern (resource ID + action)
- Add email probes to Account OTP/recovery/magic-url tests to wait for
  the correct email (security phrase, Password Reset subject, etc.)
- Add email probes to Projects tests for recovery email URL matching
- Increase FunctionsSchedule future time from 1min to 2min to avoid
  timing issues when seconds are zeroed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:43:15 +13:00
Jake Barnby 811a759a8f Update lock 2026-02-24 14:14:14 +13:00
Jake Barnby 1c457c3a80 Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2026-02-24 14:07:14 +13:00
Jake Barnby 1cb7c4b3f6 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	.gitignore
#	composer.lock
#	tests/e2e/Services/Teams/TeamsCustomClientTest.php
2026-02-24 14:06:57 +13:00
Jake Barnby bada1a7c05 Timing updates 2026-02-24 14:00:07 +13:00
Luke B. SilverandGitHub 850b35d73c Merge pull request #11385 from appwrite/fix/missing-span-import
hotfix/ incorrect span import
2026-02-23 23:35:55 +00:00
loks0nandClaude Opus 4.6 8a365b781d Fix incorrect Span import in StatsResources worker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 23:04:52 +00:00
eldadfux fb8a1057af Update composer.lock 2026-02-23 19:47:22 +01:00
eldadfux ae4c02221f Merge remote 1.8.x
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	composer.lock
2026-02-23 19:46:48 +01:00
eldadfux 3b1b7bd611 Update composer.lock and enhance error logging in HTTP requests
- Updated versions for several dependencies in composer.lock, including utopia-php/database (5.3.3), utopia-php/detector (0.2.4), utopia-php/dns (1.6.5), utopia-php/framework (0.33.40), utopia-php/queue (0.15.6), and utopia-php/storage (1.0.1).
- Improved error logging in app/http.php and app/controllers/general.php to handle null routes gracefully, ensuring that the HTTP method and request URI are logged correctly even when the route is not defined.
2026-02-23 19:35:52 +01:00
Matej BačoandGitHub f97e893238 Merge pull request #11369 from appwrite/fix-email-url-params-encoding
Fix: url params in redirect URLs
2026-02-23 16:52:44 +01:00
Matej BačoandGitHub f8783faf1e Merge branch '1.8.x' into fix-email-url-params-encoding 2026-02-23 16:52:32 +01:00
f0c8f7e00e Update tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-23 16:49:17 +01:00
Matej Bačo 8946d3b7ee AI review fixes 2026-02-23 16:19:19 +01:00
Jake BarnbyandGitHub e92b80b3cb Merge branch '1.8.x' into feat-mongodb 2026-02-24 02:53:40 +13:00
Chirag AggarwalandGitHub 99df4c4d02 Merge pull request #11360 from appwrite/feat/assistant-sdk-release-automation
feat(sdks): automate AI SDK release version and changelog updates
2026-02-23 18:30:58 +05:30
Luke B. SilverandGitHub 8c48c764a9 Merge pull request #11383 from appwrite/telemetry/better-stats-logging
Remove superfluous logs from StatsResources worker
2026-02-23 12:21:44 +00:00
Chirag Aggarwal f9d6594955 composer 2026-02-23 17:43:11 +05:30
Chirag Aggarwal 40d5a9e43b Merge branch '1.8.x' into feat/assistant-sdk-release-automation 2026-02-23 17:42:52 +05:30
Matej Bačo f4ee6488ac Add tests for custom url schema in redirects 2026-02-23 13:09:26 +01:00
Matej Bačo 5414cc67ab Finalize html encoding rework 2026-02-23 12:54:20 +01:00
Chirag AggarwalandGitHub cf9148f0ac Merge pull request #11381 from appwrite/chore/fix-specs-generation
chore: remove examples from repo after migration to specs repo
2026-02-23 17:22:51 +05:30
Matej Bačo 2b6b66d8a4 Remove html encoding for urls 2026-02-23 12:45:18 +01:00
loks0nandClaude Opus 4.6 44cd5bb7f0 Remove superfluous Console logs from StatsResources worker
Replace redundant Console::info/error/success logs with Span::add
for document count — all other details (project ID, sequence, region,
database, duration) are already captured by the telemetry span.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:36:11 +00:00
Jake BarnbyandClaude Opus 4.6 95c67ea155 fix: resolve merge conflict markers in composer.lock
The merge of 1.8.x into feat-mongodb left unresolved conflict markers
in composer.lock, breaking all CI checks. Resolved in favor of
feat-mongodb values (minimum-stability: dev, prefer-stable: true)
to match composer.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 22:17:55 +13:00
Jake BarnbyandGitHub 0019f01492 Merge branch '1.8.x' into feat-mongodb 2026-02-23 21:21:26 +13:00
Chirag Aggarwal c07a7b0ea4 fix: auto-create specs directory and improve SDK error messages
- Auto-create specs directory in Specs.php if it doesn't exist

- Add helpful error message in SDKs.php when specs are missing

- Remove docs/examples from git tracking (added to .gitignore)
2026-02-23 11:06:21 +05:30
Chirag AggarwalandGitHub e2d6b76a88 Merge pull request #11368 from appwrite/feat/assistant-sdk-release-artifacts
chore: split sdk release artifacts from automation
2026-02-23 09:53:03 +05:30
eldadfux 9cabe15219 Fix for console redirect warning 2026-02-22 20:38:42 +01:00
eldadfux addba2cdb7 Refactor OAuth provider checks in account controller
Updated the handling of OAuth provider configurations to ensure that defaults are set when parameters are missing. This change improves error handling by checking for null values and ensuring class existence before proceeding with OAuth operations.
2026-02-22 11:42:12 +01:00
Jake BarnbyandClaude Opus 4.6 f2759cb65a fix: remove file caching, disable --functional for Databases/Functions/Realtime
Services that depend on shared static state between test methods
(Databases, Functions, Realtime) now run without --functional flag,
so test methods execute sequentially within each class while classes
still run in parallel. All other services keep --functional mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 03:44:28 +13:00
Jake BarnbyandClaude Opus 4.6 32c84ae869 fix: retry on TCP connection errors in callWithAuthRetry for Functions tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 03:29:31 +13:00
Jake BarnbyandClaude Opus 4.6 48698f40ba fix: add polling and delay for testSSRLogs logging config propagation
After setting logging=false, the site runtime may not immediately pick up
the config change. Add a 5s delay and poll for the NEW log entry (filtering
out the old one by $id) to avoid picking up stale log entries with content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 03:12:44 +13:00
Jake BarnbyandClaude Opus 4.6 8d18a33072 fix: call ensureSharedProject() in all setup methods, not just setupDatabase
When setupAttributes/setupIndexes/etc hit the file cache, they return
immediately without calling setupDatabase(), so ensureSharedProject()
was never called. This left self::$project empty, causing getProject()
to create a new project that didn't match the cached database IDs (404s).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 03:11:50 +13:00
Jake BarnbyandClaude Opus 4.6 a0b8437f08 fix: scope file caching to Database tests only, revert for non-Database tests
The previous file caching approach cached getRoot(), getUser(), getProject(),
and getConsoleVariables() globally. This caused all test methods in a class
to share the same project, breaking non-Database tests that expect isolated
state (Account 401s, Storage 500s, Users 404s, etc.).

Now file caching is only applied in Database/Transaction test setup chains:
- ensureSharedProject() in DatabasesBase, TransactionsBase, TransactionPermissionsBase
  creates and file-caches both the project AND user so all methods share
  consistent project + user state (needed for collection permissions)
- Non-Database tests (Account, Storage, Users, etc.) create their own
  isolated projects per-process as before

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 02:46:56 +13:00
Jake BarnbyandClaude Opus 4.6 3ea1e8e59f fix: add file-based cross-process caching for ParaTest --functional mode
With --functional mode, each test method runs in its own process so
static caches are empty. This causes every method to recreate projects,
databases, collections, attributes, and indexes - flooding the serial
database worker queue and causing attribute polling timeouts.

File-based caching with file locks ensures resources are created once
per test class, then shared across all method processes. This restores
--functional mode and dramatically reduces worker queue load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 01:47:20 +13:00
Chirag Aggarwal 7737139f23 composer 2026-02-20 17:59:33 +05:30
Atharva Deosthale a803bafbb8 feat: add cursor-plugin SDK support 2026-02-20 17:54:50 +05:30
HemachandarandGitHub 15b5202c16 Check project access only when user is present (#11370) 2026-02-20 17:37:12 +05:30
Eldad A. FuxandGitHub e45254d456 Merge pull request #11364 from appwrite/fix-dsn-vcs-error
Enhance error handling for project database configuration
2026-02-20 12:58:38 +01:00
Jake BarnbyandClaude Opus 4.6 8be633a140 fix: remove --functional from paratest to use class-level parallelism
With --functional mode, every test method gets its own process with no
static cache sharing. This causes each method to independently create
databases, collections, and attributes, flooding the serial database
worker queue. Without --functional, each test class shares static caches
between methods, dramatically reducing schema operation count.

Also reverts _APP_WORKERS_NUM change since worker must remain serial.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:38:39 +13:00
Jake BarnbyandClaude Opus 4.6 31f4260908 fix: increase database worker count and test resilience for parallel execution
Parallel test execution via ParaTest creates more queue load than a single
database worker can handle, causing attributes to get stuck at 'processing'.
Increase _APP_WORKERS_NUM to 4 to match parallelism, increase WebSocket
timeout from 15s to 30s, and add retry logic for proxy requests in Sites tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:16:40 +13:00
Chirag Aggarwal 7fc2c93529 lock file 2026-02-20 16:34:10 +05:30
0f3ea91d5d Update app/init/resources.php
Co-authored-by: Matej Bačo <matejbacocom@gmail.com>
2026-02-20 11:41:09 +01:00
eldadfux 92439ef0c4 format 2026-02-20 11:39:28 +01:00
Chirag Aggarwal 619fe72693 use diff check 2026-02-20 16:05:36 +05:30
8e4fddddbc Update app/init/resources.php
Co-authored-by: Matej Bačo <matejbacocom@gmail.com>
2026-02-20 11:24:42 +01:00
Matej Bačo e2bc3afce6 Fix url params in redirect URLs 2026-02-20 11:15:59 +01:00
Jake BarnbyandClaude Opus 4.6 818c89fe23 fix: increase site deployment build and activation polling timeouts
Build timeout: 120s → 240s
Activation timeout: 60s → 120s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:37:41 +13:00
Chirag Aggarwal 02951064ef bump composer 2026-02-20 14:09:44 +05:30
Chirag Aggarwal 0431337b22 chore: remove sdk release artifacts from automation PR 2026-02-20 14:04:06 +05:30
Chirag Aggarwal a9d2fed1ba chore: split sdk release artifacts from automation 2026-02-20 14:02:16 +05:30
Jake BarnbyandClaude Opus 4.6 58bf529183 fix: make testGetAccountLogs flexible for 1 or 2 audit logs
The user.create audit may or may not be present depending on async
audit processing timing. Accept either count and adjust offset/limit
assertions accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 21:28:09 +13:00
Chirag AggarwalandGitHub b9ea24eeb8 Merge pull request #11367 from appwrite/fix-invalid-type
fix: invalid type error from resources endpoint
2026-02-20 13:49:24 +05:30
Jake BarnbyandClaude Opus 4.6 abdceb831a fix: resolve E2E test failures for screenshots, account logs, risky tests, and deployment timeouts
- Accept 404 alongside 200 for screenshot tests with custom headers/permissions (browser service CI limitation)
- Fix testGetAccountLogs to expect 1 log (session.create only, user.create audit not triggered for self-service)
- Move getSupportForOperators() check before any assertions in testOperators/testBulkOperators (PHPUnit 12 risky test fix)
- Increase deployment build/activation polling timeout from 240s to 360s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 20:43:51 +13:00
Jake BarnbyandClaude Opus 4.6 b976454416 fix: add early failure detection and increase polling timeouts
Throw Critical exception when attribute/index/deployment status is
'failed' to fail tests immediately instead of waiting 15-20 minutes.
Increase default schema polling timeouts from 15 to 20 minutes for
CI stability under parallel load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:51:59 +13:00
Jake BarnbyandClaude Opus 4.6 b7d503e8e9 fix: resolve MariaDB spatial attribute and index creation failures
- Replace findOne()->isEmpty() with count() > 0 in spatial check to avoid
  potential false positives when checking for existing data in empty collections
- Use waitForAllAttributes instead of waitForAttribute before spatial index
  creation to ensure the collection document cache is refreshed
- Add response assertions for attribute creation in testSpatialPolygonAttributes
  to fail fast instead of timing out for 15 minutes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:53:50 +13:00
Chirag Aggarwal ee223046e0 fix: invalid type error from resources endpoint 2026-02-20 11:22:23 +05:30
Jake BarnbyandClaude Opus 4.6 168f9828bc fix: make activate param optional in site deployments
The activate param in Deployments/Create was missing the optional flag,
making it required unlike the VCS and Template deployment endpoints.
This caused 400 errors for multipart/form-data requests where the param
was present but treated as missing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 17:40:31 +13:00
Jake BarnbyandClaude Opus 4.6 89f0f227d6 fix: resolve CI failures in database, functions, and realtime tests
- Increase SchemaPolling default timeouts from 8min to 15min for
  waitForAttribute, waitForAttributes, waitForAttributeCount, and
  waitForIndex to match waitForAllAttributes/waitForAllIndexes. Under
  parallel test load the database worker gets backlogged, causing
  attributes to stay in 'processing' state longer than 8 minutes.
- Add isEmpty check in Functions Delete before updating schedules to
  prevent 500 errors when the schedule document is missing (consistent
  with other modules like Executions/Delete).
- Add retry logic to cleanupFunction in FunctionsBase for transient
  MongoDB transaction abort errors during function deletion.
- Increase Realtime attribute/index polling timeouts from 2min to 15min
  to handle worker backlog under CI load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 17:22:51 +13:00
eldadfux f9ed35ed8a Enhance error handling for project database configuration
* Added checks to ensure project references a valid database and throws exceptions if not configured.
* Improved error messaging for non-existent projects in VCS controller.
2026-02-19 20:28:29 +01:00
Jake BarnbyandClaude Opus 4.6 0b7c8865f5 fix: increase E2E test timeout from 30 to 40 minutes
Database tests (PostgreSQL, Shared V1, Shared V2) consistently take
31-32 minutes, just exceeding the 30-minute timeout. Increase to 40
minutes for all E2E service tests to prevent false timeout failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 04:19:44 +13:00
Jake BarnbyandClaude Opus 4.6 a952dad8d3 fix: add missing Role import and convert @group to PHP 8 attributes
- Add missing `use Utopia\Database\Helpers\Role` import in ProjectsBase.php
  (fixes "Class Role not found" fatal error)
- Convert @group ciIgnore docblock annotations to #[Group('ciIgnore')]
  PHP 8 attributes (PHPUnit 12 no longer supports docblock annotations)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 03:26:15 +13:00
Jake BarnbyandClaude Opus 4.6 2a535a2adc fix: exclude ciIgnore tests from CI and increase Realtime polling timeouts
- Add --exclude-group ciIgnore to paratest commands in tests.yml
  (the @group ciIgnore annotation was defined but never excluded)
- Increase attribute/index polling timeouts in RealtimeConsoleClientTest
  from 15s to 120s for Shared V2 stability

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 03:10:56 +13:00
Jake BarnbyandClaude Opus 4.6 b645721e14 fix: resolve CI failures from 1.8.x merge
- Remove unused WhiteList import in teams.php (linter fix)
- Add missing $teamId and $newTeam params to setupProject() in ProjectsBase.php (fixes 401 errors in all Projects tests)
- Filter execution logs by requestPath in testSSRLogs to avoid picking up screenshot worker executions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 02:46:23 +13:00
Chirag Aggarwal 0af495f241 release some sdks 2026-02-19 18:15:05 +05:30
Jake BarnbyandGitHub 8a6ddeef30 Merge branch '1.8.x' into feat-mongodb 2026-02-20 01:33:02 +13:00
Jake BarnbyandClaude Opus 4.6 8a391e2e27 fix: mark transactionId as required param in transaction endpoints
Path parameters should not be optional. This caused SDK TypeScript
build failures where transactionId was typed as string | undefined.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:36:41 +13:00
Chirag Aggarwal 035227312f add back spacing 2026-02-19 15:53:00 +05:30
Chirag Aggarwal fe48882e6c feat(sdks): automate sdk version/changelog with assistant 2026-02-19 15:39:11 +05:30
Jake BarnbyandClaude Opus 4.6 c2e4ddc177 fix: bump utopia-php/database to 5.3.2
Includes fix for empty orderAsc() attribute mapping in MongoDB adapter
that caused subQueryVariables filter to hang and crash the service.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:05:47 +13:00
Jake BarnbyandClaude Opus 4.6 ce0b7ab455 fix: increase function deployment build timeout to 4 minutes
FunctionsBase deployment polling was only 100 seconds, insufficient
under CI load when build workers are busy. Increased to 240 seconds
for both deployment readiness and activation checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 06:10:18 +13:00
Jake BarnbyandClaude Opus 4.6 c1cb7bdc8e fix: increase schema polling timeouts to 15 minutes
Spatial attribute creation under parallel CI load consistently exceeds
10 minutes. Increased both waitForAllAttributes and waitForAllIndexes
from 10/8 minutes to 15 minutes to accommodate heavy parallel load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 05:12:09 +13:00
Jake BarnbyandClaude Opus 4.6 54505c0600 fix: increase polling timeouts for CI stability
Spatial attribute creation under parallel MariaDB load and function
deployment builds under MongoDB can exceed previous limits. Increased
SchemaPolling timeout from 8 to 10 minutes and GraphQL function build
timeout from 2 to 4 minutes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 04:21:49 +13:00
Jake BarnbyandClaude Opus 4.6 e26bac1e6e fix: resolve merge conflicts in projects.php template endpoints
Merge from 1.8.x left 6 unresolved conflicts in SMS/email template
endpoints. Resolved by keeping both the dynamic UID validator from
feat-mongodb and the strict WhiteList comparison from 1.8.x.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 03:41:36 +13:00
Jake BarnbyandClaude Opus 4.6 f8e29c6b38 fix: prevent testTimeout from deleting shared database
testTimeout was using setupDatabase() to get the shared database, creating
a collection within it, then deleting the entire database at cleanup. This
destroyed the shared Movies/Actors/Books collections, causing testNotSearch
and other tests relying on the shared database to fail with 404.

Fix: only delete the test collection, not the shared database. Also add
waitForAttribute to prevent intermittent failures from attribute not being
available when inserting documents.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 02:37:06 +13:00
Jake BarnbyandGitHub 3bf594ecae Merge branch '1.8.x' into feat-mongodb 2026-02-18 12:09:43 +00:00
Jake BarnbyandClaude Opus 4.6 fa972093f4 fix: front-load fulltext search infrastructure into cached setup chain
Move the Books collection, attributes, and fulltext index creation into
the shared setupCollection/setupAttributes/setupIndexes cache chain so
they are processed early when the worker queue is less loaded. This
eliminates testNotSearch creating redundant infrastructure from scratch
at test time when the queue is overwhelmed with 830+ parallel tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:26:03 +13:00
Jake BarnbyandClaude Opus 4.6 ddb392caf7 fix: increase SchemaPolling default timeouts from 4min to 8min
MariaDB attribute/index processing consistently times out under parallel
test load in CI with different tests failing each run. Increase all default
polling timeouts from 240s to 480s to handle worst-case MariaDB load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:18:56 +13:00
Jake BarnbyandClaude Opus 4.6 dca375624c fix: add missing 202 assertions before schema polling waits
- Fix testSpatialDistanceInMeter: move assertEquals(202) BEFORE waitForIndex
  (was after, causing misleading 4-minute timeout on creation failure)
- Add 202 assertions to testSpatialAttributesMixedCollection attribute creation
- Add 202 assertion to testConcurrentTransactionConflicts attribute creation
- Add 202 assertion to fulltext search index creation
- Add 202 assertion to shared collection attribute creation

These ensure tests fail fast with clear errors if schema creation returns
an error, instead of pointlessly polling for 4 minutes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:58:05 +13:00
Jake Barnby 3e0febca92 Revert "fix: increase SchemaPolling default timeouts from 4min to 6min"
This reverts commit 2a4c33be11.
2026-02-18 22:49:31 +13:00
Jake BarnbyandClaude Opus 4.6 2a4c33be11 fix: increase SchemaPolling default timeouts from 4min to 6min
MariaDB attribute/index processing consistently times out under parallel
test load in CI. Increase all default polling timeouts from 240s to 360s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:39:07 +13:00
Jake BarnbyandClaude Opus 4.6 6582ec8e67 fix: register TYPE_ID in GraphQL Mapper and fix Schedules tests for ParaTest
- Add 'id' => Type::string() to GraphQL Mapper Registry defaults to handle
  Model::TYPE_ID used by Document and Row models ($sequence field)
- Refactor SchedulesConsoleClientTest to use static-cached setup helpers
  instead of @depends annotations, which don't work in ParaTest --functional mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:59:33 +13:00
Jake Barnby 2c5806d1e7 Revert "fix: revert accidentally committed working tree changes"
This reverts commit a3503f4124.

# Conflicts:
#	composer.lock
2026-02-18 21:45:54 +13:00
Jake Barnby 43f7c99039 Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2026-02-18 21:44:48 +13:00
Jake Barnby c584a72496 Update lock 2026-02-18 21:44:32 +13:00
Jake BarnbyandGitHub ce64011551 Merge branch '1.8.x' into feat-mongodb 2026-02-18 08:42:56 +00:00
Jake BarnbyandClaude Opus 4.6 a3503f4124 fix: revert accidentally committed working tree changes
Reverts composer.lock (database 5.3.0→5.2.1), Model.php TYPE_ID,
Document.php/Row.php $sequence type changes, and OpenAPI/Swagger
id type additions that were unintentionally included in dd1a3d82.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 20:58:54 +13:00
Jake BarnbyandClaude Opus 4.6 0d1a6e07b4 fix: use relative path for --log-junit so retry action can find JUnit XML
The retry action couldn't extract the container name from docker compose
exec commands to copy the JUnit XML. Using a relative path works because
CWD inside the container is /usr/src/code and the volume mount
./tests:/usr/src/code/tests makes the file accessible at the same
relative path on both container and host.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 20:30:48 +13:00
Jake BarnbyandClaude Opus 4.6 dd1a3d8222 fix: adjust testCreateIndexes index count for MongoDB maxIndexLength=1024
MongoDB's maxIndexLength (1024) allows the 512+512 index to succeed,
resulting in 5 indexes instead of 4. Use conditional count and
assertContains for position-independent key checks.

Verified: 830/830 Database tests pass locally on MongoDB with paratest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 19:59:20 +13:00
Jake BarnbyandClaude Opus 4.6 93bc3871df fix: MongoDB test compatibility and enable JUnit XML for paratest retry
- testCreateIndexes: conditionally check index length based on adapter's maxIndexLength (MongoDB 1024 vs SQL 768)
- testConcurrentTransactionConflicts: accept both 409 and 500 since MongoDB adapter doesn't map write conflicts to ConflictException
- testEnforceCollectionPermissions: add error body to assertion for debugging
- Enable --log-junit for paratest in both project and shared mode CI jobs so itznotabug/php-retry@v3 can identify and selectively retry failing tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 19:09:01 +13:00
Jake BarnbyandClaude Opus 4.6 ca01ec7b6b fix: increase SchemaPolling default timeout to 240s for CI stability
120s was insufficient for attribute processing under parallel CI load,
causing spurious timeout failures in spatial, transaction, and attribute tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:28:24 +13:00
Jake BarnbyandClaude Opus 4.6 d6d12d818c fix: use getContainerIdResponseKey() instead of hardcoded $collectionId in upsert test
TablesDB uses $tableId not $collectionId in data payloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:03:46 +13:00
Jake BarnbyandClaude Opus 4.6 442a8d86d6 fix: use getRecordResource() instead of hardcoded 'documents' in testUpsertDocument
TablesDB uses 'rows' not 'documents' for the list records response key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:44:41 +13:00
Jake BarnbyandClaude Opus 4.6 84c40cea41 fix: resolve testCreateAttributes, testUpsertDocument, and SchemaPolling timeouts
- Fix testCreateAttributes: use $actorsAttr instead of undefined $actors variable,
  and use $this->getSchemaResource() instead of hardcoded 'attributes' for TablesDB compat
- Fix testUpsertDocument: add missing 'data' => $upsertData in PUT request body
- Increase SchemaPolling default timeout from 60s to 120s for CI stability with
  spatial attributes under parallel load

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:25:45 +13:00
Jake BarnbyandClaude Opus 4.6 9796c46975 fix: misplaced assertion in testUpdateAttributeEnum and reduce timeouts
- Move maxLength/assertStringContainsString from testUpdateAttributeEnum
  back to testPatchAttribute where $attribute is defined
- Fix remaining $data['moviesId'] -> $moviesId in testCreateAttributes
- Reduce WebSocket timeout from 30s to 15s for faster failure detection
- Reduce Realtime helper assertEventually from 30s to 15s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:07:05 +13:00
Jake BarnbyandClaude Opus 4.6 ae4e79518b fix: remaining testCreateAttributes undefined variable references
- Change $data['moviesId'] to $moviesId at integers2 attribute creation
- Change $data['actorsId'] to $actorsId at relationship assertion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:59:25 +13:00
Jake BarnbyandClaude Opus 4.6 2c1919140f fix: resolve additional test failures from CI results
DatabasesBase:
- testCreateAttributes: use local $moviesId/$actorsId instead of $data['...']
- testValidateOperators: add missing setupOneToManyRelationship() call
- testSelectQueries: add missing setupOneToManyRelationship() call
- testUniqueIndexDuplicate: define missing $serverHeaders variable
- testListDocuments: change return type from array to void (no callers)

RealtimeConsoleClientTest:
- testAttributesCollectionsAPI: create database/collection before opening
  WebSocket to prevent their events from interfering with attribute events
- testAttributesTablesAPI: same restructuring for TablesDB variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:42:41 +13:00
Jake BarnbyandClaude Opus 4.6 acad17f3b8 fix: resolve test failures and reduce excessive timeouts
- DatabasesBase: fix 5 merge corruption issues (missing integers2 attr,
  undefined vars, wrong assertion, wrong setup method)
- WebhooksBase: increase deployment build timeout from 10s to 120s
- RealtimeBase: reduce WebSocket timeout 120s→30s, fix deprecated nullable
- RealtimeConsoleClientTest: reduce assertEventually 120s→30s
- SchemaPolling: reduce all default timeouts 600s→60s
- SitesBase: reduce build timeout 300s→120s, activation 200s→60s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 14:23:42 +13:00
Jake BarnbyandClaude Opus 4.6 a70e94a67b fix: resolve merge corruption in tests.yml workflow
The e2e_service_test job's command block had corrupted shell syntax from
a bad merge - orphaned elif/else without matching if, and duplicate
if-elif blocks for DB adapter env vars. The set-db-env step already sets
these via GITHUB_ENV, making the inline exports redundant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:41:08 +13:00
Jake Barnby ec9cb430af CI 2026-02-18 13:34:50 +13:00
Jake BarnbyandClaude Opus 4.6 9e628072eb fix: resolve additional merge corruptions in test files
- DatabasesBase.php: fix array indentation for 'library' key inside 'data' array
- SitesBase.php: fix corrupted while-loop/assertEventually merge in
  activation check
- DatabasesStringTypesTest.php: remove return $data from void methods,
  convert @depends chain to use setupDatabaseAndCollection()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 04:11:37 +13:00
Jake BarnbyandClaude Opus 4.6 6c29f4e285 fix: resolve merge corruptions in DatabasesBase.php from feat-db-tests merge
- Replace all hardcoded Legacy-style URLs with API-agnostic helpers
  (getContainerUrl, getSchemaUrl, getRecordUrl, getIndexUrl)
- Replace hardcoded param keys (collectionId, documentSecurity,
  relatedCollectionId, attributes) with helper methods
- Replace hardcoded response keys ($collectionId, relatedCollection)
  with getContainerIdResponseKey() and getRelatedResourceKey()
- Fix structural merge corruptions where code from different methods
  got interleaved (testCreateAttributes, testCreateIndexes,
  testGetDocumentWithQueries, testOperators, testUniqueIndexDuplicate)
- Remove orphaned code fragments and stale return statements
- Fix void methods incorrectly returning $data
- Add proper setup calls in relationship test methods
- Update composer.lock to match composer.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 03:55:39 +13:00
Jake Barnby 64a368ba3d Merge remote-tracking branch 'origin/feat-db-tests' into feat-mongodb
# Conflicts:
#	.github/workflows/tests.yml
#	src/Appwrite/Utopia/Response.php
#	src/Appwrite/Utopia/Response/Model/AttributeList.php
#	tests/e2e/Client.php
#	tests/e2e/Scopes/ProjectCustom.php
#	tests/e2e/Services/Databases/DatabasesBase.php
#	tests/e2e/Services/Databases/Legacy/DatabasesCustomClientTest.php
#	tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php
#	tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomServerTest.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php
#	tests/e2e/Services/Databases/Transactions/TransactionsBase.php
#	tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php
#	tests/e2e/Services/GraphQL/TablesDB/DatabaseServerTest.php
#	tests/e2e/Services/Messaging/MessagingBase.php
#	tests/e2e/Services/Sites/SitesBase.php
#	tests/e2e/Services/Sites/SitesCustomServerTest.php
2026-02-18 03:27:23 +13:00
Jake Barnby 10d21bbed6 Cycle lock 2026-02-18 00:45:30 +13:00
Jake Barnby 4529786cc1 Update lock 2026-02-17 23:09:06 +13:00
Jake BarnbyandGitHub 51af714ea8 Merge branch '1.8.x' into feat-mongodb 2026-02-17 08:38:34 +00:00
Jake Barnby f262b46580 Update lock 2026-02-17 19:07:04 +13:00
Jake Barnby 459a5617d6 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2026-02-17 16:32:46 +13:00
Jake BarnbyandGitHub 1740ce59c0 Merge branch '1.8.x' into feat-mongodb 2026-02-16 13:16:45 +00:00
Jake BarnbyandClaude Opus 4.6 bf68bb2f71 fix: resolve MongoDB-specific E2E test failures and $sequence type bug
- Fix $sequence always cast to string (was incorrectly cast to int for numeric values) in Document.php and Row.php
- Add supportForMultipleFulltextIndexes, supportForAttributeResizing, supportForSchemas console variables for MongoDB adapter
- Fix testCreateIndexes to use getSupportForMultipleFulltextIndexes() instead of getSupportForRelationships() for fulltext index error branching
- Fix testAttributeRowWidthLimit/testColumnRowWidthLimit to skip on databases without schema support (MongoDB)
- Fix testAttributeUpdateStringResize/testColumnUpdateStringResize to skip on databases without attribute resizing support (MongoDB)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 01:43:28 +13:00
Jake BarnbyandGitHub 436ced3f1f Merge branch '1.8.x' into feat-mongodb 2026-02-16 09:13:34 +00:00
Jake Barnby b025ad0e8c Update lock 2026-02-14 18:33:29 +13:00
Jake Barnby 82dada5732 Cast if num 2026-02-14 18:04:28 +13:00
Jake BarnbyandGitHub caafe2678b Merge branch '1.8.x' into feat-mongodb 2026-02-14 04:50:00 +00:00
Jake BarnbyandClaude Opus 4.6 ab19a73b33 fix: adjust messaging scheduledAt timings for CI reliability
Set initial schedule to 20s and reschedule to 10s to balance between:
- Not being picked up by scheduler before reschedule (3-4s interval)
- Providing enough margin (6s) after scheduledAt for processing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 02:00:09 +13:00
Jake BarnbyandClaude Opus 4.6 ecf0b4a1dd fix: change $sequence to string type to support MongoDB UUID7 values
The Document and Row response models were casting $sequence to (int),
which destroyed UUID7 values on MongoDB. On MongoDB, $sequence is the
internal _id field which is a UUID7 string, not an auto-increment integer.
The (int) cast converted UUID7 values like "019c56a9-df6d-73fa-8d48-..."
to 19, making $sequence queries fail validation with 400.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 00:29:38 +13:00
Jake BarnbyandClaude Opus 4.6 7708f13ebd fix: revert database package, add debug output for $sequence query failures
- Revert utopia-php/database to 5.1.2 (the Sequence validator fix
  incorrectly assumed $sequence is always integer, but on MongoDB
  internal collections it uses UUID7)
- Revert Base.php change
- Add detailed error messages to testGetDocumentWithQueries and
  testGetRowWithQueries to capture actual $sequence values and API
  error responses for debugging
- Keep Messaging scheduledAt fix (30s initial, 15s rescheduled)
- Keep Functions auth retry improvement (10 retries with project refresh)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 00:09:31 +13:00
Jake BarnbyandClaude Opus 4.6 f7ba5e39b7 fix: update Sequence validator, fix messaging race condition, improve auth retry
- Update utopia-php/database to fix Sequence validator rejecting integer
  $sequence values on MongoDB (VAR_UUID7) adapters
- Fix Base.php Filter constructor passing APP_DATABASE_QUERY_MAX_VALUES
  as $idAttributeType instead of the correct Database::VAR_INTEGER
- Fix Messaging testUpdateScheduledAt race condition by increasing
  initial scheduledAt from 3s to 30s so scheduler doesn't fire before
  the PATCH update processes
- Improve Functions callWithAuthRetry to refresh project credentials
  after 4 consecutive 401 failures and increase max retries to 10

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 23:54:39 +13:00
Jake BarnbyandClaude Opus 4.6 5cf272b21a fix: add $sequence to SELECT query in testGetDocumentWithQueries
The SELECT query didn't include $sequence, so accessing
$response['body']['$sequence'] returned null. The subsequent
Query::equal('$sequence', [null.'']) produced an empty string
which is invalid for the integer $sequence field, causing 400.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:02:09 +13:00
Jake BarnbyandClaude Opus 4.6 991af09fdf fix: improve auth retry resilience for MongoDB CI stability
- Increase callWithAuthRetry from 5 to 8 retries with capped delay (50s total)
- Increase ProjectCustom retries from 3 to 5 with 1s delays
- Fix swapped assertEquals parameters in FunctionsBase for clearer errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:46:24 +13:00
Jake BarnbyandGitHub b7ce686d73 Merge branch '1.8.x' into feat-mongodb 2026-02-13 07:18:26 +00:00
Jake BarnbyandClaude Opus 4.6 5df786b5ad fix: add retry logic for 401 errors and explicit site deployment activation
- Add retry logic with delays for transient 401 auth errors during project
  setup in ProjectCustom::getProject() (cherry-pick from feat-db-tests)
- Replace 10-minute activation polling in SitesBase with 30-second auto-activation
  wait followed by explicit PATCH /sites/:siteId/deployment fallback to prevent
  test suite timeouts when the build worker is slow to auto-activate

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:05:49 +13:00
Jake Barnby 57e7f57056 Revert "Revert removals"
This reverts commit 005a3f4775.
2026-02-13 18:00:10 +13:00
Jake Barnby 005a3f4775 Revert removals 2026-02-13 17:52:50 +13:00
Jake BarnbyandGitHub 75ac4e4447 Merge branch '1.8.x' into feat-db-tests 2026-02-13 04:00:43 +00:00
Jake Barnby dbf3b52d4c Update lock 2026-02-13 16:12:43 +13:00
Jake BarnbyandGitHub 9c40d0c6f8 Merge pull request #11317 from appwrite/copilot/sub-pr-11312 2026-02-13 03:02:25 +00:00
Jake BarnbyandGitHub 2b536078bb Merge branch '1.8.x' into feat-mongodb 2026-02-13 02:19:37 +00:00
copilot-swe-agent[bot]andabnegate 3d58ed10dc Use explicit empty strings for environment variables
Co-authored-by: abnegate <5857008+abnegate@users.noreply.github.com>
2026-02-13 02:06:37 +00:00
copilot-swe-agent[bot]andabnegate 0a79370e1c Use GitHub Actions env config instead of sed commands
Co-authored-by: abnegate <5857008+abnegate@users.noreply.github.com>
2026-02-13 02:05:30 +00:00
copilot-swe-agent[bot] 2ee38ff45d Initial plan 2026-02-13 02:01:54 +00:00
Jake Barnby 3b8ab19fa0 Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2026-02-13 14:42:47 +13:00
Jake Barnby af7862bbbd Remove invalid patch 2026-02-13 14:42:37 +13:00
Jake BarnbyandGitHub 1dcee3edbf Fxx variables 2026-02-13 01:40:37 +00:00
Jake BarnbyandGitHub 1274d56234 Update src/Appwrite/Auth/OAuth2/Mock.php 2026-02-13 14:34:31 +13:00
Jake BarnbyandGitHub c8db0c6dcc Apply suggestions from code review 2026-02-13 14:34:02 +13:00
Jake BarnbyandClaude Opus 4.6 52606f352c fix: handle 401 in assertEventually and increase curl timeout to 30s
- setupDeployment's assertEventually callbacks now handle 401 responses
  gracefully instead of crashing with "Undefined array key"
- Increase Client curl timeout from 15s to 30s for slow CI runners
  (testDeleteScheduledExecution and testGetScreenshotWithPermissions
  were hitting the 15s limit)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 10:37:07 +13:00
Jake BarnbyandClaude Opus 4.6 37b857903a fix: add callWithAuthRetry to FunctionsBase for transient 401s
Extract retry logic into a reusable callWithAuthRetry helper with
exponential backoff (5 retries, 2/4/6/8s delays). Apply to both
setupFunction and setupDeployment.

Previous fix only retried setupFunction, but 401 can hit any API call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 10:07:26 +13:00
Jake BarnbyandClaude Opus 4.6 efb523dab4 fix: add retry logic to setupFunction for flaky 401 auth errors
All 15 FunctionsCustomServerTest failures originate from setupFunction()
getting a transient 401 when the API key lookup fails under CI load.
Retry up to 3 times with a 2s delay on 401 responses.

Remove the per-method #[Retry] on testScopes since the root fix is now
in setupFunction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 09:53:14 +13:00
Jake BarnbyandClaude Opus 4.6 aa369dc371 fix: increase Sites activation timeout to 600s and add retry to testScopes
MongoDB CI builds take longer to process the deployment queue, causing
testSiteDomainReclaiming to timeout at 400s. Increase to 600s.

testScopes has an intermittent 401 auth failure - add #[Retry(count: 3)]
for resilience.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 09:16:27 +13:00
Jake BarnbyandClaude Opus 4.6 a13b9c0a88 fix: Increase Sites deployment activation timeouts and fix race condition
- Increase activation timeout in SitesBase setupDeployment from 200s to 400s
- Add assertEventually for deploymentId check in testCreateDeployment to handle
  the race condition where build worker sets deployment status to 'ready' before
  setting site.deploymentId
- Increase build ready timeouts from 50s to 300s in testCreateDeployment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 08:37:14 +13:00
Jake BarnbyandClaude Opus 4.6 8b822cd97c fix: Add missing _APP_DB_ADAPTER env var to worker-executions, worker-screenshots, and task-interval
The default for _APP_DB_ADAPTER was changed from 'mariadb' to 'mongodb' in
registers.php. Services without this env var default to the MongoDB adapter,
causing MariaDB/PostgreSQL function executions to get stuck in 'waiting'
status because the worker-executions service cannot connect to the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:53:16 +13:00
Jake BarnbyandClaude Opus 4.6 f0e6105ba0 fix: Use Docker Compose profiles to isolate database containers in CI
- Add profiles to database services (mariadb, mongodb, postgresql) so only
  the needed database starts per CI job, freeing resources for the executor
- Increase Sites deployment activation timeouts from 50-100s to 200s for
  MongoDB compatibility
- Increase SSR logs polling timeout from 30s to 120s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:27:21 +13:00
Jake BarnbyandClaude Opus 4.6 25a9aa1805 fix: Fix Sites test SSR logs timing and PostgreSQL search stop word
- Add polling for SSR execution logs instead of immediate access
- Change testListSites framework from 'other' to 'analog' because
  PostgreSQL treats 'other' as a fulltext stop word

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 06:36:51 +13:00
Jake BarnbyandClaude Opus 4.6 6f56c9c2ee fix: Gate spatial index null tests on adapter capability
PostgreSQL supports spatial indexes on nullable columns and required
spatial attributes on existing data, unlike MariaDB. Expose
supportForSpatialIndexNull console variable and condition test
assertions accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 06:22:53 +13:00
Jake BarnbyandClaude Opus 4.6 e3a39499f2 fix: Patch PostgreSQL boolean type mismatch in batch inserts
The SQL adapter's createDocuments() unconditionally casts boolean values to
integers (line 2518 in SQL.php), but PostgreSQL rejects int values for native
BOOLEAN columns. The single-document path already has an instanceof check to
preserve booleans for Postgres, but createDocuments() was missed.

This patch applies the same fix via a PHP script run during Docker build.
Fixes 26 test failures across PostgreSQL Legacy, TablesDB, and GraphQL suites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 05:44:58 +13:00
Jake BarnbyandClaude Opus 4.6 e3c58a8c05 fix: Correct test capability gates and GraphQL dependencies
- Change spatial test gates from getSupportForRelationships() to
  getSupportForSpatials() in both Legacy and TablesDB DatabasesBase
- Add both relationship AND spatial gates for spatial relationship tests
- Add missing @depends for float, email, and datetime attributes/columns
  in GraphQL Server tests to ensure schema is complete before document/row
  creation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 05:06:48 +13:00
Jake BarnbyandClaude Opus 4.6 74b4c44bde fix: Gate relationship test on adapter support in TablesDB
MongoDB doesn't support relationships, so testInvalidRelationshipDocumentId
returns 400 instead of 202. Skip the test when the adapter lacks support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 04:59:00 +13:00
Jake BarnbyandClaude Opus 4.6 9b2dd958b5 fix: Use correct capability check for partial fulltext search in Users test
PostgreSQL's ts_vector/ts_query doesn't support partial word matching
(searching "man" won't find "manchester"), unlike MariaDB's LIKE-based
search. The test used getSupportForRelationships() as a proxy for this
capability, which was wrong for PostgreSQL (supports relationships but
not partial fulltext search).

Add supportForFulltextWildcard to console variables endpoint and use
it in the Users test instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 04:56:39 +13:00
Jake BarnbyandClaude Opus 4.6 b952bf7978 fix: Use custom PostgreSQL image with required extensions (PostGIS, pgvector)
The Postgres adapter in utopia-php/database requires PostGIS, pgvector,
and pg_trgm extensions. The plain postgres:18 image lacked these, causing
"collation utf8_ci_ai does not exist" errors (CREATE EXTENSION failed
before the collation could be created).

Switch to postgres:17 with a custom Dockerfile that installs the required
extensions, matching the approach used by utopia-php/database's own tests.
Also reverts the volume mount to /var/lib/postgresql/data (correct for v17).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 04:25:35 +13:00
Jake BarnbyandClaude Opus 4.6 490b9f844f fix: Update PostgreSQL volume mount for postgres:18 compatibility
PostgreSQL 18+ stores data in major-version-specific subdirectories under
/var/lib/postgresql instead of directly in /var/lib/postgresql/data. The
old mount path caused the container to crash immediately on startup with
a migration error, which was the actual root cause of all PostgreSQL CI
failures (the container was dead so Docker DNS couldn't resolve its name).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 04:14:45 +13:00
Jake BarnbyandClaude Opus 4.6 eaff193055 fix: Resolve PostgreSQL DNS failure by routing through Docker's embedded DNS
The appwrite container's `dns: 172.16.238.100` (CoreDNS) bypassed Docker's
embedded DNS, preventing resolution of service names like `postgresql`.
CoreDNS only handles custom test domains and forwards unknown queries to
Cloudflare, which can't resolve Docker service names.

Fix: Add 127.0.0.11 (Docker embedded DNS) as primary resolver and configure
CoreDNS to forward unknown queries to Docker DNS with Cloudflare as fallback.
Bind CoreDNS to its static IP to avoid port conflict with Docker's DNS proxy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 04:02:31 +13:00
Jake BarnbyandClaude Opus 4.6 61e69027a3 debug: Include error message in database connection retry log
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 03:34:22 +13:00
Jake BarnbyandClaude Opus 4.6 fc893e1c7e fix: Add connect_timeout to PostgreSQL DSN and add server logs to CI
PDO ATTR_TIMEOUT does not control connection timeout for the pgsql driver.
Without connect_timeout in the DSN, the PDO constructor blocks indefinitely
if PostgreSQL isn't ready, which blocks the Swoole master process event loop
(since coroutine hooks are not enabled in http.php) and prevents all HTTP
request dispatching.

Also add appwrite server logs to CI failure output for debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 03:17:48 +13:00
Jake BarnbyandClaude Opus 4.6 9b5695a21b fix: Use simple list form for worker-executions depends_on
Docker Compose doesn't support variable interpolation in depends_on map
keys (expanded form with condition). Use simple list form like all other
services.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 02:53:32 +13:00
Jake BarnbyandClaude Opus 4.6 c9aa80473d fix: Correct import ordering for WhiteList in teams controller
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 02:47:45 +13:00
Jake BarnbyandClaude Opus 4.6 462ce71512 fix: Replace hardcoded mariadb dependencies with dynamic DB host in docker-compose
Four services (worker-screenshots, worker-executions, task-maintenance,
task-interval) had hardcoded `depends_on: mariadb` instead of using
`${_APP_DB_HOST:-mongodb}` like all other services. This caused ALL
PostgreSQL E2E tests to fail because those services couldn't start.

Also fix Storage test assertion that incorrectly used
getSupportForRelationships() as proxy for max UID length - the UID
validator always limits to 36 chars regardless of adapter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 02:44:49 +13:00
Jake BarnbyandClaude Opus 4.6 0e6fa7f300 fix: Add missing WhiteList import in teams controller
The WhiteList validator was used at lines 486 and 1090 but never imported,
causing a "Class not found" error and 500 response instead of proper validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 02:36:29 +13:00
Jake BarnbyandGitHub cee77d0564 Merge branch '1.8.x' into feat-mongodb 2026-02-12 12:42:54 +00:00
Jake BarnbyandClaude Opus 4.6 2869d057f9 fix: Populate console variables with actual database adapter capabilities
The console variables endpoint was using model defaults (all true) for
supportForRelationships, supportForOperators, supportForSpatials, and
maxIndexLength instead of reading from the actual database adapter.
This caused tests to run unsupported operations on MongoDB.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:41:03 +13:00
Jake BarnbyandClaude Opus 4.6 f13f80b2a3 fix: Handle unlimited attribute limit for MongoDB in subQueryAttributes filter
MongoDB adapter returns 0 from getLimitForAttributes() meaning unlimited,
but Query::limit(0) fails validation since minimum valid limit is 1.
Fall back to APP_LIMIT_SUBQUERY (1000) when adapter reports unlimited.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 00:55:05 +13:00
Jake Barnby ccb8b9eb6f Revert "fix: Set MariaDB adapter for non-adapter-specific CI tests"
This reverts commit 17d9a72922.
2026-02-13 00:35:56 +13:00
Jake BarnbyandClaude Opus 4.6 17d9a72922 fix: Set MariaDB adapter for non-adapter-specific CI tests
The .env defaults were changed to mongodb, but tests that don't
explicitly set a DB adapter (general, shared mode, abuse, screenshots)
were implicitly using the default. These tests need MariaDB since
shared tables mode is not supported with MongoDB yet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 00:34:38 +13:00
Jake BarnbyandClaude Opus 4.6 d3e6c48a59 fix: Add missing Authorization parameter to Columns XList action
The TablesDB Columns XList::action() method was missing the
Authorization $authorization parameter that exists in the parent
Attributes XList class, causing a PHP fatal error that prevented
the appwrite service from starting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 23:57:51 +13:00
Jake Barnby 638778413d Fix workflows 2026-02-12 23:44:45 +13:00
Jake Barnby 6a005c06b1 Format 2026-02-12 23:40:51 +13:00
Jake Barnby 5590f0bcd0 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	.env
#	.github/workflows/tests.yml
#	app/controllers/api/projects.php
#	app/controllers/api/teams.php
#	app/init/registers.php
#	app/views/install/compose.phtml
#	composer.json
#	composer.lock
#	docker-compose.yml
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Create.php
#	src/Appwrite/Platform/Tasks/Install.php
#	src/Appwrite/Platform/Tasks/Upgrade.php
#	tests/e2e/Services/Databases/Legacy/DatabasesBase.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2026-02-12 23:35:21 +13:00
Jake BarnbyandGitHub 408f65bf64 Merge branch '1.8.x' into feat-db-tests 2026-02-12 09:14:57 +00:00
Jake BarnbyandClaude Opus 4.6 aa8324f7b9 fix: increase Proxy deployment activation timeout to 5 minutes
Under heavy parallel CI load, VCS deployments take longer to build
and activate. Increased from 200s to 300s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 19:25:08 +13:00
Jake BarnbyandClaude Opus 4.6 6efb20f44c fix: increase all schema polling default timeouts to 10 minutes
Under parallel CI load with MariaDB, attribute processing frequently
exceeds the previous 5-minute default timeout. Increased all polling
defaults (waitForAttribute, waitForAttributes, waitForAttributeCount,
waitForAllAttributes, waitForIndex, waitForAllIndexes) to 10 minutes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:50:32 +13:00
Jake BarnbyandClaude Opus 4.6 663b75bc03 fix: increase waitForAllAttributes timeout for movies collection setup
The 'integers' attribute was getting stuck in 'processing' status under
MariaDB parallel CI load, exceeding the default 5-minute timeout.
Increased to 10 minutes, matching the spatial test timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:36:36 +13:00
Jake BarnbyandClaude Opus 4.6 006867617b Fix parallel test assertions and graceful runtime cleanup
- Fix testListProjectKey: use dynamic key counts instead of hardcoded 5
  (parallel workers don't share state from testCreateProjectKey)
- Fix testGetProjectKey: expect 'Key Test' from setupProjectWithKey(),
  not 'Key Custom' from a previously-dependent test
- Handle 404 in Executor::deleteRuntime() (runtime already gone)
- Make cancel deployment cleanup best-effort (status already updated)
- Apply same fix to Sites cancel endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:23:55 +13:00
Jake BarnbyandClaude Opus 4.6 fa7ebbffa8 Fix void return in testListProjectKey and increase spatial test timeout
- Remove `return $data` from testListProjectKey() which is declared void,
  causing fatal PHP error that crashed Projects and Abuse test suites
- Increase waitForAllAttributes timeout to 10min for spatial column test
  to handle MariaDB slowness under parallel CI load

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:12:54 +13:00
Jake Barnby d2e77aa5cd Fix indents 2026-02-12 16:35:36 +13:00
Jake Barnby 7d5f6c9bf3 Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	.github/workflows/tests.yml
#	composer.lock
#	tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
#	tests/e2e/Services/Teams/TeamsCustomClientTest.php
2026-02-12 12:34:42 +13:00
Jake BarnbyandGitHub 00525a5a02 Merge branch '1.8.x' into feat-db-tests 2026-02-09 22:58:10 +00:00
Jake BarnbyandClaude Opus 4.6 39b6aad959 fix: Use 'columns' instead of 'attributes' for TablesDB index creation
The TablesDB index creation API requires 'columns' parameter, not
'attributes' (which is the collections API parameter). This caused
testDeleteIndexTablesAPI to always fail because the index was never
actually created.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 22:11:18 +13:00
Jake BarnbyandClaude Opus 4.6 7bfae7c2c3 fix: Add diagnostic assertions to Realtime test helpers
Add response status checks on all creation calls (database, table,
column, index) to catch silent failures early and provide diagnostic
info instead of mysterious 404s during polling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:26:16 +13:00
Jake BarnbyandClaude Opus 4.6 4412be13f0 fix: Increase Proxy deployment activation timeout from 100s to 200s
Same issue as Sites - under CI parallel load the deployment builds
successfully but activation propagation takes longer than 100s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:03:56 +13:00
Jake BarnbyandClaude Opus 4.6 b597fb9956 fix: Replace sleep(5) with proper polling and increase timeouts in Realtime tests
The Realtime test helpers used fragile sleep(5) for attribute/index
creation and had short polling timeouts (30s/60s) that fail under CI
parallel load. Replaced with assertEventually polling and increased
all timeouts to 120s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 20:42:15 +13:00
Jake BarnbyandClaude Opus 4.6 6a1fa1d608 fix: Increase schema polling timeouts from 3min to 5min for CI stability
Under parallel load in CI, attribute/index workers can take longer than
3 minutes to process. Increase all polling timeouts to 5 minutes to
reduce flaky failures from worker queue congestion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:54:01 +13:00
Jake BarnbyandClaude Opus 4.6 2a41fc6f23 fix: Move server-side check before setupCollection in testConsoleProject
Prevents risky test warning where setupCollection makes assertions
before expectNotToPerformAssertions is called for server-side tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:15:35 +13:00
Jake BarnbyandClaude Opus 4.6 a55a784f07 fix: Use fresh magic URL token in testCreateSessionWithMagicUrl
The cached token from setupMagicUrl may have been consumed by
setupMagicUrlSession in a previous test method in the same process,
causing a 401 when the test tries to create a session with it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 18:20:36 +13:00
Jake BarnbyandClaude Opus 4.6 64d6efd098 fix: Add missing attribute types to Collection/AttributeList models and fix parallel test issues
- Add MODEL_ATTRIBUTE_VARCHAR/TEXT/MEDIUMTEXT/LONGTEXT to Collection and
  AttributeList models, fixing TypeError in Response::hasModel() that
  caused 4974 server crashes when serializing collections with new
  string attribute types
- Initialize $ruleType to null in Response::output() to prevent
  undefined variable when no model condition matches
- Isolate testUniqueIndexDuplicate with its own collection to prevent
  duplicate title interference from parallel tests in the same process
- Add retry mechanism to phone session creation for OTP token issues
- Increase GraphQL function build timeout from 30s to 120s
- Increase Sites deployment activation timeout from 100s to 200s
- Relax GraphQL bulk update row count assertion for parallel safety

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:53:21 +13:00
Jake BarnbyandClaude Opus 4.6 b3eecac6f9 fix: Add error details to assertions and handle 409 in relationship setup
- Add response body to assertion messages for debugging 400/409 errors
- Re-add 409 handling for relationship creation (race condition in parallel mode)
- Use explicit fullName: null instead of empty data for null person documents

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 16:56:08 +13:00
Jake BarnbyandClaude Opus 4.6 d617f89642 fix: Fix parallel test failures across Databases, GraphQL, Realtime, and Account
- DatabasesBase: Wait for twoWayKey attribute on library collection in
  setupOneToManyRelationship; use server API key headers for document creation
- DatabasesStringTypesTest: Add delays between attribute creation batches to
  avoid overwhelming the database worker
- SchemaPolling: Improve waitForAllAttributes error messaging
- GraphQL UsersTest: Use fresh user in testDeleteUserSession to avoid
  session conflicts
- Realtime: Increase WebSocket timeout to 120s and index polling to 60s
- Account: Add better error reporting for phone session creation failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 16:08:55 +13:00
Jake BarnbyandClaude Opus 4.6 e319feb1d4 fix: Fix relationship twoWayKey, hardcoded IDs, and datetime format issues
- Set explicit twoWayKey in testOneToOneRelationship to avoid auto-generated ID
- Replace hardcoded person10/library10/11 IDs with ID::unique() in setupOneToManyRelationship
- Fix remaining datetime format comparison in testUpdateScheduledAt
- Use assertStringContainsString for Projects mock numbers excess items test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 15:28:38 +13:00
Jake BarnbyandClaude Opus 4.6 1b9d0ca1c6 fix: Fix remaining parallel-safety issues from CI iteration 2
- Fix DB relationship tests: use ID::unique() for person/library collections
- Fix DB StringTypesTest: use waitForAllAttributes instead of sleep(3)
- Fix DB permissions tests: update expected counts for parallel mode
- Fix Account OAuth tests: clean up shared mock OAuth email before use
- Fix Account phone tests: ensure phone auth enabled, unique phone numbers
- Fix Messaging: compare datetime via timestamps, accept processing state
- Fix GraphQL testGetColumn: call setupStringColumn instead of setupTable
- Fix Projects: update mock numbers error message assertion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:55:41 +13:00
Jake BarnbyandClaude Opus 4.6 161e4e53d4 fix: Address remaining parallel-safety failures across all test suites
- Fix Legacy DatabasesStringTypesTest with project-keyed setup cache
- Fix Account OTP test with unique email, phone session re-login
- Fix GraphQL setup methods with sleep, 409 handling, missing columns
- Fix Databases list/pagination tests with document ID filtering
- Fix Permissions tests with assertGreaterThanOrEqual for counts
- Fix Messaging scheduled tests to not depend on scheduler timing
- Increase Realtime WebSocket timeout to 60s and assertEventually to 30s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:20:42 +13:00
Jake BarnbyandClaude Opus 4.6 2da4211f69 fix: Make E2E tests parallel-safe for functional mode
- Fix missing return statement in DatabasesBase::testListDocuments
- Rewrite DatabasesStringTypesTest with project-keyed setup cache
- Use assertGreaterThanOrEqual in Functions testListDeployments
- Clean up OIDC provider after Account OAuth2 test
- Key all GraphQL static caches by project ID with unique IDs
- Replace sleep() with assertEventually in Realtime tests
- Increase Messaging scheduled message timeout to 180s
- Increase Sites deployment timeouts to 120s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:25:19 +13:00
Jake BarnbyandClaude Opus 4.5 c5ec632709 fix: Make Functions execution tests parallel-safe
- testListExecutions: Don't assume position in results list
- Use assertContains with array_column instead of position[0]

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:59:51 +13:00
Jake BarnbyandClaude Opus 4.5 870ccc9a1b fix: Fix webhook tests for parallel mode
- testDeleteUser: User is created with status=true, so assert that
- testUpdateUserStatus: Use probe to find correct webhook event

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:58:53 +13:00
Jake BarnbyandClaude Opus 4.5 0737ba1329 fix: Make more tests parallel-safe for functional mode
- Account: Use unique emails/phone numbers to avoid collisions
- Functions: Use flexible assertions for counts and search results
- GraphQL: Add better error handling and use unique IDs
- Projects: Use assertGreaterThanOrEqual for list counts
- Webhooks: Use probe to find specific delete webhook event

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:48:55 +13:00
Jake BarnbyandClaude Opus 4.5 627def5993 fix: Make Functions testListDeployments parallel-safe
- Use foreach loop to check all deployments instead of hardcoded indices
- Fix offset test to not assume exactly 1 function exists

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:25:10 +13:00
Jake BarnbyandClaude Opus 4.5 6e103393b8 fix: Fix undefined $data variables in Webhooks tests
- testCreateAttributes: Use $actorsId instead of undefined $data['actorsId']
- testCreateColumns: Use $actorsId instead of undefined $data['actorsId']
- testCreateAccountSession: Add assertion for account creation success

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:23:44 +13:00
Jake BarnbyandClaude Opus 4.5 a4cca146d3 fix: Remove undefined $totalUsers and use parallel-safe assertions
The testListUsers function was using $totalUsers which was removed when
the test was refactored for parallel execution. Now uses $minUsers and
flexible assertions instead of position-based assertions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:22:14 +13:00
Jake BarnbyandClaude Opus 4.5 e605848541 perf: Enable --functional mode for true method-level parallelism
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:19:38 +13:00
Jake BarnbyandClaude Opus 4.5 9f9d195daf fix: Add retry logic and defensive checks for Teams owner downgrade test
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:16:41 +13:00
Jake BarnbyandClaude Opus 4.5 a79f33a3a1 fix: Remove incorrect json_decode in setupUser
The client body is already decoded as array, not JSON string.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:13:23 +13:00
Jake BarnbyandClaude Opus 4.5 6be719c9d4 fix: Add retry logic for 401 errors in ProjectCustom::getProject
Under high parallel load, authentication can transiently fail with 401.
Add retry logic with delays to handle these transient failures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:07:00 +13:00
Jake BarnbyandClaude Opus 4.5 ce200d20d4 fix: Make webhook account tests self-sufficient
Update account name before testing to ensure webhook data has
expected 'New Name' value, regardless of test execution order.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:59:58 +13:00
Jake BarnbyandClaude Opus 4.5 8627251ae4 fix: Make testDeleteStorageBucket self-sufficient
Update bucket name before deleting to ensure webhook data has
expected name, regardless of whether testUpdateStorageBucket ran.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:59:16 +13:00
Jake BarnbyandClaude Opus 4.5 1f17dca7c9 fix: Handle 409 conflicts in GraphQL TablesDB tests
- setupDatabase() now fetches existing database on conflict
- setupTable() now fetches existing tables on conflict
- Enables parallel test execution with fixed resource IDs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:56:45 +13:00
Jake BarnbyandClaude Opus 4.5 0e828f3fcc fix: Handle 409 conflicts in GraphQL Legacy tests
- setupDatabase() now fetches existing database on conflict
- setupCollections() now fetches existing collections on conflict
- Enables parallel test execution with fixed resource IDs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:55:59 +13:00
Jake BarnbyandClaude Opus 4.5 d4020686f2 fix: Make testListUsers parallel-safe
- Use assertGreaterThanOrEqual instead of exact count
- Find users by ID instead of assuming position
- Document expected minimum user count for isolated execution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:53:42 +13:00
Jake BarnbyandClaude Opus 4.5 de8915b416 fix: Make testListBucket self-sufficient for parallel execution
Create bucket1 within testListBucket instead of relying on
testCreateBucket running first. Accept 409 if bucket already exists.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:47:29 +13:00
Jake BarnbyandClaude Opus 4.5 fd8e8fc736 fix: Make Storage cursorAfter test parallel-safe
Use assertGreaterThanOrEqual and find bucket by ID instead of
assuming exact count and position after cursor query.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:42:43 +13:00
Jake BarnbyandClaude Opus 4.5 07c9a274b1 fix: Use assertGreaterThanOrEqual for fileSecurity bucket count
In parallel execution, there may be more buckets with fileSecurity=true

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:36:21 +13:00
Jake BarnbyandClaude Opus 4.5 40ed0da8b3 fix: Fix testUpdateAccountNameSearch for parallel execution
Use assertGreaterThanOrEqual and find user by ID instead of assuming position

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:27:47 +13:00
Jake BarnbyandClaude Opus 4.5 a2ae55dcbe fix: Improve parallel test isolation for Account, Storage, Messaging
- Account: Add createFreshAccountWithSession() for predictable session/log counts
- Account: Update testGetAccountSessions and testGetAccountLogs to use fresh accounts
- Storage: Fix testListBucket to find bucket by ID instead of assuming first position
- Messaging: Fix testListSubscribers to find subscriber by ID instead of assuming first position

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:24:28 +13:00
Jake BarnbyandClaude Opus 4.5 3b025fa12a fix: Make testCreateAttributes and testCreateIndexes parallel-safe
These tests were creating resources directly in the shared collections,
which conflicts with setupAttributes() and setupIndexes() when tests
run in parallel. Now each test creates its own dedicated collection
to avoid conflicts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 18:50:36 +13:00
Jake BarnbyandClaude Opus 4.5 945eb43558 fix: Use more entropy for email generation in parallel tests
Adding process ID and random bytes to uniqid() to avoid email
collisions when running tests in parallel with functional mode.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 18:00:27 +13:00
Jake BarnbyandClaude Opus 4.5 62e0598008 fix: Remove more invalid returns from void test methods
Removed return statements from testManyToOneRelationship and
testManyToManyRelationship which were declared as void. These
cause PHP fatal errors in PHP 8.4+.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 17:46:42 +13:00
Jake BarnbyandClaude Opus 4.5 fd0a4a0e88 fix: Remove invalid return from void testUpdateDocument method
The testUpdateDocument method was declared as void but returned [].
This causes a PHP fatal error in PHP 8.4.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 17:36:20 +13:00
Jake BarnbyandClaude Opus 4.5 2a4ef06355 fix: Make Sites tests parallel-safe for --functional mode
Add search filters to isolate test data in testListSites to prevent
count assertions from failing when other test methods create sites
during parallel execution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 17:24:57 +13:00
Jake BarnbyandClaude Opus 4.5 2d160747ca perf: Remove @depends from Migrations, Projects, and Tokens tests
Added helper methods with static caching for independent test execution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 12:13:54 +13:00
Jake BarnbyandClaude Opus 4.5 2a3e2d8be5 perf: Remove @depends from all GraphQL tests
Added helper methods with static caching for:
- Legacy/DatabaseClientTest.php
- Legacy/DatabaseServerTest.php
- TablesDB/DatabaseClientTest.php
- TablesDB/DatabaseServerTest.php

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 12:01:31 +13:00
Jake BarnbyandClaude Opus 4.5 e976617869 perf: Remove @depends from Databases permissions and partial GraphQL tests
- Added setupDatabase() helper with caching to all permission test files
- Removed @depends from LegacyPermissionsMemberTest, LegacyPermissionsTeamTest
- Removed @depends from TablesDBPermissionsMemberTest, TablesDBPermissionsTeamTest
- Partial GraphQL test updates from agents

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:56:58 +13:00
Jake BarnbyandClaude Opus 4.5 f624646480 perf: Remove @depends from Account, Functions, Storage, Users, VCS, Messaging tests
Added helper methods with static caching to enable parallel test execution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:54:14 +13:00
Jake BarnbyandClaude Opus 4.5 f5f60ab85b perf: Remove @depends from Database Transactions tests
Enables parallel test execution for Transactions tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:47:23 +13:00
Jake BarnbyandClaude Opus 4.5 b1257cc40a perf: Remove @depends from Webhooks and Teams tests
Enables --functional mode for parallel test execution by:
- Adding helper methods for independent test setup
- Removing all #[Depends] annotations
- Using static caching for shared resources

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:47:12 +13:00
Jake BarnbyandClaude Opus 4.5 02240fd14f fix: Correct TablesDB API paths in Realtime test helpers
- Use /databases endpoint instead of /tablesdb for database creation
- Use /columns/string instead of /attributes/string for table columns
- Fix testAttributesCollectionsAPI return type from array to void

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:46:59 +13:00
Jake Barnby 24f8275f52 Revert "fix: run Databases and FunctionsSchedule tests sequentially"
This reverts commit 8dadcfeebe.
2026-02-06 04:36:55 +13:00
Jake BarnbyandClaude Opus 4.5 2e9c1b4745 fix: Remove @depends chains from Realtime tests for parallel execution
- Add helper methods createCollectionWithAttribute(), createTableWithAttribute(),
  createCollectionWithIndex(), createTableWithIndex() to RealtimeConsoleClientTest
- Add createTeam() helper to RealtimeCustomClientTest
- Replace all @depends annotations with direct helper method calls
- Enable tests to run in parallel with --functional flag

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:28:12 +13:00
Jake BarnbyandClaude Opus 4.5 8dadcfeebe fix: run Databases and FunctionsSchedule tests sequentially
Parallel test execution with paratest overwhelms the single databases
worker when many tests create attributes simultaneously. Run these
specific services sequentially with PHPUnit to ensure the worker
can keep up with attribute processing jobs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:15:56 +13:00
Jake BarnbyandClaude Opus 4.5 2c5dee0fdd fix: resolve test failures in Databases, Functions, Messaging, Storage
- Fix typo CLient -> Client in DatabasesBase.php:4568
- Fix typo CLient -> Client in StorageCustomClientTest.php:428
- Fix cleanupFunction() call with extra parameter in FunctionsScheduleTest.php:175
- Increase message scheduling test timeouts from 30s to 180s for CI stability

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:03:40 +13:00
Jake BarnbyandClaude Opus 4.5 4631a2a75f Revert test changes (back to original state)
The --functional flag cannot be used because tests rely on static
properties to share state between test methods (e.g., $databaseId
set in testCreateDatabase is used by testCreateTable). With --functional,
each test method runs in a separate process so these properties are not
initialized.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 03:42:32 +13:00
Jake BarnbyandClaude Opus 4.5 1ef96627d3 fix: scale databases worker to handle parallel test load
The root cause of flaky attribute processing tests is that a single
appwrite-worker-databases container cannot keep up with attribute
creation jobs when tests run in parallel via paratest.

This fix:
- Scales the databases worker to 4 instances during E2E service tests
- Reverts timeout increases that masked the underlying issue

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 03:04:22 +13:00
Jake BarnbyandClaude Opus 4.5 fb2dc89041 fix: increase SchemaPolling timeout to 10 minutes for CI stability
Increase all default timeouts in SchemaPolling trait from 5 minutes to
10 minutes. With tests running in parallel via paratest, the attribute
processing worker can become backlogged under heavy CI load, requiring
longer wait times for attributes to transition from 'processing' to
'available' status.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 02:59:38 +13:00
Jake BarnbyandClaude Opus 4.5 262425fdb6 fix: increase SchemaPolling timeout to 5 minutes for CI stability
Increase all default timeouts in SchemaPolling trait from 3 minutes to
5 minutes to handle slower attribute processing when running tests in
parallel under heavy CI load.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 02:41:55 +13:00
Jake Barnby c8d2cc090e Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	tests/e2e/Services/Databases/DatabasesBase.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2026-02-06 01:29:44 +13:00
Jake BarnbyandClaude Opus 4.5 3ea4810d3a fix: increase SchemaPolling default timeout to 3 minutes for CI
Increase all default timeouts in SchemaPolling trait from 2 minutes to
3 minutes to handle slower attribute processing in CI environments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 18:57:36 +13:00
Jake BarnbyandClaude Opus 4.5 20f7af49b1 fix: increase FunctionsSchedule test timeout for CI stability
Remove explicit sleep and increase assertEventually timeout from 60s to
180s to account for slower deployment and scheduling in CI. Also use
assertGreaterThanOrEqual to handle cases where multiple executions
may have run.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 18:45:30 +13:00
Jake BarnbyandClaude Opus 4.5 a9b55f4c74 fix: improve test stability for CI failures
- Skip FunctionsCustomServerTest::testCreateFunctionAndDeploymentFromTemplateCommit when GitHub API is rate-limited or unavailable
- Increase waitForAllAttributes timeout to 3 minutes for testBulkDeleteMatchingUpdatedDocuments to handle slow attribute processing in CI

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 18:31:26 +13:00
Jake BarnbyandGitHub 4a2e3b7b22 Merge branch '1.8.x' into feat-db-tests 2026-02-05 03:12:24 +00:00
Jake BarnbyandClaude Opus 4.5 6850d7ae9e fix: add sleep for async attribute deletion verification
Attribute deletion is asynchronous - add sleep to wait for deletion
to complete before verifying the attribute no longer exists.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 02:01:01 +13:00
Jake BarnbyandClaude Opus 4.5 45a3793cc0 fix: correct expected default values and attribute name in tests
- Update expected default values to match the actual defaults (update
  tests are skipped, so original defaults apply)
- Fix varchar_max -> varchar_min (varchar_max was never created)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 01:48:36 +13:00
Jake BarnbyandClaude Opus 4.5 2e061e2f2b fix: make testCreatePhoneVerification self-contained
Remove @depends annotation and set up phone number within the test
to ensure it works with PHPUnit 12 and paratest parallel execution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 01:33:01 +13:00
Jake BarnbyandClaude Opus 4.5 6f6ea94a19 fix: make testUpdateWithExistingRelationships self-contained
Remove @depends annotation and create a new database within the test
to fix compatibility with PHPUnit 12 and paratest parallel execution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 01:30:52 +13:00
Jake BarnbyandClaude Opus 4.5 0aff01793b fix: remove @depends annotations from DatabasesStringTypesTest
Replace @depends annotations with static property assertions to fix
compatibility issues with PHPUnit 12 and paratest parallel execution.

Tests now use self::$databaseId and self::$collectionId/self::$tableId
directly instead of passing data between dependent tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 01:18:16 +13:00
Jake Barnby e70739a3c0 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	Dockerfile
#	app/controllers/api/account.php
#	app/controllers/api/avatars.php
#	app/controllers/api/console.php
#	app/controllers/api/projects.php
#	app/controllers/api/storage.php
#	app/controllers/api/teams.php
#	app/http.php
#	app/init/database/filters.php
#	app/init/registers.php
#	app/init/resources.php
#	app/views/install/compose.phtml
#	composer.json
#	composer.lock
#	docker-compose.yml
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Update.php
#	src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php
#	src/Appwrite/Platform/Tasks/Install.php
#	tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
2026-02-05 01:01:03 +13:00
Jake BarnbyandClaude Opus 4.5 f34983f056 fix: update composer image to 2.x for PHP 8.3+ compatibility
PHPUnit 12 requires PHP 8.3+ and uses PHP enums like
CodeCoverageInitializationStatus. The old composer:2.0 image used
PHP 8.0.6 which doesn't support enums, causing autoloader issues.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 00:41:50 +13:00
Jake Barnby 4933cdda10 Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests 2026-02-05 00:16:15 +13:00
Jake Barnby b704378bf1 Revert platform downgrade 2026-02-05 00:16:08 +13:00
Jake Barnby cfa4c0876b Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	composer.lock
#	src/Appwrite/Messaging/Adapter/Realtime.php
2026-02-04 23:47:08 +13:00
Jake Barnby 936adce15b Update phpunit 2026-02-04 23:45:46 +13:00
Jake BarnbyandClaude Opus 4.5 0a492fb1e2 fix: pin utopia-php/platform to 0.7.14 for framework compatibility
Platform 0.7.15 introduced a breaking change that requires Utopia\Http
instead of Utopia\App, but framework 0.33.x still uses Utopia\App.
This pins platform to 0.7.14 which is compatible with the current
framework version.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 23:30:23 +13:00
Jake Barnby cdcee01015 Update lock 2026-02-04 19:53:28 +13:00
Jake Barnby d4694ff8af Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	composer.json
#	composer.lock
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
#	tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
#	tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
2026-02-04 19:49:09 +13:00
Jake BarnbyandGitHub afe800197b Update .github/workflows/tests.yml 2026-01-19 23:39:53 +13:00
Jake Barnby 8335e43eaa Merge branch '1.8.x' into feat-db-tests 2026-01-19 18:34:35 +13:00
Jake Barnby a425c460c2 CI 2026-01-17 01:25:50 +13:00
Jake Barnby 9331ae1cbb CI 2026-01-17 01:02:25 +13:00
Jake Barnby 0cc3db6095 Fix flaky test timing issues
- Increase SchemaPolling default timeout from 60s to 120s
- Replace hardcoded sleep() with assertEventually() for message status polling in Messaging tests
2026-01-16 22:57:05 +13:00
Jake Barnby 36c4228de6 Remove debug code 2026-01-16 22:31:35 +13:00
Jake Barnby 34810ca5bb Debug: dump response body on failure 2026-01-16 21:44:28 +13:00
Jake Barnby 392619284e Fix perms tests 2026-01-16 21:09:46 +13:00
Jake Barnby 2dae0ece5d Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests 2026-01-16 20:50:00 +13:00
Jake BarnbyandClaude Opus 4.5 63b3365ce9 Fix PHPUnit 11 migration test issues
- Add missing @depends testCreateDatabase to testUpdateWithExistingRelationships in DatabasesBase.php
- Fix data provider keys in LegacyPermissionsMemberTest and TablesDBPermissionsMemberTest to match parameter names (any->anyCount, users->usersCount, doconly->docOnlyCount)
- Convert provideCustomExecutions data provider to positional arrays to avoid PHPUnit 11 named argument conflicts with @depends
- Restore @depends testUpdateAccountPhone for testCreatePhoneVerification in GraphQL AccountTest

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 18:59:53 +13:00
Jake BarnbyandClaude Opus 4.5 4adc4f65ad Add missing PHPUnit #[Depends] to GraphQL Functions tests
Fixed missing #[Depends] attributes:
- FunctionsClientTest: 4 methods (testCreateDeployment, testCreateExecution,
  testGetExecutions, testGetExecution)
- FunctionsServerTest: 11 methods (all test methods with parameters)
- Legacy/DatabaseServerTest: Fixed testCreateIndex dependency
  (was depending on void-returning testUpdateURLAttribute)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 18:28:38 +13:00
Jake BarnbyandClaude Opus 4.5 7498c330eb Add missing PHPUnit #[Depends] to Functions, Messaging, Webhooks tests
Fixed missing #[Depends] attributes:
- FunctionsCustomServerTest: testCancelDeploymentBuild → testCreateDeployment
- MessagingBase: testDeleteMessage → testSendEmail
- WebhooksBase: testDeleteDocument → testUpdateDocument, testDeleteRow → testUpdateRow
- WebhooksCustomClientTest: testUpdateAccountName → testDeleteAccountSessions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 18:18:55 +13:00
Jake BarnbyandClaude Opus 4.5 1d03af22e4 Add missing PHPUnit #[Depends] attributes to GraphQL tests
PHPUnit 11 requires explicit #[Depends] attributes. The migration removed
@depends annotations but didn't add all corresponding #[Depends] attributes.

Fixed in:
- GraphQL/Legacy/DatabaseClientTest: 5 methods
- GraphQL/StorageClientTest: 7 methods
- GraphQL/StorageServerTest: 10 methods
- GraphQL/TeamsServerTest: 3 methods
- GraphQL/TeamsClientTest: 2 methods
- GraphQL/TablesDB/DatabaseServerTest: ~40 methods
- GraphQL/TablesDB/DatabaseClientTest: 5 methods

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 18:10:30 +13:00
Jake BarnbyandClaude Opus 4.5 7d6ac8f7a2 Add missing PHPUnit #[Depends] attributes
PHPUnit 11 requires explicit #[Depends] attributes instead of the old
@depends annotations. Several test methods were missing these attributes,
causing "Too few arguments" errors when PHPUnit tried to run the tests.

Fixed in:
- AccountCustomClientTest: testUpdateAccountPassword, testUpdateAccountRecovery, testCreateSession
- GraphQL/Legacy/DatabaseServerTest: Added 36 missing #[Depends] attributes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:51:31 +13:00
Jake BarnbyandClaude Opus 4.5 c5c2c44755 Fix PHPUnit --exclude-group deprecation warning
PHPUnit 11+ warns when using comma-separated values with --exclude-group.
This warning causes the tests to return exit code 1 despite all tests passing.

Fix by using separate --exclude-group flags for each group.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:33:51 +13:00
Jake Barnby 46f3fadc5d Fix hooks 2026-01-15 17:21:05 +13:00
Jake Barnby 714b8d7ea6 Migrate to PHPUnit 11 2026-01-15 16:14:53 +13:00
Jake Barnby 84f9485049 Fix tests 2026-01-15 14:04:39 +13:00
Jake Barnby d7a6a7b5d8 Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	composer.lock
#	tests/e2e/Services/Databases/Legacy/Permissions/DatabasesPermissionsGuestTest.php
#	tests/e2e/Services/Databases/TablesDB/Permissions/DatabasesPermissionsGuestTest.php
2026-01-15 12:43:26 +13:00
Jake Barnby feab8a26e5 Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	composer.lock
#	tests/e2e/Services/Databases/Legacy/Permissions/DatabasesPermissionsGuestTest.php
#	tests/e2e/Services/Databases/TablesDB/Permissions/DatabasesPermissionsGuestTest.php
2026-01-14 19:59:36 +13:00
Jake Barnby 962626a4fb Update lock 2026-01-14 01:56:02 +13:00
Jake Barnby 0f953e4a52 Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	composer.lock
2026-01-14 01:33:22 +13:00
Jake Barnby bb09abc087 Update CTA check 2026-01-14 01:23:48 +13:00
Jake Barnby 0b27d92509 Update lock 2026-01-13 22:09:05 +13:00
Jake Barnby 5153617f04 Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests
# Conflicts:
#	composer.lock
#	tests/e2e/Services/Databases/Legacy/Permissions/DatabasesPermissionsGuestTest.php
#	tests/e2e/Services/Databases/TablesDB/Permissions/DatabasesPermissionsGuestTest.php
2026-01-13 22:04:36 +13:00
Jake Barnby a34fa1ceb6 Fix abuse 2026-01-13 22:01:22 +13:00
Jake Barnby cd21140572 Fix request fetching 2026-01-13 22:01:16 +13:00
Jake Barnby 8eb09a98fe Fix otp user 2026-01-11 02:49:28 +13:00
Jake Barnby 879a599bed Fix parallel cases 2026-01-11 02:33:20 +13:00
Jake Barnby 1a22c281f5 Parallelise 2026-01-10 21:25:06 +13:00
Jake Barnby f797ae3ac6 Remove sleeps 2026-01-10 14:53:29 +13:00
Jake Barnby 658308a447 Merge remote-tracking branch 'origin/1.8.x' into feat-db-tests 2026-01-10 03:21:38 +13:00
Jake Barnby 9498a8ba5d Consolidate, remove depends, add fixtures 2026-01-10 03:21:31 +13:00
Jake Barnby 29dd4e23e2 Update console 2025-12-17 18:36:30 +13:00
Jake Barnby 58470bfa7f Update upgrade/install flows 2025-12-17 18:36:23 +13:00
Jake Barnby 4fb43c6e69 Fix install flow 2025-12-17 16:14:20 +13:00
Jake Barnby c5dadc787d Block operator tests 2025-12-11 00:23:46 +13:00
Jake Barnby 587a0facf7 Fix teams test 2025-12-10 22:33:30 +13:00
Jake Barnby a8cf0a6176 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	app/controllers/api/teams.php
#	app/init/resources.php
#	composer.lock
#	tests/e2e/Services/Databases/Legacy/DatabasesBase.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-12-10 20:12:27 +13:00
Jake Barnby 2139a4d742 Format 2025-11-26 23:49:21 +13:00
Jake Barnby b9ca0e5dc2 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-11-26 22:50:15 +13:00
Jake Barnby cdb331a704 Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2025-11-26 22:49:50 +13:00
Jake Barnby 092f623373 Fix tests 2025-11-26 22:49:39 +13:00
Jake BarnbyandGitHub bd124aad2a Merge branch '1.8.x' into feat-mongodb 2025-11-25 08:26:00 +00:00
Jake Barnby 2776925540 Tests via console features 2025-11-25 21:11:01 +13:00
Jake Barnby 188f8f3ecd Fix missing param 2025-11-20 20:20:58 +13:00
Jake Barnby e12d463fe0 Fix merge 2025-11-20 19:13:14 +13:00
Jake Barnby d8d970e03e Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	app/controllers/api/account.php
#	app/controllers/api/messaging.php
#	app/controllers/api/projects.php
#	app/controllers/api/storage.php
#	app/controllers/api/teams.php
#	app/controllers/api/users.php
#	composer.lock
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php
#	src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php
#	src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php
#	src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php
#	src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
2025-11-20 17:46:19 +13:00
Jake Barnby 1e1df1ce2d Fix skips 2025-10-24 00:06:58 +13:00
Jake Barnby bae759957c Fix graphql test 2025-10-23 23:57:11 +13:00
Jake Barnby 9aba82630a Fix console test 2025-10-23 23:57:00 +13:00
Jake Barnby 3f473f2790 Fix checks 2025-10-23 23:40:27 +13:00
Jake Barnby b79439a730 Default to mariadb for existing installs 2025-10-23 23:02:41 +13:00
Jake Barnby 861684883f Add DB adapter console var 2025-10-23 23:02:29 +13:00
Jake Barnby 337503ed47 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.lock
2025-10-23 17:15:57 +13:00
Jake Barnby 71f52025ec Review fixes 2025-10-22 00:30:00 +13:00
Jake Barnby 505181d749 Update lock 2025-10-21 18:45:27 +13:00
Jake Barnby b12e006014 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	app/init/resources.php
#	composer.lock
2025-10-21 18:43:41 +13:00
Jake Barnby 1e60a01a69 Use db length for project ID 2025-10-21 15:31:46 +13:00
Jake Barnby f1c7af0426 Fix wrong db for UID max length check 2025-10-21 15:04:08 +13:00
Jake Barnby c3d4cf947e Fix unit test 2025-10-21 14:24:32 +13:00
Jake Barnby 0d35af8cfa Fix tests 2025-10-21 14:24:26 +13:00
Jake Barnby 390d1560f4 Fix tests 2025-10-21 14:16:59 +13:00
Jake Barnby 94bf1f6ee0 Add missing imports 2025-10-21 14:15:45 +13:00
Jake Barnby 2870419e1b Update lock 2025-10-21 14:10:32 +13:00
Jake Barnby f9b0455794 Revert "Fix optionality"
This reverts commit 994de06457.

# Conflicts:
#	app/controllers/api/messaging.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php
2025-10-21 13:05:54 +13:00
Jake Barnby 6a7b8f0726 Add injection max length on txn 2025-10-21 13:03:24 +13:00
Jake Barnby 994de06457 Fix optionality 2025-10-21 12:36:13 +13:00
Jake Barnby ad9e6afd86 Fix sync 2025-10-21 03:18:29 +13:00
Jake Barnby 3a8e2b080d Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	app/init/resources.php
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Indexes/Create.php
#	tests/e2e/Services/Databases/Legacy/DatabasesBase.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-10-21 03:16:21 +13:00
Jake BarnbyGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1f743dfe8c Update src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-10-14 22:23:38 +13:00
Jake Barnby a4093067ac Update database 2025-10-14 21:06:32 +13:00
Jake Barnby 72868ed7aa Fix async index delete 2025-10-14 19:39:49 +13:00
Jake Barnby f328a68ec1 Update db for decode fix 2025-10-14 19:39:40 +13:00
Jake Barnby 754537f34b Update lock 2025-10-14 18:16:48 +13:00
Jake Barnby bc4326414b Retry loop on create databases 2025-10-14 18:12:09 +13:00
Jake Barnby 3e7f865f0a Update lock 2025-10-14 17:13:05 +13:00
Jake Barnby e956ad58a9 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.lock
2025-10-14 16:42:40 +13:00
Jake Barnby ddec873f72 Update database 2025-10-14 16:39:44 +13:00
Jake Barnby f78f08bff9 Transaction support 2025-10-13 18:23:43 +13:00
Jake Barnby 38b04566b6 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php
2025-10-09 17:36:22 +13:00
Jake Barnby c89af1201a Fix tests 2025-10-09 16:37:17 +13:00
Jake Barnby 9f85ca83b4 Fix roles param 2025-10-09 14:33:05 +13:00
Jake Barnby d5d09061e9 Fix index validator params 2025-10-09 13:46:14 +13:00
Jake Barnby 143be8239f Fix inject 2025-10-09 00:41:25 +13:00
Jake Barnby da519046dd Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2025-10-08 00:05:05 +13:00
Jake Barnby 0c5573ab05 Fix list wrapping UID injections 2025-10-08 00:04:52 +13:00
Jake BarnbyandGitHub fa60f03536 Merge branch '1.8.x' into feat-mongodb 2025-10-07 18:52:28 +13:00
Jake Barnby 28392647e0 Pass adapter max ID length to validator 2025-10-07 18:52:05 +13:00
Jake Barnby 4b0eddcc84 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.json
#	composer.lock
2025-10-07 18:16:02 +13:00
Jake Barnby 94e40c0127 Update database 2025-10-04 10:15:13 +13:00
Jake Barnby 498235a2ef Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-10-03 23:19:33 +13:00
Jake Barnby 52c9671157 Update deps 2025-10-03 23:19:09 +13:00
Jake Barnby 61a1efd0fe Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-10-02 19:25:56 +13:00
Jake Barnby faf837e389 Update db for index validation fix 2025-10-02 15:29:27 +13:00
Jake Barnby 3ecd7e6a95 Fix fts create on array 2025-10-01 22:02:41 +13:00
Jake Barnby 36782650c6 Revert "Add CI debug"
This reverts commit 2d3dea84e3.
2025-10-01 21:36:03 +13:00
Jake Barnby 2f6bc9115f Fix workflows init 2025-10-01 21:35:55 +13:00
Jake Barnby 2d3dea84e3 Add CI debug 2025-10-01 21:10:26 +13:00
Jake Barnby b12f4008eb Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.lock
2025-10-01 20:13:48 +13:00
Jake Barnby 408bca400b Fix spatial tests 2025-10-01 19:33:11 +13:00
Jake Barnby 6bacec52c5 Fix storage test 2025-10-01 17:11:02 +13:00
Jake Barnby 9d70b5cab2 Fix domain test 2025-10-01 17:09:07 +13:00
Jake Barnby d9baf5b628 Fix sites error 2025-10-01 12:31:07 +13:00
Jake Barnby 9e837c2eb9 Fix storage error 2025-10-01 12:31:02 +13:00
Jake Barnby f3460db369 Fix fulltext error 2025-10-01 12:30:48 +13:00
Jake Barnby b57839402f Update DB 2025-10-01 02:14:51 +13:00
Jake Barnby 763158fe81 Fix usage tests 2025-10-01 01:30:18 +13:00
Jake Barnby 766c3293d2 Fix index text checks 2025-10-01 00:47:07 +13:00
Jake Barnby 1226c0834c Fix upsert 2025-10-01 00:13:09 +13:00
Jake Barnby f0983cd9ff Remove mounts 2025-10-01 00:03:07 +13:00
Jake Barnby daeb721995 Fix tests 2025-09-30 23:44:26 +13:00
Jake Barnby 0555a27656 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-09-30 22:58:38 +13:00
Jake Barnby da3284ce84 Update deps 2025-09-30 22:58:29 +13:00
Jake Barnby 53f8a3a78b Update DB 2025-09-29 19:39:51 +13:00
Jake Barnby aa77253b14 Fix OSV 2025-09-26 22:17:01 +12:00
Jake Barnby 78c5743e75 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.lock
2025-09-26 22:14:40 +12:00
Jake Barnby 156a944b54 Merge remote-tracking branch 'origin/feat-mongodb-index-validation' into feat-mongodb 2025-09-26 22:05:31 +12:00
Jake Barnby b07fd5afe6 Merge remote-tracking branch 'origin/feat-mongodb-pulls' into feat-mongodb 2025-09-26 22:05:24 +12:00
shimon b828621feb Update composer dependencies and enhance index validation in database module 2025-09-21 19:14:40 +03:00
shimon 3edc055c9d Skip spatial column tests for MongoDB support in DatabasesBase trait 2025-09-21 16:02:36 +03:00
shimon ceda513083 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	composer.lock
#	src/Appwrite/Platform/Workers/StatsUsage.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-09-21 15:55:11 +03:00
Shimon NewmanandGitHub 2d47b88246 Merge pull request #10527 from appwrite/feat-mongodb-pulls
Feat mongodb pulls
2025-09-21 15:46:15 +03:00
shimon b88e74095b Merge remote-tracking branch 'origin/feat-mongodb-pulls' into feat-mongodb-pulls
# Conflicts:
#	composer.json
2025-09-21 15:44:41 +03:00
shimon 4d85c5ca4f composer 2025-09-21 15:42:44 +03:00
shimon c89e649d05 composer 2025-09-21 15:38:35 +03:00
shimon d9bdf01cee db lib ver 2025-09-21 15:30:09 +03:00
shimon 54e3a4b25d db lib ver 2025-09-21 15:27:41 +03:00
shimon b1e55702c8 Refactor MongoDB test cases for improved readability and consistency. Adjust whitespace in conditional statements and remove redundant test methods related to spatial columns. Add new tests for spatial distance queries and update existing tests to ensure proper functionality. 2025-09-16 19:14:09 +03:00
shimon f16eba2f9f Update PHPUnit configuration to stop on failure set to false, and clean up whitespace in multiple files for consistency. Adjust comments and ensure proper formatting in test cases and API handling. 2025-09-16 19:13:38 +03:00
shimon 7c0bd1da88 uncomment inversion tests 2025-09-16 17:51:29 +03:00
shimon cc87de8934 Merge branch 'feat-mongodb-pulls' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	composer.lock
2025-09-16 17:43:47 +03:00
shimon 5f8dc9998c Refactor database document handling by replacing createOrUpdateDocuments with upsertDocuments in multiple classes. Clean up common.php by removing commented-out index definitions and ensuring proper indexing for MongoDB compatibility. Update UsageTest to correct API endpoint paths for database operations. 2025-09-16 17:39:41 +03:00
shimon cc7778ceea database lib check 2025-09-16 11:10:06 +03:00
shimon f5fc099d87 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	composer.lock
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-09-16 10:26:43 +03:00
shimon 89d06d00c0 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	.github/workflows/tests.yml
#	composer.lock
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-09-11 16:54:49 +03:00
shimon 882cd8c83e Merge remote-tracking branch 'origin/feat-mongodb-pulls' into feat-mongodb-pulls
# Conflicts:
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php
2025-09-11 16:43:39 +03:00
shimon 80cc6a0bb3 DatabasesCustomClientTest.php 2025-09-11 16:42:33 +03:00
shimon fc4ad175dd Update composer.json and composer.lock to reflect new versioning for utopia-php/database and other dependencies. Adjusted package versions for brick/math, open-telemetry/api, and symfony components. Added symfony/polyfill-php83 for compatibility with PHP 8.3 features. Minor formatting changes in MigrationsBase.php for code clarity. 2025-09-11 16:38:41 +03:00
shimon a0def8edf8 Updates 2025-08-31 17:57:58 +03:00
shimon bdb8204910 Refactor database attribute tests to streamline size validation and add support for additional integer index. Updated size constraints for 'description' and 'tagline' attributes, and introduced a new 'integers2' attribute to accommodate MongoDB limitations. Adjusted assertions to reflect these changes. 2025-08-27 16:32:13 +03:00
shimon 1274614d18 Update composer.json and composer.lock to reflect new versioning for utopia-php/database and other dependencies. Adjusted Docker configuration to set the database adapter to MariaDB. 2025-08-27 13:55:48 +03:00
shimon 4f937ac549 Update composer.json and composer.lock to reflect changes in utopia-php/database versioning, including the removal of the 'with-uuid7' suffix. Updated dependencies for composer/semver, symfony/polyfill packages, and phpunit/phpunit to their latest versions for improved compatibility and performance. 2025-08-27 10:28:17 +03:00
shimon 6910d669de Update composer.json to correct utopia-php/database version reference and modify test to use the correct endpoint for MongoDB operations. 2025-08-27 10:25:23 +03:00
shimon 4fb680350a Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	Dockerfile
#	composer.json
#	composer.lock
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomServerTest.php
2025-08-26 14:20:47 +03:00
Shimon NewmanandGitHub ff35dc6114 Merge pull request #10375 from appwrite/feat-mongodb-pulls
Feat mongodb pulls
2025-08-26 13:53:42 +03:00
shimon 38433b9ac1 Remove unnecessary var_dump statement from ProjectsConsoleClientTest for improved code clarity and consistency. 2025-08-26 13:52:05 +03:00
shimon b98be4756c Update composer.json and composer.lock to reflect changes in utopia-php/database versioning and dependencies. Adjusted MongoDB-related test conditions for improved compatibility and clarity. Removed unnecessary var_dump statements in account.php and enhanced logging in various test files for better debugging. 2025-08-26 13:48:01 +03:00
shimon ff8418ffb9 Update composer.lock to reflect new references and timestamps for dependencies, including updates for utopia-php/database and phpunit/phpunit. Removed duplicate entries and unnecessary lines for improved clarity. 2025-08-12 18:01:38 +03:00
shimon e151425b95 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls 2025-08-12 17:54:55 +03:00
shimon b9f4815c3a Remove unnecessary var_dump statements from api.php and enhance logging in MessagingConsoleClientTest for improved clarity and debugging. This aligns with recent efforts to clean up debugging output across the codebase. 2025-08-12 17:47:11 +03:00
shimon c4fb7ecbc1 Refactor and clean up debugging output across multiple files, including mock.php, account.php, and OAuth2 classes. Removed unnecessary var_dump statements for improved code clarity and consistency. Updated MongoDB condition checks in test cases to ensure case-insensitive comparisons. 2025-08-12 15:20:30 +03:00
shimon 24b7c95514 Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2025-08-11 22:07:11 +03:00
shimon 870125ed84 Refactor MongoDB condition checks in DatabasesCustomClientTest and UsersBase. Improved formatting for consistency and clarity in test assertions, particularly for MongoDB-related tests. 2025-08-11 22:07:05 +03:00
shimon e1475619ba Refactor debugging output across various files, including http.php, mock.php, and OAuth2 classes. Adjusted var_dump statements for improved traceability and consistency. Updated test assertions in ProjectsConsoleClientTest and UsersBase to enhance clarity and functionality. 2025-08-11 22:05:18 +03:00
Matej Bačo ba058a89a3 Merge branch '1.8.x' into feat-mongodb 2025-08-11 13:22:27 +02:00
shimon 236fab4658 Update composer configuration for minimum stability and prefer stable packages. Adjust utopia-php/database version alias and update phpunit and sebastian packages to latest versions. Add debugging output in http.php and uncomment OAuth2 conversion test in AccountCustomClientTest for improved functionality. 2025-08-11 11:56:38 +03:00
shimon 6cfd6d9ac9 Remove debugging var_dump statements from projects API and enhance error handling in XList class. Update test cases to comment out a specific OAuth2 conversion test for clarity. 2025-08-11 11:28:11 +03:00
shimon da706378ea Enhance debugging output in OAuth2 mock endpoints and update MongoDB version in tests. Added var_dump statements for better traceability during OAuth2 flows and adjusted test assertions for user search functionality. 2025-08-11 11:27:45 +03:00
shimon 40e36e23dd Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-08-10 18:16:03 +03:00
shimon 603980759a composer.lock 2025-08-06 15:49:05 +03:00
shimon d63a218bd8 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb
# Conflicts:
#	composer.json
#	composer.lock
2025-08-06 15:48:19 +03:00
shimon def3628205 feat: rename tables to grids and update documentation 2025-08-06 15:37:59 +03:00
shimon 779fb7bd20 Update composer dependencies and Docker Compose configuration for MongoDB integration. Change database alias in composer.json, update package versions in composer.lock, and adjust volume mappings in Docker Compose to include MongoDB source directories. Refactor application code to set tenant to null for database connections. 2025-08-06 15:07:04 +03:00
shimon ea1aef1d70 composer.lock 2025-08-03 18:40:45 +03:00
shimon b1cc203eba composer 2025-08-03 18:38:38 +03:00
shimon b3e1a3415a Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb
# Conflicts:
#	app/init/resources.php
#	composer.json
#	composer.lock
2025-08-03 18:33:27 +03:00
shimon d48248c9a2 Refactor Docker Compose and application code for MongoDB integration. Remove health check configuration and simplify service dependencies in Docker Compose. Update error logging in application to include context for MongoDB connection issues. 2025-08-03 18:13:16 +03:00
shimon eafca0c431 Add support for multiple database adapters in GitHub Actions workflow, including MARIADB and MONGODB. Set environment variables based on selected adapter for testing. 2025-07-31 18:22:42 +03:00
shimon a92bdfaed0 Update Docker Compose to clarify MongoDB readiness check comment, addressing implications of template substitution limitations. 2025-07-31 17:49:04 +03:00
shimon 46965135a4 Update Docker Compose and application configuration to support MongoDB as the primary database, replacing all instances of MariaDB. Introduce a new environment variable for database selection and adjust database schemes in the application initialization. 2025-07-31 17:17:08 +03:00
shimon 33c5ac83b2 Enhance Docker Compose configuration for MongoDB, add health checks, and update user creation logic in mongo-init.js. Improve database connection handling and fix conditional checks in application code. 2025-07-31 12:47:12 +03:00
shimon 57c08a41f1 Update environment configuration to support MongoDB, enhance Docker Compose for MongoDB integration, and improve database connection handling in the application. 2025-07-29 23:08:00 +03:00
shimon 36e7452f78 Update environment variables, Dockerfile version, and enhance MongoDB connection handling in the application. Add console document creation logic and improve code formatting for better readability. 2025-07-28 17:34:11 +03:00
shimon 469801b862 Update database configuration to use MongoDB, add MongoDB and Mongo Express services to Docker Compose, and adjust related code for MongoDB integration. 2025-07-27 20:38:49 +03:00
50150 changed files with 40246 additions and 887765 deletions
+5 -3
View File
@@ -10,6 +10,7 @@ _APP_CONSOLE_SESSION_ALERTS=enabled
_APP_CONSOLE_WHITELIST_IPS=
_APP_CONSOLE_COUNTRIES_DENYLIST=AQ
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
_APP_CONSOLE_SCHEMA=appwriteio
_APP_MIGRATION_HOST=appwrite
_APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io
@@ -38,9 +39,10 @@ _APP_REDIS_HOST=redis
_APP_REDIS_PORT=6379
_APP_REDIS_PASS=
_APP_REDIS_USER=
_APP_DB_ADAPTER=mariadb
_APP_DB_HOST=mariadb
_APP_DB_PORT=3306
COMPOSE_PROFILES=mongodb
_APP_DB_ADAPTER=mongodb
_APP_DB_HOST=mongodb
_APP_DB_PORT=27017
_APP_DB_SCHEMA=appwrite
_APP_DB_USER=user
_APP_DB_PASS=password
+135 -73
View File
@@ -98,16 +98,14 @@ jobs:
fail-on-cache-miss: true
- name: Load and Start Appwrite
timeout-minutes: 3
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 10
- name: Logs
run: docker compose logs appwrite
- name: Doctor
run: docker compose exec -T appwrite doctor
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Environment Variables
run: docker compose exec -T appwrite vars
@@ -115,8 +113,9 @@ jobs:
- name: Run Unit Tests
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 15
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/unit
@@ -144,11 +143,15 @@ jobs:
fail-on-cache-miss: true
- name: Load and Start Appwrite
timeout-minutes: 3
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 10
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Wait for Open Runtimes
timeout-minutes: 3
run: |
@@ -160,8 +163,9 @@ jobs:
- name: Run General Tests
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 15
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/e2e/General
@@ -173,10 +177,8 @@ jobs:
- name: Failure Logs
if: failure()
run: |
echo "=== Appwrite Worker Builds Logs ==="
docker compose logs appwrite-worker-builds
echo "=== OpenRuntimes Executor Logs ==="
docker compose logs openruntimes-executor
echo "=== Appwrite Logs ==="
docker compose logs
e2e_service_test:
name: E2E Service Test
@@ -190,15 +192,14 @@ jobs:
matrix:
db_adapter: [
MARIADB,
POSTGRESQL
POSTGRESQL,
MONGODB
]
service: [
Account,
Avatars,
Console,
Databases/Legacy,
Databases/TablesDB,
Databases,
Functions,
FunctionsSchedule,
GraphQL,
@@ -228,12 +229,37 @@ jobs:
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true
- name: Set DB Adapter environment
id: set-db-env
run: |
DB_ADAPTER_LOWER=$(echo "${{ matrix.db_adapter }}" | tr 'A-Z' 'a-z')
echo "COMPOSE_PROFILES=${DB_ADAPTER_LOWER}" >> $GITHUB_ENV
if [ "${{ matrix.db_adapter }}" = "MARIADB" ]; then
echo "_APP_DB_ADAPTER=mariadb" >> $GITHUB_ENV
echo "_APP_DB_HOST=mariadb" >> $GITHUB_ENV
echo "_APP_DB_PORT=3306" >> $GITHUB_ENV
elif [ "${{ matrix.db_adapter }}" = "MONGODB" ]; then
echo "_APP_DB_ADAPTER=mongodb" >> $GITHUB_ENV
echo "_APP_DB_HOST=mongodb" >> $GITHUB_ENV
echo "_APP_DB_PORT=27017" >> $GITHUB_ENV
elif [ "${{ matrix.db_adapter }}" = "POSTGRESQL" ]; then
echo "_APP_DB_ADAPTER=postgresql" >> $GITHUB_ENV
echo "_APP_DB_HOST=postgresql" >> $GITHUB_ENV
echo "_APP_DB_PORT=5432" >> $GITHUB_ENV
fi
- name: Load and Start Appwrite
timeout-minutes: 3
env:
_APP_BROWSER_HOST: http://invalid-browser/v1
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's|^_APP_BROWSER_HOST=.*|_APP_BROWSER_HOST=http://invalid-browser/v1|' .env
docker compose up -d
sleep 30
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Wait for Open Runtimes
timeout-minutes: 3
@@ -246,50 +272,38 @@ jobs:
- name: Run ${{ matrix.service }} tests with Project table mode
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 20
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/e2e/Services/${{ matrix.service }}
command: |
echo "Using project tables"
SERVICE_PATH="/usr/src/code/tests/e2e/Services/${{ matrix.service }}"
export _APP_DATABASE_SHARED_TABLES=
export _APP_DATABASE_SHARED_TABLES_V1=
# Set DB Adapter Specific ENV Vars using if-elif
if [ "${{ matrix.db_adapter }}" = "MARIADB" ]; then
export _APP_DB_ADAPTER=mariadb
export _APP_DB_HOST=mariadb
export _APP_DB_PORT=3306
export _APP_DB_SCHEMA=appwrite
elif [ "${{ matrix.db_adapter }}" = "POSTGRESQL" ]; then
export _APP_DB_ADAPTER=postgresql
export _APP_DB_HOST=postgresql
export _APP_DB_PORT=5432
export _APP_DB_SCHEMA=appwrite
else
echo "Unknown DB adapter: ${{ matrix.db_adapter }}"
exit 1
fi
# Services that rely on sequential test method execution (shared static state)
FUNCTIONAL_FLAG="--functional"
case "${{ matrix.service }}" in
Databases|Functions|Realtime) FUNCTIONAL_FLAG="" ;;
esac
echo "Running with paratest (parallel) for: ${{ matrix.service }} ${FUNCTIONAL_FLAG:+(functional)}"
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
-e _APP_DB_ADAPTER \
-e _APP_DB_HOST \
-e _APP_DB_PORT \
-e _APP_DB_SCHEMA \
-e _APP_DATABASE_SHARED_TABLES="" \
-e _APP_DATABASE_SHARED_TABLES_V1="" \
-e _APP_DB_ADAPTER="${{ env._APP_DB_ADAPTER }}" \
-e _APP_DB_HOST="${{ env._APP_DB_HOST }}" \
-e _APP_DB_PORT="${{ env._APP_DB_PORT }}" \
-e _APP_DB_SCHEMA=appwrite \
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group abuseEnabled,screenshots
appwrite vendor/bin/paratest --processes $(nproc) $FUNCTIONAL_FLAG "$SERVICE_PATH" --exclude-group abuseEnabled --exclude-group screenshots --exclude-group ciIgnore --log-junit tests/e2e/Services/${{ matrix.service }}/junit.xml
- name: Failure Logs
if: failure()
run: |
echo "=== Appwrite Worker Builds Logs ==="
docker compose logs appwrite-worker-builds
echo "=== OpenRuntimes Executor Logs ==="
docker compose logs openruntimes-executor
echo "=== Appwrite Logs ==="
docker compose logs
e2e_shared_mode_test:
name: E2E Shared Mode Service Test
@@ -307,8 +321,7 @@ jobs:
Account,
Avatars,
Console,
Databases/Legacy,
Databases/TablesDB,
Databases,
Functions,
FunctionsSchedule,
GraphQL,
@@ -344,10 +357,14 @@ jobs:
fail-on-cache-miss: true
- name: Load and Start Appwrite
timeout-minutes: 3
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 30
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Wait for Open Runtimes
timeout-minutes: 3
@@ -360,8 +377,9 @@ jobs:
- name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 20
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/e2e/Services/${{ matrix.service }}
@@ -375,12 +393,20 @@ jobs:
export _APP_DATABASE_SHARED_TABLES=database_db_main
export _APP_DATABASE_SHARED_TABLES_V1=
fi
SERVICE_PATH="/usr/src/code/tests/e2e/Services/${{ matrix.service }}"
# Services that rely on sequential test method execution (shared static state)
FUNCTIONAL_FLAG="--functional"
case "${{ matrix.service }}" in
Databases|Functions|Realtime) FUNCTIONAL_FLAG="" ;;
esac
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
-e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group abuseEnabled,screenshots
appwrite vendor/bin/paratest --processes $(nproc) $FUNCTIONAL_FLAG "$SERVICE_PATH" --exclude-group abuseEnabled --exclude-group screenshots --exclude-group ciIgnore --log-junit tests/e2e/Services/${{ matrix.service }}/junit.xml
- name: Failure Logs
if: failure()
@@ -409,17 +435,22 @@ jobs:
fail-on-cache-miss: true
- name: Load and Start Appwrite
timeout-minutes: 3
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
docker compose up -d
sleep 30
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Run Projects tests in dedicated table mode
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 15
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/e2e/Services/Projects
@@ -469,17 +500,22 @@ jobs:
fail-on-cache-miss: true
- name: Load and Start Appwrite
timeout-minutes: 3
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
docker compose up -d
sleep 30
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Run Projects tests in ${{ matrix.tables-mode }} table mode
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 15
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/e2e/Services/Projects
@@ -527,17 +563,30 @@ jobs:
fail-on-cache-miss: true
- name: Load and Start Appwrite
timeout-minutes: 3
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
docker compose up -d
sleep 30
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Wait for Open Runtimes
timeout-minutes: 3
run: |
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
echo "Waiting for Executor to come online"
sleep 1
done
- name: Run Site tests with browser connected in dedicated table mode
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 15
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/e2e/Services/Sites
@@ -588,17 +637,30 @@ jobs:
fail-on-cache-miss: true
- name: Load and Start Appwrite
timeout-minutes: 3
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
docker compose up -d
sleep 30
until docker compose exec -T appwrite doctor > /dev/null 2>&1; do
echo "Waiting for Appwrite to be ready..."
sleep 2
done
- name: Wait for Open Runtimes
timeout-minutes: 3
run: |
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
echo "Waiting for Executor to come online"
sleep 1
done
- name: Run Site tests with browser connected in ${{ matrix.tables-mode }} table mode
uses: itznotabug/php-retry@v3
with:
max_attempts: 3
timeout_minutes: 30
max_attempts: 2
retry_wait_seconds: 300
timeout_minutes: 15
job_id: ${{ job.check_run_id }}
github_token: ${{ secrets.GITHUB_TOKEN }}
test_dir: tests/e2e/Services/Sites
+3 -1
View File
@@ -18,4 +18,6 @@ dev/yasd_init.php
Makefile
appwrite.config.json
/.zed/
/app/config/specs/
/app/config/specs/
/docs/examples/
.phpunit.cache
+4 -1
View File
@@ -1,4 +1,4 @@
FROM composer:2.0 AS composer
FROM composer:2 AS composer
ARG TESTING=false
ENV TESTING=$TESTING
@@ -37,6 +37,9 @@ COPY ./app /usr/src/code/app
COPY ./public /usr/src/code/public
COPY ./bin /usr/local/bin
COPY ./src /usr/src/code/src
COPY ./dev /usr/src/code/dev
COPY ./mongo-init.js /usr/src/code/mongo-init.js
COPY ./mongo-entrypoint.sh /usr/src/code/mongo-entrypoint.sh
# Set Volumes
RUN mkdir -p /storage/uploads && \
+1 -21
View File
@@ -1617,13 +1617,6 @@ return [
],
],
'indexes' => [
[
'$id' => ID::custom('_fulltext_name'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
@@ -1853,13 +1846,6 @@ return [
'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,
@@ -2127,14 +2113,8 @@ return [
'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,
+5
View File
@@ -139,6 +139,11 @@ return [
'description' => 'There was an error processing your request. Please check the inputs and try again.',
'code' => 400,
],
Exception::GENERAL_FEATURE_UNSUPPORTED => [
'name' => Exception::GENERAL_FEATURE_UNSUPPORTED,
'description' => 'This feature is not supported with your current configuration.',
'code' => 400,
],
/** User Errors */
Exception::USER_COUNT_EXCEEDED => [
+1
View File
@@ -24,6 +24,7 @@ return [
System::getEnv('_APP_CONSOLE_DOMAIN', 'localhost'),
System::getEnv('_APP_MIGRATION_HOST'),
])),
'schemas' => \array_filter(\explode(',', System::getEnv('_APP_CONSOLE_SCHEMA', ''))),
'platformName' => APP_EMAIL_PLATFORM_NAME,
'logoUrl' => APP_EMAIL_LOGO_URL,
'accentColor' => APP_EMAIL_ACCENT_COLOR,
+26 -7
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '22.3.1',
'version' => '22.4.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -60,7 +60,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '21.1.0',
'version' => '21.4.0',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -79,7 +79,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '14.1.0',
'version' => '14.3.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -117,7 +117,7 @@ return [
'key' => 'android',
'name' => 'Android',
'namespace' => 'io.appwrite',
'version' => '12.1.0',
'version' => '12.2.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -140,7 +140,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.23.1',
'version' => '0.24.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -208,7 +208,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '0.3.0',
'version' => '22.4.0',
'url' => '',
'package' => '',
'enabled' => true,
@@ -227,7 +227,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '13.4.0',
'version' => '13.5.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -289,6 +289,25 @@ return [
'repoBranch' => 'main',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/agent-skills/CHANGELOG.md'),
],
[
'key' => 'cursor-plugin',
'name' => 'CursorPlugin',
'version' => '0.1.0',
'url' => 'https://github.com/appwrite/cursor-plugin.git',
'enabled' => true,
'beta' => false,
'dev' => false,
'hidden' => false,
'family' => APP_SDK_PLATFORM_CONSOLE,
'prism' => 'cursor-plugin',
'source' => \realpath(__DIR__ . '/../sdks/console-cursor-plugin'),
'gitUrl' => 'git@github.com:appwrite/cursor-plugin.git',
'gitRepoName' => 'cursor-plugin',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'repoBranch' => 'main',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/cursor-plugin/CHANGELOG.md'),
],
],
],
+1 -1
View File
@@ -160,7 +160,7 @@ return [
'name' => 'Teams',
'subtitle' => 'The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources',
'description' => '/docs/services/teams.md',
'controller' => 'api/teams.php',
'controller' => '', // Uses modules
'sdk' => true,
'docs' => true,
'docsUrl' => 'https://appwrite.io/docs/client/teams',
+69 -69
View File
@@ -358,15 +358,6 @@ return [
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_ADAPTER',
'description' => 'Which database adapter to use. Must be one of: mariadb, postgresql.',
'introduction' => '1.6.0',
'default' => 'mariadb',
'required' => true,
'question' => 'Choose your database (mariadb|postgresql)',
'filter' => ''
],
[
'name' => '_APP_TRUSTED_HEADERS',
'description' => 'This option allows you to set the list of trusted headers, the value is a commaseparated list of HTTP header names, evaluated left-to-right for the first valid IP. Header names are treated case-insensitively.',
@@ -378,6 +369,75 @@ return [
]
],
],
[
'category' => 'Database',
'description' => 'Appwrite uses a database for storing user and meta data. You can choose between MariaDB, MongoDB or PostgreSQL.',
'variables' => [
[
'name' => '_APP_DB_ADAPTER',
'description' => 'Which database to use. Must be one of: MariaDB, MongoDB, or PostgreSQL',
'introduction' => '1.9.0',
'default' => 'mongodb',
'required' => true,
'question' => 'Choose your database (mariadb|mongodb|postgresql)',
'filter' => ''
],
[
'name' => '_APP_DB_HOST',
'description' => 'Database server host name address. Default value is: \'mongodb\'.',
'introduction' => '',
'default' => 'mongodb',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_PORT',
'description' => 'Database server TCP port. Default value is: \'27017\'.',
'introduction' => '',
'default' => '27017',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_SCHEMA',
'description' => 'Database server database schema. Default value is: \'appwrite\'.',
'introduction' => '',
'default' => 'appwrite',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_USER',
'description' => 'Database server user name. Default value is: \'user\'.',
'introduction' => '',
'default' => 'user',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_PASS',
'description' => 'Database server user password. Default value is: \'password\'.',
'introduction' => '',
'default' => 'password',
'required' => false,
'question' => '',
'filter' => 'password'
],
[
'name' => '_APP_DB_ROOT_PASS',
'description' => 'Database server root password. Default value is: \'rootsecretpassword\'.',
'introduction' => '',
'default' => 'rootsecretpassword',
'required' => false,
'question' => '',
'filter' => 'password'
],
],
],
[
'category' => 'Redis',
'description' => 'Appwrite uses a Redis server for managing cache, queues and scheduled tasks. The Redis env vars are used to allow Appwrite server to connect to the Redis container.',
@@ -420,66 +480,6 @@ return [
],
],
],
[
'category' => 'MariaDB',
'description' => 'Appwrite is using a MariaDB server for managing persistent database data. The MariaDB env vars are used to allow Appwrite server to connect to the MariaDB container.',
'variables' => [
[
'name' => '_APP_DB_HOST',
'description' => 'MariaDB server host name address. Default value is: \'mariadb\'.',
'introduction' => '',
'default' => 'mariadb',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_PORT',
'description' => 'MariaDB server TCP port. Default value is: \'3306\'.',
'introduction' => '',
'default' => '3306',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_SCHEMA',
'description' => 'MariaDB server database schema. Default value is: \'appwrite\'.',
'introduction' => '',
'default' => 'appwrite',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_USER',
'description' => 'MariaDB server user name. Default value is: \'user\'.',
'introduction' => '',
'default' => 'user',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DB_PASS',
'description' => 'MariaDB server user password. Default value is: \'password\'.',
'introduction' => '',
'default' => 'password',
'required' => false,
'question' => '',
'filter' => 'password'
],
[
'name' => '_APP_DB_ROOT_PASS',
'description' => 'MariaDB server root password. Default value is: \'rootsecretpassword\'.',
'introduction' => '',
'default' => 'rootsecretpassword',
'required' => false,
'question' => '',
'filter' => 'password'
],
],
],
[
'category' => 'InfluxDB',
'description' => 'Deprecated since 1.4.8.',
+37 -35
View File
@@ -367,7 +367,7 @@ Http::post('/v1/account')
contentType: ContentType::JSON
))
->label('abuse-limit', 10)
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be between 8 and 256 chars.', false, ['project', 'passwordsDictionary'])
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@@ -728,7 +728,7 @@ Http::get('/v1/account/sessions/:sessionId')
],
contentType: ContentType::JSON
))
->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to get the current device session.')
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'current\' to get the current device session.', false, ['dbForProject'])
->inject('response')
->inject('user')
->inject('locale')
@@ -780,7 +780,7 @@ Http::delete('/v1/account/sessions/:sessionId')
contentType: ContentType::NONE
))
->label('abuse-limit', 100)
->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to delete the current device session.')
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'current\' to delete the current device session.', false, ['dbForProject'])
->inject('requestTimestamp')
->inject('request')
->inject('response')
@@ -868,7 +868,7 @@ Http::patch('/v1/account/sessions/:sessionId')
contentType: ContentType::JSON
))
->label('abuse-limit', 10)
->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to update the current device session.')
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'current\' to update the current device session.', false, ['dbForProject'])
->inject('response')
->inject('user')
->inject('dbForProject')
@@ -902,13 +902,13 @@ Http::patch('/v1/account/sessions/:sessionId')
// Refresh OAuth access token
$provider = $session->getAttribute('provider', '');
$refreshToken = $session->getAttribute('providerRefreshToken', '');
$oAuthProviders = Config::getParam('oAuthProviders');
$className = $oAuthProviders[$provider]['class'];
if (!\class_exists($className)) {
$oAuthProviders = Config::getParam('oAuthProviders') ?? [];
$className = $oAuthProviders[$provider]['class'] ?? null;
if (!empty($provider) && ($className === null || !\class_exists($className))) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
if (!empty($provider) && \class_exists($className)) {
if (!empty($provider) && $className !== null && \class_exists($className)) {
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
@@ -1262,7 +1262,7 @@ Http::post('/v1/account/sessions/token')
->label('abuse-limit', 10)
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
->label('abuse-reset', [201])
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Secret of a token generated by login methods. For example, the `createMagicURLToken` or `createPhoneToken` methods.')
->inject('request')
->inject('response')
@@ -1340,9 +1340,9 @@ Http::get('/v1/account/sessions/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.');
}
$oAuthProviders = Config::getParam('oAuthProviders');
$className = $oAuthProviders[$provider]['class'];
if (!\class_exists($className)) {
$oAuthProviders = Config::getParam('oAuthProviders') ?? [];
$className = $oAuthProviders[$provider]['class'] ?? null;
if ($className === null || !\class_exists($className)) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
@@ -1485,19 +1485,20 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
$oAuthProviders = Config::getParam('oAuthProviders');
$className = $oAuthProviders[$provider]['class'];
if (!\class_exists($className)) {
$oAuthProviders = Config::getParam('oAuthProviders') ?? [];
$className = $oAuthProviders[$provider]['class'] ?? null;
if ($className === null || !\class_exists($className)) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$providers = Config::getParam('oAuthProviders');
$providers = Config::getParam('oAuthProviders') ?? [];
$providerName = $providers[$provider]['name'] ?? '';
/** @var Appwrite\Auth\OAuth2 $oauth2 */
@@ -1547,6 +1548,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
if (!empty($state['failure'])) {
$failure = URLParser::parse($state['failure']);
}
$failureRedirect = (function (string $type, ?string $message = null, ?int $code = null) use ($failure, $response) {
$exception = new Exception($type, $message, $code);
if (!empty($failure)) {
@@ -1592,7 +1594,9 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
$accessToken = $oauth2->getAccessToken($code);
$refreshToken = $oauth2->getRefreshToken($code);
$accessTokenExpiry = $oauth2->getAccessTokenExpiry($code);
} catch (OAuth2Exception $ex) {
$failureRedirect(
$ex->getType(),
'Failed to obtain access token. The ' . $providerName . ' OAuth2 provider returned an error: ' . $ex->getMessage(),
@@ -2031,9 +2035,9 @@ Http::get('/v1/account/tokens/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.');
}
$oAuthProviders = Config::getParam('oAuthProviders');
$className = $oAuthProviders[$provider]['class'];
if (!\class_exists($className)) {
$oAuthProviders = Config::getParam('oAuthProviders') ?? [];
$className = $oAuthProviders[$provider]['class'] ?? null;
if ($className === null || !\class_exists($className)) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
@@ -2092,7 +2096,7 @@ Http::post('/v1/account/tokens/magic-url')
))
->label('abuse-limit', 60)
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
->param('userId', '', new CustomId(), 'Unique 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. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.')
->param('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Unique 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. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('url', '', fn ($redirectValidator) => $redirectValidator, 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['redirectValidator'])
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
@@ -2111,7 +2115,7 @@ Http::post('/v1/account/tokens/magic-url')
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
$url = htmlentities($url);
if ($phrase === true) {
$phrase = Phrase::generate();
@@ -2372,7 +2376,7 @@ Http::post('/v1/account/tokens/email')
))
->label('abuse-limit', 10)
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
->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. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.')
->param('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
->inject('request')
@@ -2679,7 +2683,7 @@ Http::put('/v1/account/sessions/magic-url')
->label('abuse-limit', 10)
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
->label('abuse-reset', [201])
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('request')
->inject('response')
@@ -2728,7 +2732,7 @@ Http::put('/v1/account/sessions/phone')
))
->label('abuse-limit', 10)
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('request')
->inject('response')
@@ -2771,7 +2775,7 @@ Http::post('/v1/account/tokens/phone')
))
->label('abuse-limit', 10)
->label('abuse-key', ['url:{url},phone:{param-phone}', 'url:{url},ip:{ip}'])
->param('userId', '', new CustomId(), 'Unique 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. If the phone number has never been used, a new account is created using the provided userId. Otherwise, if the phone number is already attached to an account, the user ID is ignored.')
->param('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Unique 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. If the phone number has never been used, a new account is created using the provided userId. Otherwise, if the phone number is already attached to an account, the user ID is ignored.', false, ['dbForProject'])
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
->inject('request')
->inject('response')
@@ -3573,7 +3577,6 @@ Http::post('/v1/account/recovery')
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
$url = htmlentities($url);
$email = \strtolower($email);
$profile = $dbForProject->findOne('users', [
@@ -3749,7 +3752,7 @@ Http::put('/v1/account/recovery')
))
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid reset token.')
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be between 8 and 256 chars.', false, ['project', 'passwordsDictionary'])
->inject('response')
@@ -3889,7 +3892,6 @@ Http::post('/v1/account/verifications/email')
throw new Exception(Exception::USER_EMAIL_NOT_FOUND);
}
$url = htmlentities($url);
if ($user->getAttribute('emailVerification')) {
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
}
@@ -4100,7 +4102,7 @@ Http::put('/v1/account/verifications/email')
])
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
@@ -4316,7 +4318,7 @@ Http::put('/v1/account/verifications/phone')
))
->label('abuse-limit', 10)
->label('abuse-key', 'userId:{param-userId}')
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
@@ -4381,9 +4383,9 @@ Http::post('/v1/account/targets/push')
],
contentType: ContentType::JSON
))
->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('targetId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), '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, ['dbForProject'])
->inject('queueForEvents')
->inject('user')
->inject('request')
@@ -4465,7 +4467,7 @@ Http::put('/v1/account/targets/:targetId/push')
],
contentType: ContentType::JSON
))
->param('targetId', '', new UID(), 'Target ID.')
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
->inject('queueForEvents')
->inject('user')
@@ -4531,7 +4533,7 @@ Http::delete('/v1/account/targets/:targetId/push')
],
contentType: ContentType::NONE
))
->param('targetId', '', new UID(), 'Target ID.')
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('queueForDeletes')
->inject('user')
@@ -4653,7 +4655,7 @@ Http::delete('/v1/account/identities/:identityId')
],
contentType: ContentType::NONE
))
->param('identityId', '', new UID(), 'Identity ID.')
->param('identityId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Identity ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
+72 -72
View File
@@ -78,7 +78,7 @@ Http::post('/v1/messaging/providers/mailgun')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('apiKey', '', new Text(0), 'Mailgun API Key.', true)
->param('domain', '', new Text(0), 'Mailgun Domain.', true)
@@ -172,7 +172,7 @@ Http::post('/v1/messaging/providers/sendgrid')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('apiKey', '', new Text(0), 'Sendgrid API key.', true)
->param('fromName', '', new Text(128, 0), 'Sender Name.', true)
@@ -254,7 +254,7 @@ Http::post('/v1/messaging/providers/resend')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('apiKey', '', new Text(0), 'Resend API key.', true)
->param('fromName', '', new Text(128, 0), 'Sender Name.', true)
@@ -356,7 +356,7 @@ Http::post('/v1/messaging/providers/smtp')
]
)
])
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.')
->param('port', 587, new Range(1, 65535), 'The default SMTP server port.', true)
@@ -451,7 +451,7 @@ Http::post('/v1/messaging/providers/msg91')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('templateId', '', new Text(0), 'Msg91 template ID', true)
->param('senderId', '', new Text(0), 'Msg91 sender ID.', true)
@@ -534,7 +534,7 @@ Http::post('/v1/messaging/providers/telesign')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('customerId', '', new Text(0), 'Telesign customer ID.', true)
@@ -618,7 +618,7 @@ Http::post('/v1/messaging/providers/textmagic')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('username', '', new Text(0), 'Textmagic username.', true)
@@ -702,7 +702,7 @@ Http::post('/v1/messaging/providers/twilio')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('accountSid', '', new Text(0), 'Twilio account secret ID.', true)
@@ -786,7 +786,7 @@ Http::post('/v1/messaging/providers/vonage')
)
]
))
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('apiKey', '', new Text(0), 'Vonage API key.', true)
@@ -890,7 +890,7 @@ Http::post('/v1/messaging/providers/fcm')
]
)
])
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
@@ -980,7 +980,7 @@ Http::post('/v1/messaging/providers/apns')
]
)
])
->param('providerId', '', new CustomId(), 'Provider 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Provider 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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('authKey', '', new Text(0), 'APNS authentication key.', true)
->param('authKeyId', '', new Text(0), 'APNS authentication key ID.', true)
@@ -1135,7 +1135,7 @@ Http::get('/v1/messaging/providers/:providerId/logs')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -1231,7 +1231,7 @@ Http::get('/v1/messaging/providers/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, Database $dbForProject, Response $response) {
@@ -1265,7 +1265,7 @@ Http::patch('/v1/messaging/providers/mailgun/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('apiKey', '', new Text(0), 'Mailgun API Key.', true)
->param('domain', '', new Text(0), 'Mailgun Domain.', true)
@@ -1378,7 +1378,7 @@ Http::patch('/v1/messaging/providers/sendgrid/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Sendgrid API key.', true)
@@ -1476,7 +1476,7 @@ Http::patch('/v1/messaging/providers/resend/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Resend API key.', true)
@@ -1594,7 +1594,7 @@ Http::patch('/v1/messaging/providers/smtp/:providerId')
]
)
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.', true)
->param('port', null, new Nullable(new Range(1, 65535)), 'SMTP port.', true)
@@ -1723,7 +1723,7 @@ Http::patch('/v1/messaging/providers/msg91/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('templateId', '', new Text(0), 'Msg91 template ID.', true)
@@ -1810,7 +1810,7 @@ Http::patch('/v1/messaging/providers/telesign/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('customerId', '', new Text(0), 'Telesign customer ID.', true)
@@ -1899,7 +1899,7 @@ Http::patch('/v1/messaging/providers/textmagic/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('username', '', new Text(0), 'Textmagic username.', true)
@@ -1988,7 +1988,7 @@ Http::patch('/v1/messaging/providers/twilio/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('accountSid', '', new Text(0), 'Twilio account secret ID.', true)
@@ -2077,7 +2077,7 @@ Http::patch('/v1/messaging/providers/vonage/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Vonage API key.', true)
@@ -2186,7 +2186,7 @@ Http::patch('/v1/messaging/providers/fcm/:providerId')
]
)
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true)
@@ -2282,7 +2282,7 @@ Http::patch('/v1/messaging/providers/apns/:providerId')
]
)
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('authKey', '', new Text(0), 'APNS authentication key.', true)
@@ -2385,7 +2385,7 @@ Http::delete('/v1/messaging/providers/:providerId')
],
contentType: ContentType::NONE
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -2427,7 +2427,7 @@ Http::post('/v1/messaging/topics')
)
]
))
->param('topicId', '', new CustomId(), 'Topic ID. Choose a custom Topic ID or a new Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. Choose a custom Topic ID or a new Topic ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Topic Name.')
->param('subscribe', [Role::users()], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->inject('queueForEvents')
@@ -2539,7 +2539,7 @@ Http::get('/v1/messaging/topics/:topicId/logs')
)
]
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -2636,7 +2636,7 @@ Http::get('/v1/messaging/topics/:topicId')
)
]
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('response')
->action(function (string $topicId, Database $dbForProject, Response $response) {
@@ -2671,7 +2671,7 @@ Http::patch('/v1/messaging/topics/:topicId')
)
]
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->param('name', null, new Nullable(new Text(128)), 'Topic Name.', true)
->param('subscribe', null, new Nullable(new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->inject('queueForEvents')
@@ -2723,7 +2723,7 @@ Http::delete('/v1/messaging/topics/:topicId')
],
contentType: ContentType::NONE
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('queueForDeletes')
@@ -2770,9 +2770,9 @@ Http::post('/v1/messaging/topics/:topicId/subscribers')
)
]
))
->param('subscriberId', '', new CustomId(), 'Subscriber ID. Choose a custom Subscriber ID or a new Subscriber ID.')
->param('topicId', '', new UID(), 'Topic ID. The topic ID to subscribe to.')
->param('targetId', '', new UID(), 'Target ID. The target ID to link to the specified Topic ID.')
->param('subscriberId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID. Choose a custom Subscriber ID or a new Subscriber ID.', false, ['dbForProject'])
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID to subscribe to.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID. The target ID to link to the specified Topic ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('authorization')
@@ -2868,8 +2868,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers')
)
]
))
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('queries', [], new Subscribers(), '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(', ', Providers::ALLOWED_ATTRIBUTES), true)
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID subscribed to.', false, ['dbForProject'])
->param('queries', [], new Subscribers(), '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(', ', Subscribers::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('dbForProject')
@@ -2954,7 +2954,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs')
)
]
))
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->param('subscriberId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -3051,8 +3051,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
)
]
))
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID subscribed to.', false, ['dbForProject'])
->param('subscriberId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('authorization')
->inject('response')
@@ -3102,8 +3102,8 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
],
contentType: ContentType::NONE
))
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID subscribed to.', false, ['dbForProject'])
->param('subscriberId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('authorization')
@@ -3169,14 +3169,14 @@ Http::post('/v1/messaging/messages/email')
)
]
))
->param('messageId', '', new CustomId(), 'Message 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('messageId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Message 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.', false, ['dbForProject'])
->param('subject', '', new Text(998), 'Email Subject.')
->param('content', '', new Text(64230), 'Email Content.')
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('cc', [], new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true)
->param('bcc', [], new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true)
->param('topics', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of User IDs.', true, ['dbForProject'])
->param('targets', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Targets IDs.', true, ['dbForProject'])
->param('cc', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'Array of target IDs to be added as CC.', true, ['dbForProject'])
->param('bcc', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'Array of target IDs to be added as BCC.', true, ['dbForProject'])
->param('attachments', [], new ArrayList(new CompoundUID()), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->param('draft', false, new Boolean(), 'Is message a draft', true)
->param('html', false, new Boolean(), 'Is content of type HTML', true)
@@ -3348,11 +3348,11 @@ Http::post('/v1/messaging/messages/sms')
]
)
])
->param('messageId', '', new CustomId(), 'Message 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('messageId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Message 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.', false, ['dbForProject'])
->param('content', '', new Text(64230), 'SMS Content.')
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('topics', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of User IDs.', true, ['dbForProject'])
->param('targets', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Targets IDs.', true, ['dbForProject'])
->param('draft', false, new Boolean(), 'Is message a draft', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->inject('queueForEvents')
@@ -3471,12 +3471,12 @@ Http::post('/v1/messaging/messages/push')
)
]
))
->param('messageId', '', new CustomId(), 'Message 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('messageId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Message 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.', false, ['dbForProject'])
->param('title', '', new Text(256), 'Title for push notification.', true)
->param('body', '', new Text(64230), 'Body for push notification.', true)
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('topics', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of User IDs.', true, ['dbForProject'])
->param('targets', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Targets IDs.', true, ['dbForProject'])
->param('data', null, new Nullable(new JSON()), 'Additional key-value pair data for push notification.', true)
->param('action', '', new Text(256), 'Action for push notification.', true)
->param('image', '', new CompoundUID(), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
@@ -3752,7 +3752,7 @@ Http::get('/v1/messaging/messages/:messageId/logs')
)
],
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -3849,7 +3849,7 @@ Http::get('/v1/messaging/messages/:messageId/targets')
)
],
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->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(', ', Targets::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -3927,7 +3927,7 @@ Http::get('/v1/messaging/messages/:messageId')
)
]
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('response')
->action(function (string $messageId, Database $dbForProject, Response $response) {
@@ -3961,16 +3961,16 @@ Http::patch('/v1/messaging/messages/email/:messageId')
)
]
))
->param('messageId', '', new UID(), 'Message ID.')
->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true)
->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true)
->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true)
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('topics', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of User IDs.', true, ['dbForProject'])
->param('targets', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Targets IDs.', true, ['dbForProject'])
->param('subject', null, new Nullable(new Text(998)), 'Email Subject.', true)
->param('content', null, new Nullable(new Text(64230)), 'Email Content.', true)
->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true)
->param('html', null, new Nullable(new Boolean()), 'Is content of type HTML', true)
->param('cc', null, new Nullable(new ArrayList(new UID())), 'Array of target IDs to be added as CC.', true)
->param('bcc', null, new Nullable(new ArrayList(new UID())), 'Array of target IDs to be added as BCC.', true)
->param('cc', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'Array of target IDs to be added as CC.', true, ['dbForProject'])
->param('bcc', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'Array of target IDs to be added as BCC.', true, ['dbForProject'])
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('attachments', null, new Nullable(new ArrayList(new CompoundUID())), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->inject('queueForEvents')
@@ -4188,10 +4188,10 @@ Http::patch('/v1/messaging/messages/sms/:messageId')
]
)
])
->param('messageId', '', new UID(), 'Message ID.')
->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true)
->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true)
->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true)
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('topics', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of User IDs.', true, ['dbForProject'])
->param('targets', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Targets IDs.', true, ['dbForProject'])
->param('content', null, new Nullable(new Text(64230)), 'Email Content.', true)
->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
@@ -4350,10 +4350,10 @@ Http::patch('/v1/messaging/messages/push/:messageId')
)
]
))
->param('messageId', '', new UID(), 'Message ID.')
->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true)
->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true)
->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true)
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('topics', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of User IDs.', true, ['dbForProject'])
->param('targets', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Targets IDs.', true, ['dbForProject'])
->param('title', null, new Nullable(new Text(256)), 'Title for push notification.', true)
->param('body', null, new Nullable(new Text(64230)), 'Body for push notification.', true)
->param('data', null, new Nullable(new JSON()), 'Additional Data for push notification.', true)
@@ -4612,7 +4612,7 @@ Http::delete('/v1/messaging/messages/:messageId')
],
contentType: ContentType::NONE
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('dbForPlatform')
->inject('queueForEvents')
+6 -6
View File
@@ -64,7 +64,7 @@ Http::post('/v1/migrations/appwrite')
))
->param('resources', [], new ArrayList(new WhiteList(Appwrite::getSupportedResources())), 'List of resources to migrate')
->param('endpoint', '', new URL(), 'Source Appwrite endpoint')
->param('projectId', '', new UID(), 'Source Project ID')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Source Project ID', false, ['dbForProject'])
->param('apiKey', '', new Text(512), 'Source API Key')
->inject('response')
->inject('dbForProject')
@@ -335,8 +335,8 @@ Http::post('/v1/migrations/csv/imports')
)
]
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('bucketId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).', false, ['dbForProject'])
->param('fileId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'File ID.', false, ['dbForProject'])
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.')
->param('internalFile', false, new Boolean(), 'Is the file stored in an internal bucket?', true)
->inject('response')
@@ -678,7 +678,7 @@ Http::get('/v1/migrations/:migrationId')
)
]
))
->param('migrationId', '', new UID(), 'Migration unique ID.')
->param('migrationId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Migration unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $migrationId, Response $response, Database $dbForProject) {
@@ -911,7 +911,7 @@ Http::patch('/v1/migrations/:migrationId')
)
]
))
->param('migrationId', '', new UID(), 'Migration unique ID.')
->param('migrationId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Migration unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('project')
@@ -965,7 +965,7 @@ Http::delete('/v1/migrations/:migrationId')
],
contentType: ContentType::NONE
))
->param('migrationId', '', new UID(), 'Migration ID.')
->param('migrationId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Migration ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
+3 -3
View File
@@ -496,7 +496,7 @@ Http::get('/v1/project/variables/:variableId')
)
]
))
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('response')
->inject('project')
->inject('dbForProject')
@@ -526,7 +526,7 @@ Http::put('/v1/project/variables/:variableId')
)
]
))
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true)
->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
@@ -585,7 +585,7 @@ Http::delete('/v1/project/variables/:variableId')
],
contentType: ContentType::NONE
))
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('project')
->inject('response')
->inject('dbForProject')
+54 -55
View File
@@ -71,7 +71,7 @@ Http::get('/v1/projects/:projectId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, Response $response, Database $dbForPlatform) {
@@ -102,7 +102,7 @@ Http::patch('/v1/projects/:projectId/service')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])), true), 'Service name.')
->param('status', null, new Boolean(), 'Service status.')
->inject('response')
@@ -140,7 +140,7 @@ Http::patch('/v1/projects/:projectId/service/all')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('status', null, new Boolean(), 'Service status.')
->inject('response')
->inject('dbForPlatform')
@@ -201,7 +201,7 @@ Http::patch('/v1/projects/:projectId/api')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('api', '', new WhiteList(array_keys(Config::getParam('apis')), true), 'API name.')
->param('status', null, new Boolean(), 'API status.')
->inject('response')
@@ -259,7 +259,7 @@ Http::patch('/v1/projects/:projectId/api/all')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('status', null, new Boolean(), 'API status.')
->inject('response')
->inject('dbForPlatform')
@@ -300,7 +300,7 @@ Http::patch('/v1/projects/:projectId/oauth2')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'Provider Name')
->param('appId', null, new Nullable(new Text(256)), 'Provider app ID. Max length: 256 chars.', true)
->param('secret', null, new Nullable(new text(512)), 'Provider secret key. Max length: 512 chars.', true)
@@ -351,7 +351,7 @@ Http::patch('/v1/projects/:projectId/auth/session-alerts')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('alerts', false, new Boolean(true), 'Set to true to enable session emails.')
->inject('response')
->inject('dbForPlatform')
@@ -389,7 +389,7 @@ Http::patch('/v1/projects/:projectId/auth/memberships-privacy')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('userName', true, new Boolean(true), 'Set to true to show userName to members of a team.')
->param('userEmail', true, new Boolean(true), 'Set to true to show email to members of a team.')
->param('mfa', true, new Boolean(true), 'Set to true to show mfa to members of a team.')
@@ -431,7 +431,7 @@ Http::patch('/v1/projects/:projectId/auth/limit')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('limit', false, new Range(0, APP_LIMIT_USERS), 'Set the max number of users allowed in this project. Use 0 for unlimited.')
->inject('response')
->inject('dbForPlatform')
@@ -469,7 +469,7 @@ Http::patch('/v1/projects/:projectId/auth/duration')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('duration', 31536000, new Range(0, 31536000), 'Project session length in seconds. Max length: 31536000 seconds.')
->inject('response')
->inject('dbForPlatform')
@@ -507,7 +507,7 @@ Http::patch('/v1/projects/:projectId/auth/:method')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: ' . implode(',', \array_keys(Config::getParam('auth'))), false)
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
->inject('response')
@@ -548,7 +548,7 @@ Http::patch('/v1/projects/:projectId/auth/password-history')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('limit', 0, new Range(0, APP_LIMIT_USER_PASSWORD_HISTORY), 'Set the max number of passwords to store in user history. User can\'t choose a new password that is already stored in the password history list. Max number of passwords allowed in history is' . APP_LIMIT_USER_PASSWORD_HISTORY . '. Default value is 0')
->inject('response')
->inject('dbForPlatform')
@@ -586,7 +586,7 @@ Http::patch('/v1/projects/:projectId/auth/password-dictionary')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('enabled', false, new Boolean(false), 'Set whether or not to enable checking user\'s password against most commonly used passwords. Default is false.')
->inject('response')
->inject('dbForPlatform')
@@ -624,7 +624,7 @@ Http::patch('/v1/projects/:projectId/auth/personal-data')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('enabled', false, new Boolean(false), 'Set whether or not to check a password for similarity with personal data. Default is false.')
->inject('response')
->inject('dbForPlatform')
@@ -662,7 +662,7 @@ Http::patch('/v1/projects/:projectId/auth/max-sessions')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('limit', false, new Range(1, APP_LIMIT_USER_SESSIONS_MAX), 'Set the max number of users allowed in this project. Value allowed is between 1-' . APP_LIMIT_USER_SESSIONS_MAX . '. Default is ' . APP_LIMIT_USER_SESSIONS_DEFAULT)
->inject('response')
->inject('dbForPlatform')
@@ -700,7 +700,7 @@ Http::patch('/v1/projects/:projectId/auth/mock-numbers')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('numbers', '', new ArrayList(new MockNumber(), 10), 'An array of mock numbers and their corresponding verification codes (OTPs). Each number should be a valid E.164 formatted phone number. Maximum of 10 numbers are allowed.')
->inject('response')
->inject('dbForPlatform')
@@ -749,7 +749,7 @@ Http::delete('/v1/projects/:projectId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('user')
->inject('dbForPlatform')
@@ -792,7 +792,7 @@ Http::post('/v1/projects/:projectId/webhooks')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true)
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
@@ -857,7 +857,7 @@ Http::get('/v1/projects/:projectId/webhooks')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForPlatform')
@@ -897,8 +897,8 @@ Http::get('/v1/projects/:projectId/webhooks/:webhookId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('webhookId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForPlatform) {
@@ -938,8 +938,8 @@ Http::put('/v1/projects/:projectId/webhooks/:webhookId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('webhookId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForPlatform'])
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true)
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
@@ -1004,8 +1004,8 @@ Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('webhookId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForPlatform) {
@@ -1051,8 +1051,8 @@ Http::delete('/v1/projects/:projectId/webhooks/:webhookId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('webhookId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForPlatform) {
@@ -1098,10 +1098,9 @@ Http::post('/v1/projects/:projectId/keys')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
// TODO: When migrating to Platform API, mark keyId required for consistency
->param('keyId', 'unique()', new CustomId(), 'Key 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.', true)
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('keyId', 'unique()', fn (Database $dbForPlatform) => new CustomId($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key 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.', true, ['dbForPlatform'])->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('expire', null, new Nullable(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')
@@ -1163,7 +1162,7 @@ Http::get('/v1/projects/:projectId/keys')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('queries', [], new Keys(), '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(', ', Keys::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -1236,8 +1235,8 @@ Http::get('/v1/projects/:projectId/keys/:keyId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('keyId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForPlatform) {
@@ -1278,8 +1277,8 @@ Http::put('/v1/projects/:projectId/keys/:keyId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('keyId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForPlatform'])
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('expire', null, new Nullable(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)
@@ -1333,8 +1332,8 @@ Http::delete('/v1/projects/:projectId/keys/:keyId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('keyId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForPlatform) {
@@ -1381,7 +1380,7 @@ Http::post('/v1/projects/:projectId/jwts')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
@@ -1425,7 +1424,7 @@ Http::post('/v1/projects/:projectId/platforms')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param(
'type',
null,
@@ -1503,7 +1502,7 @@ Http::get('/v1/projects/:projectId/platforms')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForPlatform')
@@ -1543,8 +1542,8 @@ Http::get('/v1/projects/:projectId/platforms/:platformId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', '', new UID(), 'Platform unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('platformId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForPlatform) {
@@ -1584,8 +1583,8 @@ Http::put('/v1/projects/:projectId/platforms/:platformId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', '', new UID(), 'Platform unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('platformId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForPlatform'])
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
->param('key', '', new Text(256), 'Package name for android or bundle ID for iOS. Max length: 256 chars.', true)
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
@@ -1641,8 +1640,8 @@ Http::delete('/v1/projects/:projectId/platforms/:platformId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', '', new UID(), 'Platform unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('platformId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForPlatform'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForPlatform) {
@@ -1708,7 +1707,7 @@ Http::patch('/v1/projects/:projectId/smtp')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('enabled', false, new Boolean(), 'Enable custom SMTP service')
->param('senderName', '', new Text(255, 0), 'Name of the email sender', true)
->param('senderEmail', '', new Email(), 'Email of the sender', true)
@@ -1826,7 +1825,7 @@ Http::post('/v1/projects/:projectId/smtp/tests')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('emails', [], new ArrayList(new Email(), 10), 'Array of emails to send test email to. Maximum of 10 emails are allowed.')
->param('senderName', System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender')
->param('senderEmail', System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), new Email(), 'Email of the sender')
@@ -1921,7 +1920,7 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? [], true), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -1969,7 +1968,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -2088,7 +2087,7 @@ Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? [], true), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('message', '', new Text(0), 'Template message')
@@ -2135,7 +2134,7 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('subject', '', new Text(255), 'Email Subject')
@@ -2214,7 +2213,7 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
contentType: ContentType::JSON
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? [], true), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -2265,7 +2264,7 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale')
],
contentType: ContentType::JSON
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -2317,7 +2316,7 @@ Http::patch('/v1/projects/:projectId/auth/session-invalidation')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('enabled', false, new Boolean(), 'Update authentication session invalidation status. Use this endpoint to enable or disable session invalidation on password change')
->inject('response')
->inject('dbForPlatform')
File diff suppressed because it is too large Load Diff
+48 -48
View File
@@ -246,7 +246,7 @@ Http::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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', null, new Nullable(new EmailValidator()), 'User email.', true)
->param('phone', null, new Nullable(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'])
@@ -283,7 +283,7 @@ Http::post('/v1/users/bcrypt')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Bcrypt.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@@ -321,7 +321,7 @@ Http::post('/v1/users/md5')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using MD5.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@@ -358,7 +358,7 @@ Http::post('/v1/users/argon2')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Argon2.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@@ -395,7 +395,7 @@ Http::post('/v1/users/sha')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using SHA.')
->param('passwordVersion', '', new WhiteList(['sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512']), "Optional SHA version used to hash password. Allowed values are: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512'", true)
@@ -436,7 +436,7 @@ Http::post('/v1/users/phpass')
)
]
))
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or pass the string `ID.unique()`to auto generate it. 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', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'User ID. Choose a custom ID or pass the string `ID.unique()`to auto generate it. 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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using PHPass.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@@ -473,7 +473,7 @@ Http::post('/v1/users/scrypt')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Scrypt.')
->param('passwordSalt', '', new Text(128), 'Optional salt used to hash password.')
@@ -521,7 +521,7 @@ Http::post('/v1/users/scrypt-modified')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Scrypt Modified.')
->param('passwordSalt', '', new Text(128), 'Salt used to hash password.')
@@ -566,11 +566,11 @@ Http::post('/v1/users/:userId/targets')
)
]
))
->param('targetId', '', new CustomId(), 'Target ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->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('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), '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, ['dbForProject'])
->param('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
->inject('queueForEvents')
->inject('response')
@@ -731,7 +731,7 @@ Http::get('/v1/users/:userId')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -762,7 +762,7 @@ Http::get('/v1/users/:userId/prefs')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -795,8 +795,8 @@ Http::get('/v1/users/:userId/targets/:targetId')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $targetId, Response $response, Database $dbForProject) {
@@ -833,7 +833,7 @@ Http::get('/v1/users/:userId/sessions')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForProject')
@@ -877,7 +877,7 @@ Http::get('/v1/users/:userId/memberships')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('queries', [], new Memberships(), '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(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
@@ -932,7 +932,7 @@ Http::get('/v1/users/:userId/logs')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -1019,7 +1019,7 @@ Http::get('/v1/users/:userId/targets')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->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(', ', Targets::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -1147,7 +1147,7 @@ Http::patch('/v1/users/:userId/status')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`.')
->inject('response')
->inject('dbForProject')
@@ -1188,7 +1188,7 @@ Http::put('/v1/users/:userId/labels')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('labels', [], new ArrayList(new Text(36, allowList: [...Text::NUMBERS, ...Text::ALPHABET_UPPER, ...Text::ALPHABET_LOWER]), APP_LIMIT_ARRAY_LABELS_SIZE), 'Array of user labels. Replaces the previous labels. Maximum of ' . APP_LIMIT_ARRAY_LABELS_SIZE . ' labels are allowed, each up to 36 alphanumeric characters long.')
->inject('response')
->inject('dbForProject')
@@ -1231,7 +1231,7 @@ Http::patch('/v1/users/:userId/verification/phone')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('phoneVerification', false, new Boolean(), 'User phone verification status.')
->inject('response')
->inject('dbForProject')
@@ -1273,7 +1273,7 @@ Http::patch('/v1/users/:userId/name')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('name', '', new Text(128, 0), 'User name. Max length: 128 chars.')
->inject('response')
->inject('dbForProject')
@@ -1316,7 +1316,7 @@ Http::patch('/v1/users/:userId/password')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, enabled: $project->getAttribute('auths', [])['passwordDictionary'] ?? false, allowEmpty: true), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->inject('response')
->inject('project')
@@ -1415,7 +1415,7 @@ Http::patch('/v1/users/:userId/email')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('email', '', new EmailValidator(allowEmpty: true), 'User email.')
->inject('response')
->inject('dbForProject')
@@ -1526,7 +1526,7 @@ Http::patch('/v1/users/:userId/phone')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('number', '', new Phone(allowEmpty: true), 'User phone number.')
->inject('response')
->inject('dbForProject')
@@ -1616,7 +1616,7 @@ Http::patch('/v1/users/:userId/verification')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('emailVerification', false, new Boolean(), 'User email verification status.')
->inject('response')
->inject('dbForProject')
@@ -1654,7 +1654,7 @@ Http::patch('/v1/users/:userId/prefs')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->inject('response')
->inject('dbForProject')
@@ -1695,10 +1695,10 @@ Http::patch('/v1/users/:userId/targets/:targetId')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->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('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), '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, ['dbForProject'])
->param('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
->inject('queueForEvents')
->inject('response')
@@ -1820,7 +1820,7 @@ Http::patch('/v1/users/:userId/mfa')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('mfa', null, new Boolean(), 'Enable or disable MFA.')
->inject('response')
->inject('dbForProject')
@@ -1880,7 +1880,7 @@ Http::get('/v1/users/:userId/mfa/factors')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -1939,7 +1939,7 @@ Http::get('/v1/users/:userId/mfa/recovery-codes')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -2004,7 +2004,7 @@ Http::patch('/v1/users/:userId/mfa/recovery-codes')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2077,7 +2077,7 @@ Http::put('/v1/users/:userId/mfa/recovery-codes')
public: false,
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2150,7 +2150,7 @@ Http::delete('/v1/users/:userId/mfa/authenticators/:type')
contentType: ContentType::NONE
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.')
->inject('response')
->inject('dbForProject')
@@ -2197,7 +2197,7 @@ Http::post('/v1/users/:userId/sessions')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->inject('request')
->inject('response')
->inject('dbForProject')
@@ -2289,7 +2289,7 @@ Http::post('/v1/users/:userId/tokens')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('length', 6, new Range(4, 128), 'Token length in characters. The default length is 6 characters', true)
->param('expire', TOKEN_EXPIRATION_GENERIC, new Range(60, TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
->inject('request')
@@ -2355,8 +2355,8 @@ Http::delete('/v1/users/:userId/sessions/:sessionId')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', '', new UID(), 'Session ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2406,7 +2406,7 @@ Http::delete('/v1/users/:userId/sessions')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2456,7 +2456,7 @@ Http::delete('/v1/users/:userId')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2508,8 +2508,8 @@ Http::delete('/v1/users/:userId/targets/:targetId')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('queueForDeletes')
->inject('response')
@@ -2566,7 +2566,7 @@ Http::delete('/v1/users/identities/:identityId')
],
contentType: ContentType::NONE,
))
->param('identityId', '', new UID(), 'Identity ID.')
->param('identityId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Identity ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2605,8 +2605,8 @@ Http::post('/v1/users/:userId/jwts')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', '', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true)
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true, ['dbForProject'])
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForProject')
+3
View File
@@ -36,6 +36,9 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$projectId = $repository->getAttribute('projectId');
$project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND, 'Repository references non-existent project');
}
$dbForProject = $getProjectDB($project);
$resourceCollection = $resourceType === "function" ? 'functions' : 'sites';
+5 -1
View File
@@ -1352,7 +1352,7 @@ Http::error()
$log->setMessage($error->getMessage());
$log->addTag('database', $dsn->getHost());
$log->addTag('method', $route->getMethod());
$log->addTag('method', $route?->getMethod() ?? $request->getMethod());
$log->addTag('url', $request->getURI());
$log->addTag('verboseType', get_class($error));
$log->addTag('code', $error->getCode());
@@ -1450,10 +1450,14 @@ Http::error()
// don't fail the error handler
}
$sdk = $route?->getLabel("sdk", false);
$action = 'UNKNOWN_NAMESPACE.UNKNOWN.METHOD';
if (!empty($sdk)) {
/** @var \Appwrite\SDK\Method $sdk */
$action = $sdk->getNamespace() . '.' . $sdk->getMethodName();
} elseif ($route === null) {
$path = ltrim(parse_url($request->getURI(), PHP_URL_PATH) ?? '/', '/') ?: 'root';
$action = 'http.' . $request->getMethod() . '.' . $path;
}
$log->setAction($action);
-1
View File
@@ -31,7 +31,6 @@ Http::get('/v1/mock/tests/general/oauth2')
->param('state', '', new Text(1024), 'OAuth2 state.')
->inject('response')
->action(function (string $client_id, string $redirectURI, string $scope, string $state, Response $response) {
$response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state]));
});
+2
View File
@@ -472,6 +472,7 @@ Http::init()
/*
* Abuse Check
*/
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
$timeLimitArray = [];
@@ -507,6 +508,7 @@ Http::init()
$abuse = new Abuse($timeLimit);
$remaining = $timeLimit->remaining();
$limit = $timeLimit->limit();
$time = $timeLimit->time() + $route->getLabel('abuse-time', 3600);
+58 -18
View File
@@ -192,8 +192,8 @@ include __DIR__ . '/controllers/general.php';
function createDatabase(Http $app, string $resourceKey, string $dbName, array $collections, mixed $pools, ?callable $extraSetup = null): void
{
$max = 10;
$sleep = 1;
$max = 15;
$sleep = 2;
$attempts = 0;
while (true) {
@@ -203,8 +203,8 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
/* @var $database Database */
$database = is_callable($resource) ? $resource() : $resource;
break; // exit loop on success
} catch (\Exception $e) {
Console::warning(" └── Database not ready. Retrying connection ({$attempts})...");
} catch (\Throwable $e) {
Console::warning(" └── Database not ready ({$dbName}). Retrying connection ({$attempts}): " . $e->getMessage());
if ($attempts >= $max) {
throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage());
}
@@ -215,11 +215,27 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c
Span::init("database.setup");
Span::add('database.name', $dbName);
// Attempt to create the database
try {
$database->create();
} catch (\Exception $e) {
Span::add('database.exists', true);
$attempts = 0;
while (true) {
try {
$attempts++;
Console::info(" └── Creating database: $dbName...");
$database->create();
break; // exit loop on success
} catch (\Exception $e) {
if ($e instanceof DuplicateException) {
Span::add('database.exists', true);
Console::info(" └── Skip: metadata table already exists");
break;
}
Console::warning(" └── Database create failed. Retrying ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception(' └── Failed to create database: ' . $e->getMessage());
}
\sleep($sleep);
}
}
// Process collections
@@ -406,10 +422,26 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $tot
->setTenant(null)
->setNamespace(System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', ''));
try {
$dbForProject->create();
} catch (DuplicateException) {
Span::add('database.exists', true);
$max = 15;
$sleep = 2;
$attempts = 0;
while (true) {
try {
$attempts++;
Console::success('[Setup] - Creating project database: ' . $hostname . '...');
$dbForProject->create();
break; // exit loop on success
} catch (DuplicateException) {
Span::add('database.exists', true);
Console::success('[Setup] - Skip: metadata table already exists');
break;
} catch (\Throwable $e) {
Console::warning(" └── Project database create failed. Retrying ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception(' └── Failed to create project database: ' . $e->getMessage());
}
sleep($sleep);
}
}
if ($dbForProject->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
@@ -528,8 +560,13 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$log->setType(Log::TYPE_ERROR);
$log->setMessage($th->getMessage());
$log->addTag('method', $route->getMethod());
$log->addTag('url', $route->getPath());
$log->addTag('method', $route?->getMethod() ?? $request->getMethod());
$log->addTag('url', $route?->getPath() ?? $request->getURI());
if (str_contains($th->getMessage(), 'FTS_TERM or FTS_NUMB')) {
$log->addTag('paramQueries', json_encode($request->getParam('queries')));
}
$log->addTag('verboseType', get_class($th));
$log->addTag('code', $th->getCode());
// $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant
@@ -541,12 +578,15 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$log->addExtra('trace', $th->getTraceAsString());
$log->addExtra('roles', isset($authorization) ? $authorization->getRoles() : []);
$sdk = $route->getLabel("sdk", false);
$sdk = $route?->getLabel("sdk", false);
$action = 'UNKNOWN_NAMESPACE.UNKNOWN.METHOD';
if (!empty($sdk)) {
/** @var Appwrite\SDK\Method $sdk */
$action = $sdk->getNamespace() . '.' . $sdk->getMethodName();
} elseif ($route === null) {
$path = ltrim(parse_url($request->getURI(), PHP_URL_PATH) ?? '/', '/') ?: 'root';
$action = 'http.' . $request->getMethod() . '.' . $path;
}
$log->setAction($action);
@@ -610,7 +650,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register) {
if ($latestDocument !== null) {
$queries[] = Query::cursorAfter($latestDocument);
}
if ($lastSyncUpdate != null) {
if ($lastSyncUpdate !== null) {
$queries[] = Query::greaterThanEqual('$updatedAt', $lastSyncUpdate);
}
$results = [];
@@ -618,7 +658,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register) {
$authorization = $app->getResource('authorization');
$results = $authorization->skip(fn () => $dbForPlatform->find('rules', $queries));
} catch (Throwable $th) {
Console::error($th->getMessage());
Console::error('rules ' . $th->getMessage());
}
$sum = count($results);
+1
View File
@@ -6,6 +6,7 @@ const APP_NAME = 'Appwrite';
const APP_DOMAIN = 'appwrite.io';
const APP_VIEWS_DIR = __DIR__ . '/../views';
const APP_CE_CONFIG_DIR = __DIR__ . '/../config';
// Email
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
+5 -1
View File
@@ -72,7 +72,7 @@ Database::addFilter(
$attributes = $database->getAuthorization()->skip(fn () => $database->find('attributes', [
Query::equal('collectionInternalId', [$document->getSequence()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForAttributes()),
Query::limit($database->getLimitForAttributes() ?: APP_LIMIT_SUBQUERY),
]));
foreach ($attributes as $attribute) {
@@ -88,6 +88,10 @@ Database::addFilter(
break;
case Database::VAR_STRING:
case Database::VAR_VARCHAR:
case Database::VAR_TEXT:
case Database::VAR_MEDIUMTEXT:
case Database::VAR_LONGTEXT:
$filters = $attribute->getAttribute('filters', []);
$attribute->setAttribute('encrypt', in_array('encrypt', $filters));
break;
+37 -22
View File
@@ -12,6 +12,7 @@ use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Config\Config;
use Utopia\Console;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\Mongo;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Adapter\Postgres;
use Utopia\Database\Adapter\SQL;
@@ -24,6 +25,7 @@ use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Logger;
use Utopia\Mongo\Client as MongoClient;
use Utopia\Pools\Adapter\Stack as StackPool;
use Utopia\Pools\Adapter\Swoole as SwoolePool;
use Utopia\Pools\Group;
@@ -151,13 +153,14 @@ $register->set('pools', function () {
$group = new Group();
$fallbackForDB = 'db_main=' . AppwriteURL::unparse([
'scheme' => System::getEnv('_APP_DB_ADAPTER', 'mariadb'),
'host' => System::getEnv('_APP_DB_HOST', 'mariadb'),
'port' => System::getEnv('_APP_DB_PORT', '3306'),
'scheme' => System::getEnv('_APP_DB_ADAPTER', 'mongodb'),
'host' => System::getEnv('_APP_DB_HOST', 'mongodb'),
'port' => System::getEnv('_APP_DB_PORT', '27017'),
'user' => System::getEnv('_APP_DB_USER', ''),
'pass' => System::getEnv('_APP_DB_PASS', ''),
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
]);
$fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([
'scheme' => 'redis',
'host' => System::getEnv('_APP_REDIS_HOST', 'redis'),
@@ -171,19 +174,19 @@ $register->set('pools', function () {
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => false,
'schemes' => ['mariadb', 'mysql','postgresql'],
'schemes' => ['mariadb', 'mongodb', 'mysql', 'postgresql'],
],
'database' => [
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => true,
'schemes' => ['mariadb', 'mysql','postgresql'],
'schemes' => ['mariadb', 'mongodb', 'mysql', 'postgresql'],
],
'logs' => [
'type' => 'database',
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_LOGS', $fallbackForDB),
'multiple' => false,
'schemes' => ['mariadb', 'mysql','postgresql'],
'schemes' => ['mariadb', 'mongodb', 'mysql', 'postgresql'],
],
'publisher' => [
'type' => 'publisher',
@@ -263,6 +266,7 @@ $register->set('pools', function () {
*
* Resource assignment to an adapter will happen below.
*/
$resource = match ($dsnScheme) {
'mysql',
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
@@ -276,9 +280,19 @@ $register->set('pools', function () {
]);
});
},
'mongodb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase, $dsn) {
try {
$mongo = new MongoClient($dsnDatabase, $dsnHost, (int)$dsnPort, $dsnUser, $dsnPass, false);
@$mongo->connect();
return $mongo;
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "MongoDB connection failed: " . $e->getMessage());
}
},
'postgresql' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("pgsql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase}", $dsnUser, $dsnPass, array(
return new PDO("pgsql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};connect_timeout=3", $dsnUser, $dsnPass, array(
\PDO::ATTR_TIMEOUT => 3, // Seconds
\PDO::ATTR_PERSISTENT => false,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
@@ -309,6 +323,7 @@ $register->set('pools', function () {
$adapter = match ($dsn->getScheme()) {
'mariadb' => new MariaDB($resource()),
'mysql' => new MySQL($resource()),
'mongodb' => new Mongo($resource()),
'postgresql' => new Postgres($resource()),
default => null
};
@@ -365,28 +380,28 @@ $register->set('db', function () {
$dbUser = System::getEnv('_APP_DB_USER', '');
$dbPass = System::getEnv('_APP_DB_PASS', '');
$dbSchema = System::getEnv('_APP_DB_SCHEMA', '');
$dbAdapter = System::getEnv('_APP_DB_ADAPTER', 'mariadb');
$dbAdapter = System::getEnv('_APP_DB_ADAPTER', 'mongodb');
$dsn = '';
switch ($dbAdapter) {
case 'postgresql':
$dsn = "pgsql:host={$dbHost};port={$dbPort};dbname={$dbSchema}";
break;
case 'mongodb':
try {
$mongo = new MongoClient($dbSchema, $dbHost, (int)$dbPort, $dbUser, $dbPass, false);
@$mongo->connect();
return $mongo;
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'MongoDB connection failed: ' . $e->getMessage());
}
case 'mysql':
case 'mariadb':
default:
$dsn = "mysql:host={$dbHost};port={$dbPort};dbname={$dbSchema};charset=utf8mb4";
break;
return new PDO($dsn, $dbUser, $dbPass, SQL::getPDOAttributes());
case 'postgresql':
$dsn = "pgsql:host={$dbHost};port={$dbPort};dbname={$dbSchema};connect_timeout=3";
return new PDO($dsn, $dbUser, $dbPass, SQL::getPDOAttributes());
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid database adapter');
}
return new PDO(
$dsn,
$dbUser,
$dbPass,
SQL::getPDOAttributes()
);
});
$register->set('smtp', function () {
+20 -14
View File
@@ -230,8 +230,8 @@ Http::setResource('allowedHostnames', function (array $platform, Document $proje
/**
* List of allowed request schemes for the request.
*/
Http::setResource('allowedSchemes', function (Document $project) {
$allowed = [];
Http::setResource('allowedSchemes', function (array $platform, Document $project) {
$allowed = [...($platform['schemas'] ?? [])];
if (!$project->isEmpty() && $project->getId() !== 'console') {
/* Add hardcoded schemes */
@@ -245,7 +245,7 @@ Http::setResource('allowedSchemes', function (Document $project) {
}
return array_unique($allowed);
}, ['project']);
}, ['platform', 'project']);
/**
* Rule associated with a request origin.
@@ -412,13 +412,7 @@ Http::setResource('user', function (string $mode, Document $project, Document $c
) { // Validate user has valid login token
$user = new User([]);
}
// if (APP_MODE_ADMIN === $mode) {
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
// $authorization->setDefaultStatus(false); // Cancel security segmentation for admin users.
// } else {
// $user = new Document([]);
// }
// }
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
if (!$user->isEmpty()) {
@@ -565,11 +559,16 @@ Http::setResource('dbForProject', function (Group $pools, Database $dbForPlatfor
return $dbForPlatform;
}
$database = $project->getAttribute('database', '');
if (empty($database)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Project database is not configured');
}
try {
$dsn = new DSN($project->getAttribute('database'));
$dsn = new DSN($database);
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
$dsn = new DSN('mysql://' . $database);
}
$adapter = new DatabasePool($pools->get($dsn->getHost()));
@@ -829,11 +828,16 @@ Http::setResource('getProjectDB', function (Group $pools, Database $dbForPlatfor
return $dbForPlatform;
}
$database = $project->getAttribute('database', '');
if (empty($database)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Project database is not configured');
}
try {
$dsn = new DSN($project->getAttribute('database'));
$dsn = new DSN($database);
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
$dsn = new DSN('mysql://' . $database);
}
$configure = (function (Database $database) use ($project, $dsn, $authorization) {
@@ -1287,6 +1291,8 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform,
}
}
// if teamInternalId is empty, return an empty document
if (empty($teamInternalId)) {
return new Document([]);
}
+99 -4
View File
@@ -13,6 +13,7 @@ $organization = $this->getParam('organization', '');
$image = $this->getParam('image', '');
$enableAssistant = $this->getParam('enableAssistant', false);
$dbService = $this->getParam('database');
?>services:
traefik:
image: traefik:3.6
@@ -104,6 +105,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -183,7 +185,7 @@ $dbService = $this->getParam('database');
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:7.5.7
image: <?php echo $organization; ?>/console:7.6.4
restart: unless-stopped
networks:
- appwrite
@@ -237,6 +239,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -265,6 +268,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -290,6 +294,7 @@ $dbService = $this->getParam('database');
- _APP_OPENSSL_KEY_V1
- _APP_EMAIL_SECURITY
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -328,6 +333,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -385,6 +391,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -419,6 +426,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -492,6 +500,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -522,6 +531,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -557,6 +567,7 @@ $dbService = $this->getParam('database');
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -597,6 +608,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -656,6 +668,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -692,6 +705,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -722,6 +736,7 @@ $dbService = $this->getParam('database');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -752,6 +767,7 @@ $dbService = $this->getParam('database');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -781,6 +797,7 @@ $dbService = $this->getParam('database');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -814,6 +831,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -840,6 +858,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -866,6 +885,7 @@ $dbService = $this->getParam('database');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -945,7 +965,6 @@ $dbService = $this->getParam('database');
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
<?php if ($dbService === 'mariadb'): ?>
mariadb:
@@ -964,10 +983,83 @@ $dbService = $this->getParam('database');
- MARIADB_AUTO_UPGRADE=1
command: 'mysqld --innodb-flush-method=fsync'
<?php elseif ($dbService === 'mongodb'): ?>
mongodb:
image: mongo:8.2.5
container_name: appwrite-mongodb
<<: *x-logging
networks:
- appwrite
volumes:
- appwrite-mongodb:/data/db
- appwrite-mongodb-keyfile:/data/keyfile
ports:
- "27017:27017"
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
- MONGO_INITDB_DATABASE=${_APP_DB_SCHEMA}
- MONGO_INITDB_USERNAME=${_APP_DB_USER}
- MONGO_INITDB_PASSWORD=${_APP_DB_PASS}
entrypoint:
- /bin/bash
- -c
- |
set -e
KEYFILE_PATH="/data/keyfile/mongo-keyfile"
INIT_FLAG="/data/db/.mongodb_initialized"
# Generate keyfile if it doesn't exist
if [ ! -f "$$KEYFILE_PATH" ]; then
echo "Generating random MongoDB keyfile..."
mkdir -p /data/keyfile
openssl rand -base64 756 > "$$KEYFILE_PATH"
fi
chmod 400 "$$KEYFILE_PATH"
chown mongodb:mongodb "$$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$$KEYFILE_PATH"
# If not initialized, start without auth first to set up replica set and users
if [ ! -f "$$INIT_FLAG" ]; then
echo "First-time initialization: starting MongoDB without auth..."
mongod --replSet rs0 --bind_ip_all --fork --logpath /var/log/mongodb/mongod.log --dbpath /data/db
echo "Waiting for MongoDB to start..."
sleep 5
echo "Initializing replica set..."
mongosh --eval "rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'mongodb:27017'}]})"
echo "Waiting for replica set to initialize..."
sleep 5
echo "Creating root user..."
mongosh admin --eval "db.createUser({user: '$$MONGO_INITDB_ROOT_USERNAME', pwd: '$$MONGO_INITDB_ROOT_PASSWORD', roles: ['root']})"
echo "Creating application user..."
mongosh admin --eval "db.createUser({user: '$$MONGO_INITDB_USERNAME', pwd: '$$MONGO_INITDB_PASSWORD', roles: [{role: 'readWrite', db: '$$MONGO_INITDB_DATABASE'}]})"
echo "Shutting down MongoDB..."
mongod --dbpath /data/db --shutdown
touch "$$INIT_FLAG"
echo "Initialization complete."
fi
echo "Starting MongoDB with authentication..."
exec mongod --replSet rs0 --bind_ip_all --auth --keyFile "$$KEYFILE_PATH"
healthcheck:
test: |
mongosh -u root -p "$$MONGO_INITDB_ROOT_PASSWORD" --authenticationDatabase admin --quiet --eval "rs.status().ok" | grep -q 1
interval: 10s
timeout: 10s
retries: 10
start_period: 60s
<?php elseif ($dbService === 'postgresql'): ?>
postgresql:
image: postgres:15
image: postgres:18
container_name: appwrite-postgresql
restart: unless-stopped
networks:
@@ -983,7 +1075,7 @@ $dbService = $this->getParam('database');
<?php endif; ?>
redis:
image: redis:7.2.4-alpine
image: redis:7.4.7-alpine
container_name: appwrite-redis
<<: *x-logging
restart: unless-stopped
@@ -1019,6 +1111,9 @@ volumes:
appwrite-mariadb:
<?php elseif ($dbService === 'postgresql'): ?>
appwrite-postgresql:
<?php elseif ($dbService === 'mongodb'): ?>
appwrite-mongodb:
appwrite-mongodb-keyfile:
<?php endif; ?>
appwrite-redis:
appwrite-cache:
+9 -5
View File
@@ -29,6 +29,8 @@
"Appwrite\\Tests\\": "tests/extensions"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.3.0",
"ext-curl": "*",
@@ -46,6 +48,7 @@
"appwrite/php-runtimes": "0.19.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "1.2.*",
"utopia-php/agents": "1.2.*",
"utopia-php/analytics": "0.15.*",
"utopia-php/audit": "2.2.*",
"utopia-php/auth": "0.5.*",
@@ -76,22 +79,23 @@
"utopia-php/storage": "1.0.*",
"utopia-php/system": "0.10.*",
"utopia-php/telemetry": "0.2.*",
"utopia-php/vcs": "1.*",
"utopia-php/vcs": "2.*",
"utopia-php/websocket": "1.0.*",
"matomo/device-detector": "6.4.*",
"dragonmantank/cron-expression": "3.4.*",
"phpmailer/phpmailer": "6.9.*",
"chillerlan/php-qrcode": "4.4.*",
"chillerlan/php-qrcode": "4.3.*",
"adhocore/jwt": "1.1.*",
"spomky-labs/otphp": "^11.4.2",
"spomky-labs/otphp": "11.*",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.24.*",
"league/csv": "9.14.*",
"enshrined/svg-sanitize": "0.22.*"
},
"require-dev": {
"ext-fileinfo": "*",
"appwrite/sdk-generator": "*",
"phpunit/phpunit": "9.*",
"brianium/paratest": "7.*",
"phpunit/phpunit": "12.*",
"swoole/ide-helper": "6.*",
"phpstan/phpstan": "1.12.*",
"textalk/websocket": "1.5.*",
Generated
+711 -553
View File
File diff suppressed because it is too large Load Diff
+108 -28
View File
@@ -64,6 +64,7 @@ services:
networks:
- appwrite
dns:
- 127.0.0.11
- 172.16.238.100
labels:
- "traefik.enable=true"
@@ -98,9 +99,9 @@ services:
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
- ./dev:/usr/src/code/dev
# - ./vendor/utopia-php/framework:/usr/src/code/vendor/utopia-php/framework
depends_on:
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
- redis
- coredns
# - clamav
@@ -121,6 +122,7 @@ services:
- _APP_CONSOLE_SESSION_ALERTS
- _APP_CONSOLE_WHITELIST_IPS
- _APP_CONSOLE_HOSTNAMES
- _APP_CONSOLE_SCHEMA
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
- _APP_SYSTEM_TEAM_EMAIL
@@ -144,6 +146,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -284,7 +287,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -296,6 +299,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -320,7 +324,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -330,6 +334,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -351,7 +356,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
- request-catcher-sms
- request-catcher-webhook
environment:
@@ -360,6 +365,7 @@ services:
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_EMAIL_SECURITY
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -383,7 +389,7 @@ services:
- appwrite
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
volumes:
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
@@ -402,6 +408,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -451,7 +458,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -461,6 +468,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -487,7 +495,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -499,6 +507,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -560,7 +569,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
# Specific
- _APP_BROWSER_HOST
@@ -577,6 +586,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -618,7 +628,7 @@ services:
- appwrite
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
volumes:
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
@@ -642,6 +652,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -662,10 +673,8 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
redis:
condition: service_started
mariadb:
condition: service_started
- redis
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -674,6 +683,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -693,7 +703,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -706,6 +716,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -791,6 +802,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -840,7 +852,7 @@ services:
- ./src:/usr/src/code/src
- ./tests:/usr/src/code/tests
depends_on:
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -857,6 +869,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -881,7 +894,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -899,6 +912,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -927,7 +941,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -946,6 +960,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -967,12 +982,13 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1000,12 +1016,13 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1033,12 +1050,13 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_POOL_ADAPTER
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1065,7 +1083,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -1076,6 +1094,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1095,7 +1114,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -1106,6 +1125,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1124,7 +1144,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- ${_APP_DB_HOST:-mariadb}
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -1135,6 +1155,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1217,6 +1238,7 @@ services:
start_period: 5s
mariadb:
profiles: ["mariadb"]
image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
<<: *x-logging
@@ -1234,8 +1256,64 @@ services:
- MARIADB_AUTO_UPGRADE=1
command: "mysqld --innodb-flush-method=fsync"
mongodb:
profiles: ["mongodb"]
image: mongo:8.2.5
container_name: appwrite-mongodb
<<: *x-logging
networks:
- appwrite
volumes:
- appwrite-mongodb:/data/db
- appwrite-mongodb-keyfile:/data/keyfile
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
- ./mongo-entrypoint.sh:/mongo-entrypoint.sh:ro
ports:
- "27017:27017"
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
- MONGO_INITDB_DATABASE=${_APP_DB_SCHEMA}
- MONGO_INITDB_USERNAME=${_APP_DB_USER}
- MONGO_INITDB_PASSWORD=${_APP_DB_PASS}
entrypoint: ["/bin/bash", "/mongo-entrypoint.sh"]
healthcheck:
test: |
bash -c "
if mongosh -u root -p ${_APP_DB_ROOT_PASS} --authenticationDatabase admin --quiet --eval 'rs.status().ok' 2>/dev/null | grep -q 1; then
exit 0
else
mongosh -u root -p ${_APP_DB_ROOT_PASS} --authenticationDatabase admin --quiet --eval \"
rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'appwrite-mongodb:27017'}]})
\" 2>/dev/null || exit 1
fi
"
interval: 10s
timeout: 10s
retries: 10
start_period: 30s
appwrite-mongo-express:
profiles: ["mongodb"]
image: mongo-express
container_name: appwrite-mongo-express
networks:
- appwrite
ports:
- "8082:8081"
environment:
ME_CONFIG_MONGODB_URL: "mongodb://root:${_APP_DB_ROOT_PASS}@appwrite-mongodb:27017/?replicaSet=rs0&directConnection=true"
ME_CONFIG_BASICAUTH_USERNAME: ${_APP_DB_USER}
ME_CONFIG_BASICAUTH_PASSWORD: ${_APP_DB_PASS}
depends_on:
- mongodb
postgresql:
image: postgres:18
profiles: ["postgresql"]
build:
context: ./tests/resources/postgresql
args:
POSTGRES_VERSION: 17
container_name: appwrite-postgresql
<<: *x-logging
networks:
@@ -1251,7 +1329,7 @@ services:
command: "postgres"
redis:
image: redis:7.2.4-alpine
image: redis:7.4.7-alpine
<<: *x-logging
container_name: appwrite-redis
command: >
@@ -1319,7 +1397,7 @@ services:
- MAILDEV_INCOMING_PASS=${_APP_SMTP_PASSWORD}
request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook)
image: appwrite/requestcatcher:1.0.0
image: appwrite/requestcatcher:1.1.0
container_name: appwrite-requestcatcher-webhook
<<: *x-logging
ports:
@@ -1328,7 +1406,7 @@ services:
- appwrite
request-catcher-sms: # used mainly for dev tests (mock SMS auth secret)
image: appwrite/requestcatcher:1.0.0
image: appwrite/requestcatcher:1.1.0
container_name: appwrite-requestcatcher-sms
<<: *x-logging
ports:
@@ -1477,6 +1555,8 @@ configs:
volumes:
appwrite-mariadb:
appwrite-mongodb:
appwrite-mongodb-keyfile:
appwrite-postgresql:
appwrite-redis:
appwrite-cache:
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.createAnonymousSession(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.createJWT(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.createMagicURLSession(
"email@example.com",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.createOAuth2Session(
this,
"amazon",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.createRecovery(
"email@example.com",
"https://example.com"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.createSession(
"email@example.com",
"password"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.createVerification(
"https://example.com"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.create(
"email@example.com",
"password",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.deleteSession(
"[SESSION_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.deleteSessions(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.delete(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.getLogs(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.getPrefs(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.getSession(
"[SESSION_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.getSessions(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.get(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.updateEmail(
"email@example.com",
"password"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.updateMagicURLSession(
"[USER_ID]",
"[SECRET]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.updateName(
"[NAME]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.updatePassword(
"password",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.updatePrefs(
mapOf( "a" to "b" )
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,51 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.updateRecovery(
"[USER_ID]",
"[SECRET]",
"password",
"password"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
account.updateVerification(
"[USER_ID]",
"[SECRET]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Avatars
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getBrowser(
"aa",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Avatars
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getCreditCard(
"amex",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Avatars
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getFavicon(
"https://example.com"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Avatars
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getFlag(
"af",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Avatars
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getImage(
"https://example.com",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,47 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Avatars
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getInitials(
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Avatars
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getQR(
"[TEXT]",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Database
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Database database = new Database(client);
database.createDocument(
"[COLLECTION_ID]",
mapOf( "a" to "b" ),
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Database
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Database database = new Database(client);
database.deleteDocument(
"[COLLECTION_ID]",
"[DOCUMENT_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Database
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Database database = new Database(client);
database.getDocument(
"[COLLECTION_ID]",
"[DOCUMENT_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Database
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Database database = new Database(client);
database.listDocuments(
"[COLLECTION_ID]",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,50 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Database
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Database database = new Database(client);
database.updateDocument(
"[COLLECTION_ID]",
"[DOCUMENT_ID]",
mapOf( "a" to "b" ),
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Functions
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
functions.createExecution(
"[FUNCTION_ID]",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Functions
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
functions.getExecution(
"[FUNCTION_ID]",
"[EXECUTION_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Functions
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Functions functions = new Functions(client);
functions.listExecutions(
"[FUNCTION_ID]",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Locale
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
locale.getContinents(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Locale
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
locale.getCountriesEU(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Locale
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
locale.getCountriesPhones(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Locale
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
locale.getCountries(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Locale
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
locale.getCurrencies(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Locale
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
locale.getLanguages(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,46 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Locale
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Locale locale = new Locale(client);
locale.get(new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
});
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.createFile(
File("./path-to-files/image.jpg"),
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.deleteFile(
"[FILE_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.getFileDownload(
"[FILE_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.getFilePreview(
"[FILE_ID]",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.getFileView(
"[FILE_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.getFile(
"[FILE_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,47 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.listFiles(
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,50 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Storage
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Storage storage = new Storage(client);
storage.updateFile(
"[FILE_ID]",
listOf(),
listOf()
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,51 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.createMembership(
"[TEAM_ID]",
"email@example.com",
listOf(),
"https://example.com",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.create(
"[NAME]",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.deleteMembership(
"[TEAM_ID]",
"[MEMBERSHIP_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.delete(
"[TEAM_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.getMemberships(
"[TEAM_ID]",
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,48 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.get(
"[TEAM_ID]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,47 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.list(
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,50 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.updateMembershipRoles(
"[TEAM_ID]",
"[MEMBERSHIP_ID]",
listOf()
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,51 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.updateMembershipStatus(
"[TEAM_ID]",
"[MEMBERSHIP_ID]",
"[USER_ID]",
"[SECRET]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,49 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Teams
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Teams teams = new Teams(client);
teams.update(
"[TEAM_ID]",
"[NAME]"
new Continuation<Object>() {
@NotNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NotNull Object o) {
String json = "";
try {
if (o instanceof Result.Failure) {
Result.Failure failure = (Result.Failure) o;
throw failure.exception;
} else {
Response response = (Response) o;
json = response.body().string();
}
} catch (Throwable th) {
Log.e("ERROR", th.toString());
}
}
}
);
}
}
@@ -1,24 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val client = Client(applicationContext)
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
val account = Account(client)
GlobalScope.launch {
val response = account.createAnonymousSession()
val json = response.body?.string()
}
}
}
@@ -1,24 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val client = Client(applicationContext)
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
val account = Account(client)
GlobalScope.launch {
val response = account.createJWT()
val json = response.body?.string()
}
}
}
@@ -1,26 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val client = Client(applicationContext)
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
val account = Account(client)
GlobalScope.launch {
val response = account.createMagicURLSession(
email = "email@example.com",
)
val json = response.body?.string()
}
}
}
@@ -1,27 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val client = Client(applicationContext)
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
val account = Account(client)
GlobalScope.launch {
account.createOAuth2Session(
activity = this@MainActivity,
provider = "amazon",
)
}
}
}
@@ -1,27 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val client = Client(applicationContext)
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
val account = Account(client)
GlobalScope.launch {
val response = account.createRecovery(
email = "email@example.com",
url = "https://example.com"
)
val json = response.body?.string()
}
}
}
@@ -1,27 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val client = Client(applicationContext)
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
val account = Account(client)
GlobalScope.launch {
val response = account.createSession(
email = "email@example.com",
password = "password"
)
val json = response.body?.string()
}
}
}
@@ -1,26 +0,0 @@
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.appwrite.Client
import io.appwrite.services.Account
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val client = Client(applicationContext)
.setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint
.setProject("5df5acd0d48c2") // Your project ID
val account = Account(client)
GlobalScope.launch {
val response = account.createVerification(
url = "https://example.com"
)
val json = response.body?.string()
}
}
}

Some files were not shown because too many files have changed in this diff Show More