diff --git a/CHANGES.md b/CHANGES.md index 62db3d525e..bc903e4b31 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,267 @@ +# Version 1.6.2 + +## What's Changed + +### Notable changes + +* Delete git folder to reduce build size in [9076](https://github.com/appwrite/appwrite/pull/9076) +* Upgrade assistant in [9100](https://github.com/appwrite/appwrite/pull/9100) +* Use redis adapter for abuse in [9121](https://github.com/appwrite/appwrite/pull/9121) +* Set base specification CPUs to 0.5 again in [9146](https://github.com/appwrite/appwrite/pull/9146) +* Add new push message parameters in [9060](https://github.com/appwrite/appwrite/pull/9060) +* Update audits to include user type in [9211](https://github.com/appwrite/appwrite/pull/9211) +* Enable HEIC in [9251](https://github.com/appwrite/appwrite/pull/9251) +* Added teamName to membership redirect url in [9269](https://github.com/appwrite/appwrite/pull/9269) +* Add support endpoint url for S3 in [9303](https://github.com/appwrite/appwrite/pull/9303) +* Added RuPay Credit Card Icon in Avatars Service in [5046](https://github.com/appwrite/appwrite/pull/5046) +* Add figma oauth provider in [9623](https://github.com/appwrite/appwrite/pull/9623) +* Update console to version 5.2.58 in [9637](https://github.com/appwrite/appwrite/pull/9637) + +### Fixes + +* Remove failed attribute in [9032](https://github.com/appwrite/appwrite/pull/9032) +* Fix delete notFound attribute in [9038](https://github.com/appwrite/appwrite/pull/9038) +* 🇮🇸 Added missing Icelandic translations for email strings. in [4848](https://github.com/appwrite/appwrite/pull/4848) +* fix doc comment for filter method in [5769](https://github.com/appwrite/appwrite/pull/5769) +* Delete attribute No throwing Exception on not found in [9157](https://github.com/appwrite/appwrite/pull/9157) +* Fix VCS identity collision in [9138](https://github.com/appwrite/appwrite/pull/9138) +* Fix disabling of email-otp when user wants to in [9200](https://github.com/appwrite/appwrite/pull/9200) +* Ensure user can delete session in [9209](https://github.com/appwrite/appwrite/pull/9209) +* Fix resend invitation in [9218](https://github.com/appwrite/appwrite/pull/9218) +* Fix phone number parsing exception handling in [9246](https://github.com/appwrite/appwrite/pull/9246) +* Fix amazon oauth in [9253](https://github.com/appwrite/appwrite/pull/9253) +* Fix slack oauth scopes, and updated to v2 in [9228](https://github.com/appwrite/appwrite/pull/9228) +* Fix forwarded user agent in [9271](https://github.com/appwrite/appwrite/pull/9271) +* Fix WEBP File Preview Rendering Issue in [9321](https://github.com/appwrite/appwrite/pull/9321) +* Fix build memory specifications in [9360](https://github.com/appwrite/appwrite/pull/9360) +* Fix Self Hosting functions by adding missed config in [9373](https://github.com/appwrite/appwrite/pull/9373) +* Fix resend team invite if already accepted in [9348](https://github.com/appwrite/appwrite/pull/9348) +* Fix null errors on team invite in [9391](https://github.com/appwrite/appwrite/pull/9391) +* Fix email (smtp) to multiple recipients in [9243](https://github.com/appwrite/appwrite/pull/9243) +* Fix stats timing by using receivedAt date when available in [9428](https://github.com/appwrite/appwrite/pull/9428) +* Make min/max params optional for attribute update in [9387](https://github.com/appwrite/appwrite/pull/9387) +* Fix blocking of phone sessions when disabled on console in [9447](https://github.com/appwrite/appwrite/pull/9447) +* Fix logging config in [9467](https://github.com/appwrite/appwrite/pull/9467) +* Update audit timestamp origin in [9481](https://github.com/appwrite/appwrite/pull/9481) +* Fix certificates in deletes worker in [9466](https://github.com/appwrite/appwrite/pull/9466) +* Fix console audits delete in [9547](https://github.com/appwrite/appwrite/pull/9547) +* Fix migrations in [9633](https://github.com/appwrite/appwrite/pull/9633) +* Ensure all 4xx errors in OAuth redirect lead to the failure URL in [9679](https://github.com/appwrite/appwrite/pull/9679) +* Treat 0 as unlimited for CPUs and memory in [9638](https://github.com/appwrite/appwrite/pull/9638) +* Add contextual dispatch logic to fix high CPU usage in [9687](https://github.com/appwrite/appwrite/pull/9687) + +### Miscellaneous + +* Merge 1.6.x into feat-custom-cf-hostnames in [8904](https://github.com/appwrite/appwrite/pull/8904) +* Improve compression param checks in [8922](https://github.com/appwrite/appwrite/pull/8922) +* upgrade utopia storage in [8930](https://github.com/appwrite/appwrite/pull/8930) +* Feat migration in [8797](https://github.com/appwrite/appwrite/pull/8797) +* feat fix web routes in [8962](https://github.com/appwrite/appwrite/pull/8962) +* Fix no pool access in [9027](https://github.com/appwrite/appwrite/pull/9027) +* feat: use environment variable to check rules format in [9039](https://github.com/appwrite/appwrite/pull/9039) +* Update storage.php in [9037](https://github.com/appwrite/appwrite/pull/9037) +* Upgrade db 0.53.200 in [9050](https://github.com/appwrite/appwrite/pull/9050) +* Chore: upgrade utopia storage in [9066](https://github.com/appwrite/appwrite/pull/9066) +* Update usage-dump payload in [9085](https://github.com/appwrite/appwrite/pull/9085) +* GitHub Workflows security hardening in [3728](https://github.com/appwrite/appwrite/pull/3728) +* Update add-oauth2-provider.md in [4313](https://github.com/appwrite/appwrite/pull/4313) +* update readme-cn some doc in [5278](https://github.com/appwrite/appwrite/pull/5278) +* Add accessibility features in [7042](https://github.com/appwrite/appwrite/pull/7042) +* Add Appwrite Cloud to read me. in [5445](https://github.com/appwrite/appwrite/pull/5445) +* Migration throw error in [9092](https://github.com/appwrite/appwrite/pull/9092) +* Fix usage payload bug in [9097](https://github.com/appwrite/appwrite/pull/9097) +* chore: replace occurrences of dbForConsole to dbForPlatform in [9096](https://github.com/appwrite/appwrite/pull/9096) +* fix(realtime): decrement connectionCounter only if connection is known in [9055](https://github.com/appwrite/appwrite/pull/9055) +* payload bug fix in [9098](https://github.com/appwrite/appwrite/pull/9098) +* Fix usage payload bug in [9099](https://github.com/appwrite/appwrite/pull/9099) +* Usage payload debug in [9101](https://github.com/appwrite/appwrite/pull/9101) +* Usage payload debug in [9103](https://github.com/appwrite/appwrite/pull/9103) +* Usage payload debug in [9104](https://github.com/appwrite/appwrite/pull/9104) +* Feat: createFunction abuse labels in [9102](https://github.com/appwrite/appwrite/pull/9102) +* Docs-create-document in [9105](https://github.com/appwrite/appwrite/pull/9105) +* Docs: Create document and unknown attribute error messages. in [5427](https://github.com/appwrite/appwrite/pull/5427) +* Fix: update project accessed at from router and schedulers in [9109](https://github.com/appwrite/appwrite/pull/9109) +* chore: initial commit in [9111](https://github.com/appwrite/appwrite/pull/9111) +* chore: optimise webhooks payload in [9115](https://github.com/appwrite/appwrite/pull/9115) +* Revert "chore: initial commit" in [9117](https://github.com/appwrite/appwrite/pull/9117) +* chore: fix attribute name in [9118](https://github.com/appwrite/appwrite/pull/9118) +* Migrate to redis abuse in [9124](https://github.com/appwrite/appwrite/pull/9124) +* Added webhooks usage stats in [9125](https://github.com/appwrite/appwrite/pull/9125) +* chore remove abuse cleanup in [9137](https://github.com/appwrite/appwrite/pull/9137) +* fix: remove abuse delete trigger in [9139](https://github.com/appwrite/appwrite/pull/9139) +* Remove firebase OAuth API endpoints in [9144](https://github.com/appwrite/appwrite/pull/9144) +* chore: release client sdks in [9112](https://github.com/appwrite/appwrite/pull/9112) +* Update general.php in [9155](https://github.com/appwrite/appwrite/pull/9155) +* feat(swoole): allow configuration override of available cpus in [9177](https://github.com/appwrite/appwrite/pull/9177) +* Usage databases api read writes addition in [9142](https://github.com/appwrite/appwrite/pull/9142) +* Fix dead connections in [9190](https://github.com/appwrite/appwrite/pull/9190) +* Add hostname to audits in [9165](https://github.com/appwrite/appwrite/pull/9165) +* chore: shifted authphone usage tracking to api calls in [9191](https://github.com/appwrite/appwrite/pull/9191) +* Revert "Fix dead connections" in [9201](https://github.com/appwrite/appwrite/pull/9201) +* Add assertEventually to messaging provider logs test in [9192](https://github.com/appwrite/appwrite/pull/9192) +* feat project sms usage in [9198](https://github.com/appwrite/appwrite/pull/9198) +* chore: add audit labels to project resources in [9056](https://github.com/appwrite/appwrite/pull/9056) +* fix sms usage in [9207](https://github.com/appwrite/appwrite/pull/9207) +* Update database in [9202](https://github.com/appwrite/appwrite/pull/9202) +* Fix dead connections in [9213](https://github.com/appwrite/appwrite/pull/9213) +* Revert "Fix dead connections" in [9214](https://github.com/appwrite/appwrite/pull/9214) +* Add logs db init for consistency in [9163](https://github.com/appwrite/appwrite/pull/9163) +* Split the collection definitions in [9153](https://github.com/appwrite/appwrite/pull/9153) +* Log path with populated parameters in [9220](https://github.com/appwrite/appwrite/pull/9220) +* Add missing scope on function template in [9208](https://github.com/appwrite/appwrite/pull/9208) +* Add relatedCollection default in [9225](https://github.com/appwrite/appwrite/pull/9225) +* fix: function usage in [9235](https://github.com/appwrite/appwrite/pull/9235) +* feat: optimise events payloads in [9232](https://github.com/appwrite/appwrite/pull/9232) +* Optimise webhook events in [9168](https://github.com/appwrite/appwrite/pull/9168) +* fix: maintenance job missing type in [9238](https://github.com/appwrite/appwrite/pull/9238) +* Update Fetch to 0.3.0 in [9245](https://github.com/appwrite/appwrite/pull/9245) +* Fix maintenance job in [9247](https://github.com/appwrite/appwrite/pull/9247) +* chore: add missing case for executions in [9248](https://github.com/appwrite/appwrite/pull/9248) +* Add index dependency exception in [9226](https://github.com/appwrite/appwrite/pull/9226) +* chore: fix benchmarking test when made from fork in [9233](https://github.com/appwrite/appwrite/pull/9233) +* Update SDK Generator versions in [9188](https://github.com/appwrite/appwrite/pull/9188) +* chore: skipped job instead of throwing error in [9250](https://github.com/appwrite/appwrite/pull/9250) +* Implement new SDK Class on 1.6.x in [9237](https://github.com/appwrite/appwrite/pull/9237) +* Delete collection before Appwrite's attributes in [9256](https://github.com/appwrite/appwrite/pull/9256) +* Feat batch usage dump in [9255](https://github.com/appwrite/appwrite/pull/9255) +* Fix cloud tests in [9261](https://github.com/appwrite/appwrite/pull/9261) +* Usage: Databases reads writes in [9260](https://github.com/appwrite/appwrite/pull/9260) +* Update: Latest sdk specs in [9274](https://github.com/appwrite/appwrite/pull/9274) +* Revert "Feat batch usage dump" in [9276](https://github.com/appwrite/appwrite/pull/9276) +* feat: add fast2SMS adapter in [9263](https://github.com/appwrite/appwrite/pull/9263) +* Update Sdk Generator dependency in [9280](https://github.com/appwrite/appwrite/pull/9280) +* Transformed at addition in [9281](https://github.com/appwrite/appwrite/pull/9281) +* Docs: clarify update endpoints only work on draft messages in [9236](https://github.com/appwrite/appwrite/pull/9236) +* Update sdk generator dependency in [9282](https://github.com/appwrite/appwrite/pull/9282) +* Revert "Transformed at addition" in [9284](https://github.com/appwrite/appwrite/pull/9284) +* replaced init for cloud link in [9285](https://github.com/appwrite/appwrite/pull/9285) +* Add transformed at in [9289](https://github.com/appwrite/appwrite/pull/9289) +* Make migrations use Dynamic keys for destination in [9291](https://github.com/appwrite/appwrite/pull/9291) +* Make sessions limit tests assert eventually in [9298](https://github.com/appwrite/appwrite/pull/9298) +* Chore update database in [9306](https://github.com/appwrite/appwrite/pull/9306) +* feat: add AMQP queues in [9287](https://github.com/appwrite/appwrite/pull/9287) +* fix(test): use assertEventually instead of while(true) in [9308](https://github.com/appwrite/appwrite/pull/9308) +* fix(certificate worker): events are published without queue name in [9309](https://github.com/appwrite/appwrite/pull/9309) +* chore: update utopia-php/queue to 0.8.1 in [9311](https://github.com/appwrite/appwrite/pull/9311) +* chore: update utopia-php/queue to 0.8.2 in [9312](https://github.com/appwrite/appwrite/pull/9312) +* fix(schedule-tasks): revert back to direct pool usage in [9313](https://github.com/appwrite/appwrite/pull/9313) +* feat: custom app schemes in [9262](https://github.com/appwrite/appwrite/pull/9262) +* Revert "feat: custom app schemes" in [9319](https://github.com/appwrite/appwrite/pull/9319) +* Restore "feat: custom app schemes"" in [9320](https://github.com/appwrite/appwrite/pull/9320) +* Revert "Restore "feat: custom app schemes""" in [9323](https://github.com/appwrite/appwrite/pull/9323) +* chore: update dependencies in [9330](https://github.com/appwrite/appwrite/pull/9330) +* Feat: logs DB in [9272](https://github.com/appwrite/appwrite/pull/9272) +* Catch invalid index in [9329](https://github.com/appwrite/appwrite/pull/9329) +* Fix: missing call for image transformations counting in [9342](https://github.com/appwrite/appwrite/pull/9342) +* Fix drop abuse on shared table project delete in [9346](https://github.com/appwrite/appwrite/pull/9346) +* Only run all table mode tests on db update in [9338](https://github.com/appwrite/appwrite/pull/9338) +* Fix: missing periodic metric in [9350](https://github.com/appwrite/appwrite/pull/9350) +* feat(builds): check if function is blocked before building in [9332](https://github.com/appwrite/appwrite/pull/9332) +* feat: batch create audit logs in [9347](https://github.com/appwrite/appwrite/pull/9347) +* Chore: Update migrations in [9355](https://github.com/appwrite/appwrite/pull/9355) +* Fix: metric time was not being written to DB in [9354](https://github.com/appwrite/appwrite/pull/9354) +* Fix patch index validation in [9356](https://github.com/appwrite/appwrite/pull/9356) +* Fix image trnasformation metrics in [9370](https://github.com/appwrite/appwrite/pull/9370) +* Use batch delete in worker in [9375](https://github.com/appwrite/appwrite/pull/9375) +* Fix Model Platform is missing response key: store in [9361](https://github.com/appwrite/appwrite/pull/9361) +* Feat key segmented usage in [9336](https://github.com/appwrite/appwrite/pull/9336) +* Feat messaging metrics in [9353](https://github.com/appwrite/appwrite/pull/9353) +* Fix removed audits for shared v2 in [9388](https://github.com/appwrite/appwrite/pull/9388) +* chore: bump utopia-php/image to 0.8.0 in [9390](https://github.com/appwrite/appwrite/pull/9390) +* Fix outdated CLI commands in documentation in [9122](https://github.com/appwrite/appwrite/pull/9122) +* disable logs display in [9398](https://github.com/appwrite/appwrite/pull/9398) +* Log batches per project in [9403](https://github.com/appwrite/appwrite/pull/9403) +* Batch per project in [9410](https://github.com/appwrite/appwrite/pull/9410) +* Fix: stats resources only queue projects accessed in last 3 hours in [9411](https://github.com/appwrite/appwrite/pull/9411) +* Track options requests in [9397](https://github.com/appwrite/appwrite/pull/9397) +* chore: bump docker-base in [9406](https://github.com/appwrite/appwrite/pull/9406) +* refactor: migrate Realtime::send calls to queueForRealtime in [9325](https://github.com/appwrite/appwrite/pull/9325) +* Revert "Fix: stats resources only queue projects accessed in last 3 hours" in [9424](https://github.com/appwrite/appwrite/pull/9424) +* Remove usage and usage dump in favor of stats-usage and stats-usage-dump in [9339](https://github.com/appwrite/appwrite/pull/9339) +* Fix: disable dual writing in [9429](https://github.com/appwrite/appwrite/pull/9429) +* Disable transformedAt update for console users in [9425](https://github.com/appwrite/appwrite/pull/9425) +* chore: add image transformation stats to usage endpoint in [9393](https://github.com/appwrite/appwrite/pull/9393) +* chore: added timeout to deployment builds in tests in [9426](https://github.com/appwrite/appwrite/pull/9426) +* fix: model for image transformations in usage project in [9442](https://github.com/appwrite/appwrite/pull/9442) +* Feat: calculate database storage in stats-resources in [9443](https://github.com/appwrite/appwrite/pull/9443) +* Activities batch writes in [9438](https://github.com/appwrite/appwrite/pull/9438) +* chore: bump cache 0.12.x in [9412](https://github.com/appwrite/appwrite/pull/9412) +* chore: queue console project for maintenance delete in [9479](https://github.com/appwrite/appwrite/pull/9479) +* chore: added logsdb for deletes worker in [9462](https://github.com/appwrite/appwrite/pull/9462) +* Feat: calculate and log time taken for each project in [9491](https://github.com/appwrite/appwrite/pull/9491) +* chore: update initializing dbForLogs in [9494](https://github.com/appwrite/appwrite/pull/9494) +* Feat bulk audit delete in [9487](https://github.com/appwrite/appwrite/pull/9487) +* Prepare 1.6.2 release in [9499](https://github.com/appwrite/appwrite/pull/9499) +* Regenerate specs in [9497](https://github.com/appwrite/appwrite/pull/9497) +* Regenerate examples in [9498](https://github.com/appwrite/appwrite/pull/9498) +* chore: bump sdk in [9414](https://github.com/appwrite/appwrite/pull/9414) +* update queue to 0.9.* in [9505](https://github.com/appwrite/appwrite/pull/9505) +* Feat improve delete queries in [9507](https://github.com/appwrite/appwrite/pull/9507) +* Feat: Add rule attributes in [9508](https://github.com/appwrite/appwrite/pull/9508) +* Sync main into 1.6.x in [9496](https://github.com/appwrite/appwrite/pull/9496) +* Bump console to version 5.2.53 in [9495](https://github.com/appwrite/appwrite/pull/9495) +* Prepare 1.6.1 release in [9294](https://github.com/appwrite/appwrite/pull/9294) +* Improve delete ordering in [9512](https://github.com/appwrite/appwrite/pull/9512) +* Cleanups in [9511](https://github.com/appwrite/appwrite/pull/9511) +* Feat dynamic regions in [9408](https://github.com/appwrite/appwrite/pull/9408) +* Feat env vars to system lib in [9515](https://github.com/appwrite/appwrite/pull/9515) +* Feat: domains count in [9514](https://github.com/appwrite/appwrite/pull/9514) +* Migration read from db in [9529](https://github.com/appwrite/appwrite/pull/9529) +* feat: add pool telemetry in [9530](https://github.com/appwrite/appwrite/pull/9530) +* Disable PDO persistence since we manage our own pool in [9526](https://github.com/appwrite/appwrite/pull/9526) +* chore: set min operations to 1 for reads and writes in [9536](https://github.com/appwrite/appwrite/pull/9536) +* Remove default region in [9430](https://github.com/appwrite/appwrite/pull/9430) +* Use cursor pagination with bigger limit for maintenance project loop in [9546](https://github.com/appwrite/appwrite/pull/9546) +* chore: stop tests on failure in [9525](https://github.com/appwrite/appwrite/pull/9525) +* chore: only update total count for privileged users in [9554](https://github.com/appwrite/appwrite/pull/9554) +* refactor: initialization of audit retention in [9563](https://github.com/appwrite/appwrite/pull/9563) +* Delete worker queries fixes in [9523](https://github.com/appwrite/appwrite/pull/9523) +* Bump database 0.62.x in [9568](https://github.com/appwrite/appwrite/pull/9568) +* Fix: schedules region filtering in [9577](https://github.com/appwrite/appwrite/pull/9577) +* Deletes worker fix selects for pagination in [9578](https://github.com/appwrite/appwrite/pull/9578) +* Add $permissions for delete documents selects in [9579](https://github.com/appwrite/appwrite/pull/9579) +* chore(audits): return queue pre-fetch results in [9533](https://github.com/appwrite/appwrite/pull/9533) +* Revert "chore(audits): return queue pre-fetch results" in [9586](https://github.com/appwrite/appwrite/pull/9586) +* Feat multi tenant insert in [9573](https://github.com/appwrite/appwrite/pull/9573) +* Add order by for cursor in [9588](https://github.com/appwrite/appwrite/pull/9588) +* Feat update fetch in [9592](https://github.com/appwrite/appwrite/pull/9592) +* Fix tenant casting in [9598](https://github.com/appwrite/appwrite/pull/9598) +* Feat update ws in [9602](https://github.com/appwrite/appwrite/pull/9602) +* Update database in [9603](https://github.com/appwrite/appwrite/pull/9603) +* Fix: image transformation cache in [9608](https://github.com/appwrite/appwrite/pull/9608) +* Remove audit payload in [9610](https://github.com/appwrite/appwrite/pull/9610) +* Sample rate from DSN in [9559](https://github.com/appwrite/appwrite/pull/9559) +* Restrict role change for sole org owner in [9615](https://github.com/appwrite/appwrite/pull/9615) +* chore: update php image to 0.8.1 in [9616](https://github.com/appwrite/appwrite/pull/9616) +* feat: refactor executor setup in [9420](https://github.com/appwrite/appwrite/pull/9420) +* chore: update gitpod.yml config in [9561](https://github.com/appwrite/appwrite/pull/9561) +* chore: update dependencies in [9625](https://github.com/appwrite/appwrite/pull/9625) +* Update migrations lib in [9628](https://github.com/appwrite/appwrite/pull/9628) +* feat: cache telemetry in [9624](https://github.com/appwrite/appwrite/pull/9624) +* Bump console to version 5.2.56 in [9631](https://github.com/appwrite/appwrite/pull/9631) +* Multi region support in [8667](https://github.com/appwrite/appwrite/pull/8667) +* Revert "Multi region support" in [9632](https://github.com/appwrite/appwrite/pull/9632) +* Revert "Revert "Multi region support"" in [9636](https://github.com/appwrite/appwrite/pull/9636) +* Fix tasks in [9644](https://github.com/appwrite/appwrite/pull/9644) +* chore: updated the migration version to 8.6 in [9646](https://github.com/appwrite/appwrite/pull/9646) +* Fix: merge the working of StatsUsage and StatsUsageDump in [9585](https://github.com/appwrite/appwrite/pull/9585) +* Update database in [9643](https://github.com/appwrite/appwrite/pull/9643) +* chore: fix error logging for CLI tasks in [9651](https://github.com/appwrite/appwrite/pull/9651) +* fix: usage test assertion in [9653](https://github.com/appwrite/appwrite/pull/9653) +* Fix keys in [9656](https://github.com/appwrite/appwrite/pull/9656) +* Feat: multi tenant dual writing in [9583](https://github.com/appwrite/appwrite/pull/9583) +* Fix/throwing 400 for null order attributes in [9657](https://github.com/appwrite/appwrite/pull/9657) +* feat: sdk group attribute in [9596](https://github.com/appwrite/appwrite/pull/9596) +* Add configurable function and build size in [9648](https://github.com/appwrite/appwrite/pull/9648) +* feat: update API endpoint in the code examples in [8933](https://github.com/appwrite/appwrite/pull/8933) +* chore: abstract token secret hiding to response model in [9574](https://github.com/appwrite/appwrite/pull/9574) +* chore: update sdks in [9655](https://github.com/appwrite/appwrite/pull/9655) +* feat: allow non-critical events to ignore exceptions when enqueuing the event in [9680](https://github.com/appwrite/appwrite/pull/9680) +* Revert "Add configurable function and build size" in [9681](https://github.com/appwrite/appwrite/pull/9681) +* core: introduce endpoint.docs in specs in [9685](https://github.com/appwrite/appwrite/pull/9685) +* fix: remove content-type header from get request specs in [9666](https://github.com/appwrite/appwrite/pull/9666) +* chore: update flutter sdk in [9691](https://github.com/appwrite/appwrite/pull/9691) + # Version 1.6.1 ## What's Changed diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 096c23ad92..0b1e462d8e 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -82,9 +82,9 @@ abstract class Migration '1.5.11' => 'V20', '1.6.0' => 'V21', '1.6.1' => 'V21', - '1.6.2' => 'V22', - '1.7.0-RC1' => 'V23', - '1.7.0' => 'V23', + '1.6.2' => 'V21', + '1.7.0-RC1' => 'V22', + '1.7.0' => 'V22', ]; /** @@ -385,6 +385,10 @@ abstract class Migration default => 'projects', }; + if ($from === 'files') { + $collectionType = 'buckets'; + } + $collection = $this->collections[$collectionType][$from] ?? null; if ($collection === null) { diff --git a/src/Appwrite/Migration/Version/V21.php b/src/Appwrite/Migration/Version/V21.php index 5a5a30bb6f..38e8a8d513 100644 --- a/src/Appwrite/Migration/Version/V21.php +++ b/src/Appwrite/Migration/Version/V21.php @@ -74,6 +74,27 @@ class V21 extends Migration Console::warning("'accessedAt' from {$id}: {$th->getMessage()}"); } break; + case 'rules': + $attributesToCreate = ['owner', 'region']; + foreach ($attributesToCreate as $attribute) { + // Create attribute + try { + $this->createAttributeFromCollection($this->dbForProject, $id, $attribute); + } catch (Throwable $th) { + Console::warning("'$attribute' from {$id}: {$th->getMessage()}"); + } + } + + $indexesToCreate = ['_key_owner', '_key_region']; + foreach ($indexesToCreate as $index) { + // Create index + try { + $this->createIndexFromCollection($this->dbForProject, $id, $index); + } catch (Throwable $th) { + Console::warning("'$index' from {$id}: {$th->getMessage()}"); + } + } + break; case 'platforms': // Increase 'type' length to 255 try { @@ -82,6 +103,17 @@ class V21 extends Migration Console::warning("'type' from {$id}: {$th->getMessage()}"); } break; + case 'installations': + $attributesToCreate = ['personalAccessToken', 'personalAccessTokenExpiry', 'personalRefreshToken']; + foreach ($attributesToCreate as $attribute) { + // Create attribute + try { + $this->createAttributeFromCollection($this->dbForProject, $id, $attribute); + } catch (Throwable $th) { + Console::warning("'$attribute' from {$id}: {$th->getMessage()}"); + } + } + break; case 'migrations': // Create destination attribute try { @@ -197,11 +229,15 @@ class V21 extends Migration $document->setAttribute('accessedAt', DateTime::now()); break; case 'functions': - // Add scopes attribute - $document->setAttribute('scopes', []); + // Set scopes attribute + if (empty($document->getAttribute('scopes', []))) { + $document->setAttribute('scopes', []); + } - // Add size attribute - $document->setAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT); + // Set specification attribute + if (empty($document->getAttribute('specification'))) { + $document->setAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT); + } } return $document; @@ -214,15 +250,34 @@ class V21 extends Migration */ private function migrateBuckets(): void { - foreach ($this->documentsIterator('buckets') as $bucket) { + $this->dbForProject->forEach('buckets', function (Document $bucket) { $bucketId = 'bucket_' . $bucket['$internalId']; + Console::log("Migrating Bucket {$bucketId} {$bucket->getId()} ({$bucket->getAttribute('name')})"); + try { $this->dbForProject->updateAttribute($bucketId, 'metadata', size: 65534); + } catch (\Throwable $th) { + Console::warning("'metadata' from {$bucketId}: {$th->getMessage()}"); + } + + try { + $this->createAttributeFromCollection($this->dbForProject, $bucketId, 'transformedAt', 'files'); + } catch (\Throwable $th) { + Console::warning("'transformedAt' from {$bucketId}: {$th->getMessage()}"); + } + + try { + $this->createIndexFromCollection($this->dbForProject, $bucketId, '_key_transformedAt', 'files'); + } catch (\Throwable $th) { + Console::warning("'_key_transformedAt' from {$bucketId}: {$th->getMessage()}"); + } + + try { $this->dbForProject->purgeCachedCollection($bucketId); } catch (\Throwable $th) { - Console::warning("'bucketId' from {$bucketId}: {$th->getMessage()}"); + Console::warning("purging {$bucketId}: {$th->getMessage()}"); } - } + }); } } diff --git a/src/Appwrite/Migration/Version/V22.php b/src/Appwrite/Migration/Version/V22.php index a5474ff80c..400b223756 100644 --- a/src/Appwrite/Migration/Version/V22.php +++ b/src/Appwrite/Migration/Version/V22.php @@ -7,8 +7,13 @@ use Exception; use Throwable; use Utopia\CLI\Console; use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Database\Exception\Conflict; +use Utopia\Database\Exception\Structure; +use Utopia\Database\Exception\Timeout; +use Utopia\Database\Query; -class V22 extends Migration +class V23 extends Migration { /** * @throws Throwable @@ -16,9 +21,9 @@ class V22 extends Migration public function execute(): void { /** - * Disable SubQueries for Performance. - */ - foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables', 'subQueryChallenges', 'subQueryProjectVariables', 'subQueryTargets', 'subQueryTopicTargets'] as $name) { + * Disable SubQueries for Performance. + */ + foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryDevKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables', 'subQueryChallenges', 'subQueryProjectVariables', 'subQueryTargets', 'subQueryTopicTargets'] as $name) { Database::addFilter( $name, fn () => null, @@ -26,8 +31,14 @@ class V22 extends Migration ); } - Console::info('Migrating Collections'); + Console::info('Migrating collections'); $this->migrateCollections(); + + Console::info('Migrating documents'); + $this->forEachDocument($this->migrateDocument(...)); + + Console::info('Cleaning up collections'); + $this->cleanCollections(); } /** @@ -38,8 +49,428 @@ class V22 extends Migration */ private function migrateCollections(): void { - $internalProjectId = $this->project->getInternalId(); - $collectionType = match ($internalProjectId) { + $projectInternalId = $this->project->getInternalId(); + + if (empty($projectInternalId)) { + throw new Exception('Project ID is null'); + } + + $collectionType = match ($projectInternalId) { + 'console' => 'console', + default => 'projects', + }; + + $collections = $this->collections[$collectionType]; + + foreach ($collections as $collection) { + $id = $collection['$id']; + + if (empty($id)) { + continue; + } + + Console::log("Migrating collection \"{$id}\""); + + switch ($id) { + case '_metadata': + $this->createCollection('sites'); + $this->createCollection('resourceTokens'); + if ($projectInternalId === 'console') { + $this->createCollection('devKeys'); + } + break; + case 'identities': + $attributes = [ + 'scopes', + 'expire', + ]; + try { + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + $this->dbForProject->purgeCachedCollection($id); + break; + case 'projects': + try { + $attributes = [ + 'devKeys', + ]; + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + $this->dbForProject->purgeCachedCollection($id); + break; + case 'rules': + $attributes = [ + 'type', + 'trigger', + 'redirectUrl', + 'redirectStatusCode', + 'deploymentResourceType', + 'deploymentId', + 'deploymentInternalId', + 'deploymentResourceId', + 'deploymentResourceInternalId', + 'deploymentVcsProviderBranch', + 'search' + ]; + try { + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + + $indexes = [ + '_key_search', + '_key_type', + '_key_trigger', + '_key_deploymentResourceType', + '_key_deploymentResourceId', + '_key_deploymentResourceInternalId', + '_key_deploymentId', + '_key_deploymentInternalId', + '_key_deploymentVcsProviderBranch', + ]; + + foreach ($indexes as $index) { + try { + $this->createIndexFromCollection($this->dbForProject, $id, $index); + } catch (\Throwable $th) { + Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); + } + } + $this->dbForProject->purgeCachedCollection($id); + break; + case 'memberships': + $indexes = [ + '_key_roles', + ]; + foreach ($indexes as $index) { + try { + $this->createIndexFromCollection($this->dbForProject, $id, $index); + } catch (Throwable $th) { + Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); + } + } + $this->dbForProject->purgeCachedCollection($id); + break; + case 'migrations': + $attributes = [ + 'options', + 'resourceId', + 'resourceType' + ]; + try { + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + + $indexes = [ + '_key_resource_id', + ]; + + foreach ($indexes as $index) { + try { + $this->createIndexFromCollection($this->dbForProject, $id, $index); + } catch (Throwable $th) { + Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + case 'functions': + $attributes = [ + 'deploymentId', + 'deploymentCreatedAt', + 'latestDeploymentId', + 'latestDeploymentInternalId', + 'latestDeploymentCreatedAt', + 'latestDeploymentStatus', + ]; + try { + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + + $indexes = [ + '_key_deploymentId', + ]; + + foreach ($indexes as $index) { + try { + $this->createIndexFromCollection($this->dbForProject, $id, $index); + } catch (Throwable $th) { + Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + case 'deployments': + $attributes = [ + 'buildCommands', + 'sourcePath', + 'buildOutput', + 'adapter', + 'fallbackFile', + 'sourceSize', + 'sourceMetadata', + 'sourceChunksTotal', + 'sourceChunksUploaded', + 'screenshotLight', + 'screenshotDark', + 'buildStartedAt', + 'buildEndedAt', + 'buildDuration', + 'buildSize', + 'status', + 'buildPath', + 'buildLogs', + 'totalSize', + ]; + try { + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + + $indexes = [ + '_key_sourceSize', + '_key_buildSize', + '_key_totalSize', + '_key_buildDuration', + '_key_type', + '_key_status', + ]; + + foreach ($indexes as $index) { + try { + $this->createIndexFromCollection($this->dbForProject, $id, $index); + } catch (\Throwable $th) { + Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + case 'executions': + $attributes = [ + 'resourceInternalId', + 'resourceId', + 'resourceType' + ]; + try { + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + + $indexes = [ + '_key_resource', + ]; + foreach ($indexes as $index) { + try { + $this->createIndexFromCollection($this->dbForProject, $id, $index); + } catch (\Throwable $th) { + Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + case 'variables': + $attributes = [ + 'secret', + ]; + try { + $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); + } catch (\Throwable $th) { + Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); + } + + $this->dbForProject->purgeCachedCollection($id); + break; + default: + break; + } + } + } + + /** + * Fix run on each document + * + * @param Document $document + * @return Document + * @throws Conflict + * @throws Structure + * @throws Timeout + * @throws \Utopia\Database\Exception + * @throws \Utopia\Database\Exception\Authorization + * @throws \Utopia\Database\Exception\Query + */ + private function migrateDocument(Document $document): Document + { + switch ($document->getCollection()) { + case 'rules': + /* + 1. Convert "resourceType" to "type". Convert "function" to "deployment" + 2. Convert "resourceId" to "deploymentResourceId" + 3. Convert "resourceInternalId" to "deploymentResourceInternalId" + 4. Fill "trigger" with "manual" + 5. Fill "deploymentResourceType". If "resourceType" is "function", set "deploymentResourceType" to "function" + 6. Fill "search" with "{$id} {domain}" + 7. Fill "deploymentId" and "deploymentInternalId". If "deploymentResourceType" is "function", get project DB, and find function with ID "resourceId". Then fill rule's "deploymentId" with function's "deployment", and "deploymentId" as backup + */ + + $deploymentResourceType = null; + + $type = $document->getAttribute('resourceType', $document->getAttribute('type', '')); + if ($type === 'function') { + $type = 'deployment'; + $deploymentResourceType = 'function'; + } + + $resourceId = $document->getAttribute('resourceId', $document->getAttribute('deploymentResourceId')); + $resourceInternalId = $document->getAttribute('resourceInternalId', $document->getAttribute('deploymentResourceInternalId')); + + $document + ->setAttribute('type', $type) + ->setAttribute('trigger', 'manual') + ->setAttribute('deploymentResourceId', $resourceId) + ->setAttribute('deploymentResourceInternalId', $resourceInternalId) + ->setAttribute('deploymentResourceType', $document->getAttribute('deploymentResourceType', $deploymentResourceType)) + ->setAttribute('search', \implode(' ', [$document->getId(), $document->getAttribute('domain', '')])); + + if ($deploymentResourceType === 'function') { + $project = $this->dbForProject->getDocument('projects', $document->getAttribute('projectId')); + $dbForOwnerProject = ($this->getProjectDB)($project); + $function = $dbForOwnerProject->getDocument('functions', $resourceId); + $deploymentId = $function->getAttribute('deployment', $function->getAttribute('deploymentId', $document->getAttribute('deploymentId'))); + $deploymentInternalId = $function->getAttribute('deploymentInternalId', $document->getAttribute('deploymentInternalId', '')); + + $document + ->setAttribute('deploymentId', $deploymentId) + ->setAttribute('deploymentInternalId', $deploymentInternalId); + } + break; + case 'variables': + /* + 1. Fill "secret" with "false" + */ + $document->setAttribute('secret', $document->getAttribute('secret', false)); + break; + case 'executions': + /* + 1. Convert "functionInternalId" to "resourceInternalId" + 2. Convert "functionId" to "resourceId" + 3. Fill "resourceType" with "functions" + */ + $document + ->setAttribute('resourceInternalId', $document->getAttribute('functionInternalId', $document->getAttribute('resourceInternalId'))) + ->setAttribute('resourceId', $document->getAttribute('functionId', $document->getAttribute('resourceId', ''))) + ->setAttribute('resourceType', $document->getAttribute('resourceType', 'functions')); + break; + case 'functions': + /* + 1. Convert "deployment" to "deploymentId" + --- Fetch activeDeployment from "deploymentId" + 2. Fill "deploymentCreatedAt" with deployment's "$createdAt" + --- Fetch latestDeployment using find() + 3. Fill latestDeploymentId with latestDeployment's "$id" + 4. Fill latestDeploymentInternalId with latestDeployment's "$internalId" + 5. Fill latestDeploymentCreatedAt with latestDeployment's "$createdAt" + 6. Fill latestDeploymentStatus with latestDeployment's build's "status" + */ + if ($document->getAttribute('deployment')) { + $document->setAttribute('deploymentId', $document->getAttribute('deployment', $document->getAttribute('deploymentId', ''))); + } + + $deploymentId = $document->getAttribute('deploymentId'); + $deployment = $this->dbForProject->getDocument('deployments', $deploymentId); + $document->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt()); + + $latestDeployment = $this->dbForProject->findOne('deployments', [ + Query::orderDesc(), + Query::equal('resourceId', [$document->getId()]), + Query::equal('resourceType', ['functions']), + ]); + + $latestBuild = $this->dbForProject->getDocument('builds', $latestDeployment->getAttribute('buildId', '')); + + $document + ->setAttribute('latestDeploymentId', $latestDeployment->getId()) + ->setAttribute('latestDeploymentInternalId', $latestDeployment->getInternalId()) + ->setAttribute('latestDeploymentCreatedAt', $latestDeployment->getCreatedAt()) + ->setAttribute('latestDeploymentStatus', $latestBuild->getAttribute('status', $document->getAttribute('latestDeploymentStatus', ''))); + break; + case 'deployments': + /* + 6. Convert "commands" to "buildCommands" + 7. Convert "path" to "sourcePath" + 8. Convert "size" to "sourceSize" + 9. Convert "metadata" to "sourceMetadata" + 10. Convert "chunksTotal" to "sourceChunksTotal" + 11. Convert "chunksUploaded" to "sourceChunksUploaded" + --- Get build of deployment + 12. Convert build's "startTime" to "buildStartedAt" + 13. Convert build's "endTime" to "buildEndedAt" + 14. Convert build's "duration" to "buildDuration" + 15. Convert build's "size" to "buildSize" + 16. Convert build's "status" to "status" + 17. Convert build's "path" to "buildPath" + 18. Convert build's "logs" to "buildLogs" + 19. Fill "totalSize" with "buildSize" plus "sourceSize" + */ + + $document + ->setAttribute('buildCommands', $document->getAttribute('commands', $document->getAttribute('buildCommands', ''))) + ->setAttribute('sourcePath', $document->getAttribute('path', $document->getAttribute('sourcePath', ''))) + ->setAttribute('sourceSize', $document->getAttribute('size', $document->getAttribute('sourceSize', 0))) + ->setAttribute('sourceMetadata', $document->getAttribute('metadata', $document->getAttribute('sourceMetadata', []))) + ->setAttribute('sourceChunksTotal', $document->getAttribute('chunksTotal', $document->getAttribute('sourceChunksTotal', 0))) + ->setAttribute('sourceChunksUploaded', $document->getAttribute('chunksUploaded', $document->getAttribute('sourceChunksUploaded', 0))); + + $build = new Document(); + if (!empty($document->getAttribute('buildId'))) { + $build = $this->dbForProject->getDocument('builds', $document->getAttribute('buildId')); + } + + $document + ->setAttribute('buildStartedAt', $build->getAttribute('startTime', $document->getAttribute('buildStartTime', ''))) + ->setAttribute('buildEndedAt', $build->getAttribute('endTime', $document->getAttribute('buildEndTime', ''))) + ->setAttribute('buildDuration', $build->getAttribute('duration', $document->getAttribute('buildDuration', 0))) + ->setAttribute('buildSize', $build->getAttribute('size', $document->getAttribute('buildSize', 0))) + ->setAttribute('status', $build->getAttribute('status', $document->getAttribute('status', ''))) + ->setAttribute('buildPath', $build->getAttribute('path', $document->getAttribute('buildPath', ''))) + ->setAttribute('buildLogs', $build->getAttribute('logs', $document->getAttribute('buildLogs', ''))); + + $totalSize = $document->getAttribute('buildSize', 0) + + $document->getAttribute('sourceSize', 0); + + $document->setAttribute('totalSize', $totalSize); + break; + case 'migrations': + /* + 1. Fill "options" with "[]" + */ + $document->setAttribute('options', $document->getAttribute('options', [])); + break; + default: + break; + } + return $document; + } + + private function cleanCollections(): void + { + $projectInternalId = $this->project->getInternalId(); + + $collectionType = match ($projectInternalId) { 'console' => 'console', default => 'projects', }; @@ -48,36 +479,129 @@ class V22 extends Migration foreach ($collections as $collection) { $id = $collection['$id']; - Console::log("Migrating Collection \"{$id}\""); - - $this->dbForProject->setNamespace("_$internalProjectId"); + Console::log("Cleaning up collection \"{$id}\""); switch ($id) { - case 'installations': - // Create personalAccessToken attribute - try { - $this->createAttributeFromCollection($this->dbForProject, $id, 'personalAccessToken'); - } catch (Throwable $th) { - Console::warning("'personalAccessToken' from {$id}: {$th->getMessage()}"); - } - - // Create personalAccessTokenExpiry attribute - try { - $this->createAttributeFromCollection($this->dbForProject, $id, 'personalAccessTokenExpiry'); - } catch (Throwable $th) { - Console::warning("'personalAccessTokenExpiry' from {$id}: {$th->getMessage()}"); - } - - // Create personalRefreshToken attribute - try { - $this->createAttributeFromCollection($this->dbForProject, $id, 'personalRefreshToken'); - } catch (Throwable $th) { - Console::warning("'personalRefreshToken' from {$id}: {$th->getMessage()}"); + case '_metadata': + if (!$this->dbForProject->getCollection('builds')->isEmpty()) { + $this->dbForProject->deleteCollection('builds'); } break; - } + case 'rules': + $attributes = [ + 'resourceId', + 'resourceInternalId', + 'resourceType', + ]; + foreach ($attributes as $attribute) { + try { + $this->dbForProject->deleteAttribute($id, $attribute); + } catch (\Throwable $th) { + Console::warning("Failed to delete attribute \"$attribute\" from collection {$id}: {$th->getMessage()}"); + } + } - usleep(50000); + $indexesToDelete = [ + '_key_resourceId', + '_key_resourceInternalId', + '_key_resourceType', + ]; + foreach ($indexesToDelete as $index) { + try { + $this->dbForProject->deleteIndex($id, $index); + } catch (\Throwable $th) { + Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + case 'functions': + try { + $this->dbForProject->deleteAttribute($id, 'deployment'); + } catch (\Throwable $th) { + Console::warning("Failed to delete attribute \"deployment\" from collection {$id}: {$th->getMessage()}"); + } + + $indexesToDelete = [ + '_key_deployment' + ]; + foreach ($indexesToDelete as $index) { + try { + $this->dbForProject->deleteIndex($id, $index); + } catch (\Throwable $th) { + Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + case 'deployments': + $attributes = [ + 'buildInternalId', + 'buildId', + 'commands', + 'path', + 'size', + 'metadata', + 'chunksTotal', + 'chunksUploaded', + 'search' + ]; + foreach ($attributes as $attribute) { + try { + $this->dbForProject->deleteAttribute($id, $attribute); + } catch (\Throwable $th) { + Console::warning("Failed to delete attribute \"$attribute\" from collection {$id}: {$th->getMessage()}"); + } + } + + $indexesToDelete = [ + '_key_buildId', + '_key_size', + '_key_search' + ]; + foreach ($indexesToDelete as $index) { + try { + $this->dbForProject->deleteIndex($id, $index); + } catch (\Throwable $th) { + Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + case 'executions': + $attributes = [ + 'functionId', + 'functionInternalId', + 'search' + ]; + foreach ($attributes as $attribute) { + try { + $this->dbForProject->deleteAttribute($id, $attribute); + } catch (\Throwable $th) { + Console::warning("Failed to delete attribute \"$attribute\" from collection {$id}: {$th->getMessage()}"); + } + } + + $indexesToDelete = [ + '_key_function', + '_fulltext_search' + ]; + foreach ($indexesToDelete as $index) { + try { + $this->dbForProject->deleteIndex($id, $index); + } catch (\Throwable $th) { + Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); + } + } + + $this->dbForProject->purgeCachedCollection($id); + break; + default: + break; + } } } } diff --git a/src/Appwrite/Migration/Version/V23.php b/src/Appwrite/Migration/Version/V23.php deleted file mode 100644 index 400b223756..0000000000 --- a/src/Appwrite/Migration/Version/V23.php +++ /dev/null @@ -1,607 +0,0 @@ - null, - fn () => [] - ); - } - - Console::info('Migrating collections'); - $this->migrateCollections(); - - Console::info('Migrating documents'); - $this->forEachDocument($this->migrateDocument(...)); - - Console::info('Cleaning up collections'); - $this->cleanCollections(); - } - - /** - * Migrate Collections. - * - * @return void - * @throws Exception|Throwable - */ - private function migrateCollections(): void - { - $projectInternalId = $this->project->getInternalId(); - - if (empty($projectInternalId)) { - throw new Exception('Project ID is null'); - } - - $collectionType = match ($projectInternalId) { - 'console' => 'console', - default => 'projects', - }; - - $collections = $this->collections[$collectionType]; - - foreach ($collections as $collection) { - $id = $collection['$id']; - - if (empty($id)) { - continue; - } - - Console::log("Migrating collection \"{$id}\""); - - switch ($id) { - case '_metadata': - $this->createCollection('sites'); - $this->createCollection('resourceTokens'); - if ($projectInternalId === 'console') { - $this->createCollection('devKeys'); - } - break; - case 'identities': - $attributes = [ - 'scopes', - 'expire', - ]; - try { - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - $this->dbForProject->purgeCachedCollection($id); - break; - case 'projects': - try { - $attributes = [ - 'devKeys', - ]; - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - $this->dbForProject->purgeCachedCollection($id); - break; - case 'rules': - $attributes = [ - 'type', - 'trigger', - 'redirectUrl', - 'redirectStatusCode', - 'deploymentResourceType', - 'deploymentId', - 'deploymentInternalId', - 'deploymentResourceId', - 'deploymentResourceInternalId', - 'deploymentVcsProviderBranch', - 'search' - ]; - try { - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - - $indexes = [ - '_key_search', - '_key_type', - '_key_trigger', - '_key_deploymentResourceType', - '_key_deploymentResourceId', - '_key_deploymentResourceInternalId', - '_key_deploymentId', - '_key_deploymentInternalId', - '_key_deploymentVcsProviderBranch', - ]; - - foreach ($indexes as $index) { - try { - $this->createIndexFromCollection($this->dbForProject, $id, $index); - } catch (\Throwable $th) { - Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); - } - } - $this->dbForProject->purgeCachedCollection($id); - break; - case 'memberships': - $indexes = [ - '_key_roles', - ]; - foreach ($indexes as $index) { - try { - $this->createIndexFromCollection($this->dbForProject, $id, $index); - } catch (Throwable $th) { - Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); - } - } - $this->dbForProject->purgeCachedCollection($id); - break; - case 'migrations': - $attributes = [ - 'options', - 'resourceId', - 'resourceType' - ]; - try { - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - - $indexes = [ - '_key_resource_id', - ]; - - foreach ($indexes as $index) { - try { - $this->createIndexFromCollection($this->dbForProject, $id, $index); - } catch (Throwable $th) { - Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - case 'functions': - $attributes = [ - 'deploymentId', - 'deploymentCreatedAt', - 'latestDeploymentId', - 'latestDeploymentInternalId', - 'latestDeploymentCreatedAt', - 'latestDeploymentStatus', - ]; - try { - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - - $indexes = [ - '_key_deploymentId', - ]; - - foreach ($indexes as $index) { - try { - $this->createIndexFromCollection($this->dbForProject, $id, $index); - } catch (Throwable $th) { - Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - case 'deployments': - $attributes = [ - 'buildCommands', - 'sourcePath', - 'buildOutput', - 'adapter', - 'fallbackFile', - 'sourceSize', - 'sourceMetadata', - 'sourceChunksTotal', - 'sourceChunksUploaded', - 'screenshotLight', - 'screenshotDark', - 'buildStartedAt', - 'buildEndedAt', - 'buildDuration', - 'buildSize', - 'status', - 'buildPath', - 'buildLogs', - 'totalSize', - ]; - try { - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - - $indexes = [ - '_key_sourceSize', - '_key_buildSize', - '_key_totalSize', - '_key_buildDuration', - '_key_type', - '_key_status', - ]; - - foreach ($indexes as $index) { - try { - $this->createIndexFromCollection($this->dbForProject, $id, $index); - } catch (\Throwable $th) { - Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - case 'executions': - $attributes = [ - 'resourceInternalId', - 'resourceId', - 'resourceType' - ]; - try { - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - - $indexes = [ - '_key_resource', - ]; - foreach ($indexes as $index) { - try { - $this->createIndexFromCollection($this->dbForProject, $id, $index); - } catch (\Throwable $th) { - Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - case 'variables': - $attributes = [ - 'secret', - ]; - try { - $this->createAttributesFromCollection($this->dbForProject, $id, $attributes); - } catch (\Throwable $th) { - Console::warning('Failed to create attributes "' . \implode(', ', $attributes) . "\" in collection {$id}: {$th->getMessage()}"); - } - - $this->dbForProject->purgeCachedCollection($id); - break; - default: - break; - } - } - } - - /** - * Fix run on each document - * - * @param Document $document - * @return Document - * @throws Conflict - * @throws Structure - * @throws Timeout - * @throws \Utopia\Database\Exception - * @throws \Utopia\Database\Exception\Authorization - * @throws \Utopia\Database\Exception\Query - */ - private function migrateDocument(Document $document): Document - { - switch ($document->getCollection()) { - case 'rules': - /* - 1. Convert "resourceType" to "type". Convert "function" to "deployment" - 2. Convert "resourceId" to "deploymentResourceId" - 3. Convert "resourceInternalId" to "deploymentResourceInternalId" - 4. Fill "trigger" with "manual" - 5. Fill "deploymentResourceType". If "resourceType" is "function", set "deploymentResourceType" to "function" - 6. Fill "search" with "{$id} {domain}" - 7. Fill "deploymentId" and "deploymentInternalId". If "deploymentResourceType" is "function", get project DB, and find function with ID "resourceId". Then fill rule's "deploymentId" with function's "deployment", and "deploymentId" as backup - */ - - $deploymentResourceType = null; - - $type = $document->getAttribute('resourceType', $document->getAttribute('type', '')); - if ($type === 'function') { - $type = 'deployment'; - $deploymentResourceType = 'function'; - } - - $resourceId = $document->getAttribute('resourceId', $document->getAttribute('deploymentResourceId')); - $resourceInternalId = $document->getAttribute('resourceInternalId', $document->getAttribute('deploymentResourceInternalId')); - - $document - ->setAttribute('type', $type) - ->setAttribute('trigger', 'manual') - ->setAttribute('deploymentResourceId', $resourceId) - ->setAttribute('deploymentResourceInternalId', $resourceInternalId) - ->setAttribute('deploymentResourceType', $document->getAttribute('deploymentResourceType', $deploymentResourceType)) - ->setAttribute('search', \implode(' ', [$document->getId(), $document->getAttribute('domain', '')])); - - if ($deploymentResourceType === 'function') { - $project = $this->dbForProject->getDocument('projects', $document->getAttribute('projectId')); - $dbForOwnerProject = ($this->getProjectDB)($project); - $function = $dbForOwnerProject->getDocument('functions', $resourceId); - $deploymentId = $function->getAttribute('deployment', $function->getAttribute('deploymentId', $document->getAttribute('deploymentId'))); - $deploymentInternalId = $function->getAttribute('deploymentInternalId', $document->getAttribute('deploymentInternalId', '')); - - $document - ->setAttribute('deploymentId', $deploymentId) - ->setAttribute('deploymentInternalId', $deploymentInternalId); - } - break; - case 'variables': - /* - 1. Fill "secret" with "false" - */ - $document->setAttribute('secret', $document->getAttribute('secret', false)); - break; - case 'executions': - /* - 1. Convert "functionInternalId" to "resourceInternalId" - 2. Convert "functionId" to "resourceId" - 3. Fill "resourceType" with "functions" - */ - $document - ->setAttribute('resourceInternalId', $document->getAttribute('functionInternalId', $document->getAttribute('resourceInternalId'))) - ->setAttribute('resourceId', $document->getAttribute('functionId', $document->getAttribute('resourceId', ''))) - ->setAttribute('resourceType', $document->getAttribute('resourceType', 'functions')); - break; - case 'functions': - /* - 1. Convert "deployment" to "deploymentId" - --- Fetch activeDeployment from "deploymentId" - 2. Fill "deploymentCreatedAt" with deployment's "$createdAt" - --- Fetch latestDeployment using find() - 3. Fill latestDeploymentId with latestDeployment's "$id" - 4. Fill latestDeploymentInternalId with latestDeployment's "$internalId" - 5. Fill latestDeploymentCreatedAt with latestDeployment's "$createdAt" - 6. Fill latestDeploymentStatus with latestDeployment's build's "status" - */ - if ($document->getAttribute('deployment')) { - $document->setAttribute('deploymentId', $document->getAttribute('deployment', $document->getAttribute('deploymentId', ''))); - } - - $deploymentId = $document->getAttribute('deploymentId'); - $deployment = $this->dbForProject->getDocument('deployments', $deploymentId); - $document->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt()); - - $latestDeployment = $this->dbForProject->findOne('deployments', [ - Query::orderDesc(), - Query::equal('resourceId', [$document->getId()]), - Query::equal('resourceType', ['functions']), - ]); - - $latestBuild = $this->dbForProject->getDocument('builds', $latestDeployment->getAttribute('buildId', '')); - - $document - ->setAttribute('latestDeploymentId', $latestDeployment->getId()) - ->setAttribute('latestDeploymentInternalId', $latestDeployment->getInternalId()) - ->setAttribute('latestDeploymentCreatedAt', $latestDeployment->getCreatedAt()) - ->setAttribute('latestDeploymentStatus', $latestBuild->getAttribute('status', $document->getAttribute('latestDeploymentStatus', ''))); - break; - case 'deployments': - /* - 6. Convert "commands" to "buildCommands" - 7. Convert "path" to "sourcePath" - 8. Convert "size" to "sourceSize" - 9. Convert "metadata" to "sourceMetadata" - 10. Convert "chunksTotal" to "sourceChunksTotal" - 11. Convert "chunksUploaded" to "sourceChunksUploaded" - --- Get build of deployment - 12. Convert build's "startTime" to "buildStartedAt" - 13. Convert build's "endTime" to "buildEndedAt" - 14. Convert build's "duration" to "buildDuration" - 15. Convert build's "size" to "buildSize" - 16. Convert build's "status" to "status" - 17. Convert build's "path" to "buildPath" - 18. Convert build's "logs" to "buildLogs" - 19. Fill "totalSize" with "buildSize" plus "sourceSize" - */ - - $document - ->setAttribute('buildCommands', $document->getAttribute('commands', $document->getAttribute('buildCommands', ''))) - ->setAttribute('sourcePath', $document->getAttribute('path', $document->getAttribute('sourcePath', ''))) - ->setAttribute('sourceSize', $document->getAttribute('size', $document->getAttribute('sourceSize', 0))) - ->setAttribute('sourceMetadata', $document->getAttribute('metadata', $document->getAttribute('sourceMetadata', []))) - ->setAttribute('sourceChunksTotal', $document->getAttribute('chunksTotal', $document->getAttribute('sourceChunksTotal', 0))) - ->setAttribute('sourceChunksUploaded', $document->getAttribute('chunksUploaded', $document->getAttribute('sourceChunksUploaded', 0))); - - $build = new Document(); - if (!empty($document->getAttribute('buildId'))) { - $build = $this->dbForProject->getDocument('builds', $document->getAttribute('buildId')); - } - - $document - ->setAttribute('buildStartedAt', $build->getAttribute('startTime', $document->getAttribute('buildStartTime', ''))) - ->setAttribute('buildEndedAt', $build->getAttribute('endTime', $document->getAttribute('buildEndTime', ''))) - ->setAttribute('buildDuration', $build->getAttribute('duration', $document->getAttribute('buildDuration', 0))) - ->setAttribute('buildSize', $build->getAttribute('size', $document->getAttribute('buildSize', 0))) - ->setAttribute('status', $build->getAttribute('status', $document->getAttribute('status', ''))) - ->setAttribute('buildPath', $build->getAttribute('path', $document->getAttribute('buildPath', ''))) - ->setAttribute('buildLogs', $build->getAttribute('logs', $document->getAttribute('buildLogs', ''))); - - $totalSize = $document->getAttribute('buildSize', 0) - + $document->getAttribute('sourceSize', 0); - - $document->setAttribute('totalSize', $totalSize); - break; - case 'migrations': - /* - 1. Fill "options" with "[]" - */ - $document->setAttribute('options', $document->getAttribute('options', [])); - break; - default: - break; - } - return $document; - } - - private function cleanCollections(): void - { - $projectInternalId = $this->project->getInternalId(); - - $collectionType = match ($projectInternalId) { - 'console' => 'console', - default => 'projects', - }; - - $collections = $this->collections[$collectionType]; - foreach ($collections as $collection) { - $id = $collection['$id']; - - Console::log("Cleaning up collection \"{$id}\""); - - switch ($id) { - case '_metadata': - if (!$this->dbForProject->getCollection('builds')->isEmpty()) { - $this->dbForProject->deleteCollection('builds'); - } - break; - case 'rules': - $attributes = [ - 'resourceId', - 'resourceInternalId', - 'resourceType', - ]; - foreach ($attributes as $attribute) { - try { - $this->dbForProject->deleteAttribute($id, $attribute); - } catch (\Throwable $th) { - Console::warning("Failed to delete attribute \"$attribute\" from collection {$id}: {$th->getMessage()}"); - } - } - - $indexesToDelete = [ - '_key_resourceId', - '_key_resourceInternalId', - '_key_resourceType', - ]; - foreach ($indexesToDelete as $index) { - try { - $this->dbForProject->deleteIndex($id, $index); - } catch (\Throwable $th) { - Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - case 'functions': - try { - $this->dbForProject->deleteAttribute($id, 'deployment'); - } catch (\Throwable $th) { - Console::warning("Failed to delete attribute \"deployment\" from collection {$id}: {$th->getMessage()}"); - } - - $indexesToDelete = [ - '_key_deployment' - ]; - foreach ($indexesToDelete as $index) { - try { - $this->dbForProject->deleteIndex($id, $index); - } catch (\Throwable $th) { - Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - case 'deployments': - $attributes = [ - 'buildInternalId', - 'buildId', - 'commands', - 'path', - 'size', - 'metadata', - 'chunksTotal', - 'chunksUploaded', - 'search' - ]; - foreach ($attributes as $attribute) { - try { - $this->dbForProject->deleteAttribute($id, $attribute); - } catch (\Throwable $th) { - Console::warning("Failed to delete attribute \"$attribute\" from collection {$id}: {$th->getMessage()}"); - } - } - - $indexesToDelete = [ - '_key_buildId', - '_key_size', - '_key_search' - ]; - foreach ($indexesToDelete as $index) { - try { - $this->dbForProject->deleteIndex($id, $index); - } catch (\Throwable $th) { - Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - case 'executions': - $attributes = [ - 'functionId', - 'functionInternalId', - 'search' - ]; - foreach ($attributes as $attribute) { - try { - $this->dbForProject->deleteAttribute($id, $attribute); - } catch (\Throwable $th) { - Console::warning("Failed to delete attribute \"$attribute\" from collection {$id}: {$th->getMessage()}"); - } - } - - $indexesToDelete = [ - '_key_function', - '_fulltext_search' - ]; - foreach ($indexesToDelete as $index) { - try { - $this->dbForProject->deleteIndex($id, $index); - } catch (\Throwable $th) { - Console::warning("Failed to delete index \"$index\" from collection {$id}: {$th->getMessage()}"); - } - } - - $this->dbForProject->purgeCachedCollection($id); - break; - default: - break; - } - } - } -}