Merge branch 'feat-bulk-operations' into feat-bulk-deletes

# Conflicts:
#	app/config/specs/open-api3-latest-client.json
#	app/config/specs/open-api3-latest-console.json
#	app/config/specs/open-api3-latest-server.json
#	app/config/specs/swagger2-latest-client.json
#	app/config/specs/swagger2-latest-console.json
#	app/config/specs/swagger2-latest-server.json
#	src/Appwrite/Utopia/Response/Model/BulkOperation.php
This commit is contained in:
Bradley Schofield
2024-11-11 12:41:24 +09:00
49 changed files with 2744 additions and 1758 deletions
+1
View File
@@ -2,6 +2,7 @@ _APP_ENV=development
_APP_EDITION=self-hosted
_APP_LOCALE=en
_APP_WORKER_PER_CORE=6
_APP_COMPRESSION_MIN_SIZE_BYTES=1024
_APP_CONSOLE_WHITELIST_ROOT=disabled
_APP_CONSOLE_WHITELIST_EMAILS=
_APP_CONSOLE_SESSION_ALERTS=enabled
+3 -3
View File
@@ -92,10 +92,10 @@ RUN chmod +x /usr/local/bin/doctor && \
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
# Enable Extensions
RUN if [ "$DEBUG" == "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" == "true" ]; then mkdir -p /tmp/xdebug; fi
RUN if [ "$DEBUG" = "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" = "true" ]; then mkdir -p /tmp/xdebug; fi
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so; fi
EXPOSE 80
+41 -35
View File
@@ -4945,7 +4945,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5033,7 +5033,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 304,
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5150,7 +5150,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5226,7 +5226,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 330,
"weight": 331,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5280,7 +5280,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 329,
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5766,7 +5766,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5851,7 +5851,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 385,
"weight": 386,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5928,7 +5928,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6016,7 +6016,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 206,
"weight": 207,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6116,7 +6116,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6190,7 +6190,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 213,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6281,7 +6281,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 214,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6350,7 +6350,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6419,7 +6419,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6637,7 +6637,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 211,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6713,7 +6713,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6791,7 +6791,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 217,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6878,7 +6878,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6942,7 +6942,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7018,7 +7018,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7069,7 +7069,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.",
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Memberships List",
@@ -7084,7 +7084,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7172,7 +7172,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7270,7 +7270,7 @@
"tags": [
"teams"
],
"description": "Get a team member by the membership unique id. All team members have read access for this resource.",
"description": "Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Membership",
@@ -7285,7 +7285,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7359,7 +7359,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7448,7 +7448,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 229,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7524,7 +7524,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7624,7 +7624,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7687,7 +7687,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9266,12 +9266,12 @@
},
"userName": {
"type": "string",
"description": "User name.",
"description": "User name. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "John Doe"
},
"userEmail": {
"type": "string",
"description": "User email address.",
"description": "User email address. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "john@appwrite.io"
},
"teamId": {
@@ -9301,7 +9301,7 @@
},
"mfa": {
"type": "boolean",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise.",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.",
"x-example": false
},
"roles": {
@@ -9826,7 +9826,7 @@
"name": {
"type": "string",
"description": "Target Name.",
"x-example": "Aegon apple token"
"x-example": "Apple iPhone 12"
},
"userId": {
"type": "string",
@@ -9848,6 +9848,11 @@
"type": "string",
"description": "The target identifier.",
"x-example": "token"
},
"expired": {
"type": "boolean",
"description": "Is the target expired.",
"x-example": false
}
},
"required": [
@@ -9857,7 +9862,8 @@
"name",
"userId",
"providerType",
"identifier"
"identifier",
"expired"
]
}
},
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33 -33
View File
@@ -5031,7 +5031,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5119,7 +5119,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5236,7 +5236,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 307,
"weight": 308,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5312,7 +5312,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 331,
"weight": 332,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5366,7 +5366,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 330,
"weight": 331,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5852,7 +5852,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 382,
"weight": 383,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5937,7 +5937,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 386,
"weight": 387,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6014,7 +6014,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6102,7 +6102,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6202,7 +6202,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6276,7 +6276,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 214,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6367,7 +6367,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 215,
"weight": 216,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6436,7 +6436,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 211,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6505,7 +6505,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6723,7 +6723,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 212,
"weight": 213,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6799,7 +6799,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6877,7 +6877,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6964,7 +6964,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7028,7 +7028,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7104,7 +7104,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7155,7 +7155,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.",
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Memberships List",
@@ -7170,7 +7170,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7258,7 +7258,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7356,7 +7356,7 @@
"tags": [
"teams"
],
"description": "Get a team member by the membership unique id. All team members have read access for this resource.",
"description": "Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Membership",
@@ -7371,7 +7371,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7445,7 +7445,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7534,7 +7534,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 230,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7610,7 +7610,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 229,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7710,7 +7710,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7773,7 +7773,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9352,12 +9352,12 @@
},
"userName": {
"type": "string",
"description": "User name.",
"description": "User name. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "John Doe"
},
"userEmail": {
"type": "string",
"description": "User email address.",
"description": "User email address. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "john@appwrite.io"
},
"teamId": {
@@ -9387,7 +9387,7 @@
},
"mfa": {
"type": "boolean",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise.",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.",
"x-example": false
},
"roles": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+41 -35
View File
@@ -5101,7 +5101,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5186,7 +5186,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 304,
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5307,7 +5307,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5381,7 +5381,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 330,
"weight": 331,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5457,7 +5457,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 329,
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5981,7 +5981,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6070,7 +6070,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 385,
"weight": 386,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6145,7 +6145,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6230,7 +6230,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 206,
"weight": 207,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6324,7 +6324,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6396,7 +6396,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 213,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6487,7 +6487,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 214,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6561,7 +6561,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6635,7 +6635,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6836,7 +6836,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 211,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6910,7 +6910,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6987,7 +6987,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 217,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7081,7 +7081,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7145,7 +7145,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7222,7 +7222,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7277,7 +7277,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.",
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Memberships List",
@@ -7288,7 +7288,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7373,7 +7373,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7479,7 +7479,7 @@
"tags": [
"teams"
],
"description": "Get a team member by the membership unique id. All team members have read access for this resource.",
"description": "Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Membership",
@@ -7490,7 +7490,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7562,7 +7562,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7650,7 +7650,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 229,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7724,7 +7724,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7822,7 +7822,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7885,7 +7885,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9445,12 +9445,12 @@
},
"userName": {
"type": "string",
"description": "User name.",
"description": "User name. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "John Doe"
},
"userEmail": {
"type": "string",
"description": "User email address.",
"description": "User email address. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "john@appwrite.io"
},
"teamId": {
@@ -9480,7 +9480,7 @@
},
"mfa": {
"type": "boolean",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise.",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.",
"x-example": false
},
"roles": {
@@ -10008,7 +10008,7 @@
"name": {
"type": "string",
"description": "Target Name.",
"x-example": "Aegon apple token"
"x-example": "Apple iPhone 12"
},
"userId": {
"type": "string",
@@ -10030,6 +10030,11 @@
"type": "string",
"description": "The target identifier.",
"x-example": "token"
},
"expired": {
"type": "boolean",
"description": "Is the target expired.",
"x-example": false
}
},
"required": [
@@ -10039,7 +10044,8 @@
"name",
"userId",
"providerType",
"identifier"
"identifier",
"expired"
]
}
},
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33 -33
View File
@@ -5189,7 +5189,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5274,7 +5274,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5395,7 +5395,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 307,
"weight": 308,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5469,7 +5469,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 331,
"weight": 332,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5545,7 +5545,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 330,
"weight": 331,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -6069,7 +6069,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 382,
"weight": 383,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6158,7 +6158,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 386,
"weight": 387,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6233,7 +6233,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6318,7 +6318,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6412,7 +6412,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6484,7 +6484,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 214,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6575,7 +6575,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 215,
"weight": 216,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6649,7 +6649,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 211,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6723,7 +6723,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6924,7 +6924,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 212,
"weight": 213,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6998,7 +6998,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7075,7 +7075,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7169,7 +7169,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7233,7 +7233,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7310,7 +7310,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7365,7 +7365,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.",
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Memberships List",
@@ -7376,7 +7376,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7461,7 +7461,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7567,7 +7567,7 @@
"tags": [
"teams"
],
"description": "Get a team member by the membership unique id. All team members have read access for this resource.",
"description": "Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Membership",
@@ -7578,7 +7578,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7650,7 +7650,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7738,7 +7738,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 230,
"weight": 231,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7812,7 +7812,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 229,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7910,7 +7910,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7973,7 +7973,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9533,12 +9533,12 @@
},
"userName": {
"type": "string",
"description": "User name.",
"description": "User name. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "John Doe"
},
"userEmail": {
"type": "string",
"description": "User email address.",
"description": "User email address. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "john@appwrite.io"
},
"teamId": {
@@ -9568,7 +9568,7 @@
},
"mfa": {
"type": "boolean",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise.",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.",
"x-example": false
},
"roles": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -326,9 +326,9 @@ App::post('/v1/functions')
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($functionsDomain)) {
$ruleId = ID::unique();
$routeSubdomain = ID::unique();
$domain = "{$routeSubdomain}.{$functionsDomain}";
$ruleId = md5($domain);
$rule = Authorization::skip(
fn () => $dbForConsole->createDocument('rules', new Document([
+38
View File
@@ -111,6 +111,9 @@ App::post('/v1/projects')
'personalDataCheck' => false,
'mockNumbers' => [],
'sessionAlerts' => false,
'membershipsUserName' => false,
'membershipsUserEmail' => false,
'membershipsMfa' => false,
];
foreach ($auth as $method) {
@@ -648,6 +651,41 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/memberships-privacy')
->desc('Update project memberships privacy attributes')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateMembershipsPrivacy')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('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.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $userName, bool $userEmail, bool $mfa, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['membershipsUserName'] = $userName;
$auths['membershipsUserEmail'] = $userEmail;
$auths['membershipsMfa'] = $mfa;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/limit')
->desc('Update project users limit')
->groups(['api', 'projects'])
+3 -5
View File
@@ -11,7 +11,6 @@ use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
@@ -60,9 +59,8 @@ App::post('/v1/proxy/rules')
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.');
}
$document = $dbForConsole->findOne('rules', [
Query::equal('domain', [$domain]),
]);
$ruleId = md5($domain);
$document = $dbForConsole->getDocument('rules', $ruleId);
if (!$document->isEmpty()) {
if ($document->getAttribute('projectId') === $project->getId()) {
@@ -103,7 +101,7 @@ App::post('/v1/proxy/rules')
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
}
$ruleId = ID::unique();
$ruleId = md5($domain->get());
$rule = new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
+77 -34
View File
@@ -727,9 +727,9 @@ App::get('/v1/teams/:teamId/memberships')
->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)
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (string $teamId, array $queries, string $search, Response $response, Database $dbForProject) {
->action(function (string $teamId, array $queries, string $search, Response $response, Document $project, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -790,27 +790,48 @@ App::get('/v1/teams/:teamId/memberships')
$memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId')));
$memberships = array_map(function ($membership) use ($dbForProject, $team) {
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$membershipsPrivacy = [
'userName' => $project->getAttribute('auths', [])['membershipsUserName'] ?? true,
'userEmail' => $project->getAttribute('auths', [])['membershipsUserEmail'] ?? true,
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$mfa = $user->getAttribute('mfa', false);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
$membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) {
return $privacy || $isPrivilegedUser || $isAppUser;
}, $membershipsPrivacy);
$memberships = array_map(function ($membership) use ($dbForProject, $team, $membershipsPrivacy) {
if ($membershipsPrivacy['mfa']) {
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$mfa = $user->getAttribute('mfa', false);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
}
}
$membership->setAttribute('mfa', $mfa);
}
$membership
->setAttribute('mfa', $mfa)
->setAttribute('teamName', $team->getAttribute('name'))
->setAttribute('userName', $user->getAttribute('name'))
->setAttribute('userEmail', $user->getAttribute('email'))
;
if ($membershipsPrivacy['userName']) {
$membership->setAttribute('userName', $user->getAttribute('name'));
}
if ($membershipsPrivacy['userEmail']) {
$membership->setAttribute('userEmail', $user->getAttribute('email'));
}
$membership->setAttribute('teamName', $team->getAttribute('name'));
return $membership;
}, $memberships);
@@ -837,8 +858,9 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject) {
->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -852,27 +874,48 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
}
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$membershipsPrivacy = [
'userName' => $project->getAttribute('auths', [])['membershipsUserName'] ?? true,
'userEmail' => $project->getAttribute('auths', [])['membershipsUserEmail'] ?? true,
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$mfa = $user->getAttribute('mfa', false);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
$membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) {
return $privacy || $isPrivilegedUser || $isAppUser;
}, $membershipsPrivacy);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
if ($membershipsPrivacy['mfa']) {
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$mfa = $user->getAttribute('mfa', false);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
}
}
$membership->setAttribute('mfa', $mfa);
}
$membership
->setAttribute('mfa', $mfa)
->setAttribute('teamName', $team->getAttribute('name'))
->setAttribute('userName', $user->getAttribute('name'))
->setAttribute('userEmail', $user->getAttribute('email'))
;
if ($membershipsPrivacy['userName']) {
$membership->setAttribute('userName', $user->getAttribute('name'));
}
if ($membershipsPrivacy['userEmail']) {
$membership->setAttribute('userEmail', $user->getAttribute('email'));
}
$membership->setAttribute('teamName', $team->getAttribute('name'));
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
});
+5 -12
View File
@@ -29,7 +29,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Domains\Domain;
use Utopia\DSN\DSN;
@@ -52,14 +51,9 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$host = $request->getHostname() ?? '';
$route = Authorization::skip(
fn () => $dbForConsole->find('rules', [
Query::equal('domain', [$host]),
Query::limit(1)
])
)[0] ?? null;
$route = Authorization::skip(fn () => $dbForConsole->getDocument('rules', md5($host)));
if ($route === null) {
if ($route->isEmpty()) {
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
}
@@ -518,19 +512,18 @@ App::init()
if (!empty($envDomain) && $envDomain !== 'localhost') {
$mainDomain = $envDomain;
} else {
$domainDocument = $dbForConsole->findOne('rules', [Query::orderAsc('$id')]);
$domainDocument = $dbForConsole->getDocument('rules', md5($envDomain));
$mainDomain = !$domainDocument->isEmpty() ? $domainDocument->getAttribute('domain') : $domain->get();
}
if ($mainDomain !== $domain->get()) {
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
} else {
$domainDocument = $dbForConsole->findOne('rules', [
Query::equal('domain', [$domain->get()])
]);
$domainDocument = $dbForConsole->getDocument('rules', md5($domain->get()));
if ($domainDocument->isEmpty()) {
$domainDocument = new Document([
'$id' => md5($domain->get()),
'domain' => $domain->get(),
'resourceType' => 'api',
'status' => 'verifying',
+5 -2
View File
@@ -39,8 +39,7 @@ $http
->set([
'worker_num' => $workerNumber,
'open_http2_protocol' => true,
'http_compression' => true,
'http_compression_level' => 6,
'http_compression' => false,
'package_max_length' => $payloadSize,
'buffer_output_size' => $payloadSize,
]);
@@ -61,6 +60,8 @@ include __DIR__ . '/controllers/general.php';
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
$app = new App('UTC');
$app->setCompression(true);
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
go(function () use ($register, $app) {
$pools = $register->get('pools');
@@ -244,6 +245,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
}
$app = new App('UTC');
$app->setCompression(true);
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
$pools = $register->get('pools');
App::setResource('pools', fn () => $pools);
+1
View File
@@ -73,6 +73,7 @@ $image = $this->getParam('image', '');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_LOCALE
- _APP_COMPRESSION_MIN_SIZE_BYTES
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
Generated
+67 -20
View File
@@ -1671,6 +1671,52 @@
},
"time": "2024-10-04T13:55:36+00:00"
},
{
"name": "utopia-php/compression",
"version": "0.1.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/compression.git",
"reference": "6062f70596415f8d5de40a589367b0eb2a435f98"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/compression/zipball/6062f70596415f8d5de40a589367b0eb2a435f98",
"reference": "6062f70596415f8d5de40a589367b0eb2a435f98",
"shasum": ""
},
"require": {
"php": ">=8.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.0.1"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\Compression\\": "src/Compression"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A simple Compression library to handle file compression",
"keywords": [
"compression",
"framework",
"php",
"upf",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/compression/issues",
"source": "https://github.com/utopia-php/compression/tree/0.1.2"
},
"time": "2024-11-08T14:59:54+00:00"
},
{
"name": "utopia-php/config",
"version": "0.2.2",
@@ -1926,20 +1972,21 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.8",
"version": "0.33.11",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5"
"reference": "354ff0d23bfc6e82bea0fe8e89e115cff1af8466"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5",
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5",
"url": "https://api.github.com/repos/utopia-php/http/zipball/354ff0d23bfc6e82bea0fe8e89e115cff1af8466",
"reference": "354ff0d23bfc6e82bea0fe8e89e115cff1af8466",
"shasum": ""
},
"require": {
"php": ">=8.0"
"php": ">=8.0",
"utopia-php/compression": "0.1.*"
},
"require-dev": {
"laravel/pint": "^1.2",
@@ -1965,9 +2012,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.8"
"source": "https://github.com/utopia-php/http/tree/0.33.11"
},
"time": "2024-08-15T14:10:09+00:00"
"time": "2024-11-08T18:47:43+00:00"
},
{
"name": "utopia-php/image",
@@ -3513,16 +3560,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.12.0",
"version": "1.12.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
"shasum": ""
},
"require": {
@@ -3561,7 +3608,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
},
"funding": [
{
@@ -3569,7 +3616,7 @@
"type": "tidelift"
}
],
"time": "2024-06-12T14:39:25+00:00"
"time": "2024-11-08T17:47:46+00:00"
},
{
"name": "nikic/php-parser",
@@ -4068,23 +4115,23 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.9.0",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d"
"reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
"reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a",
"reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.0",
"php": "^7.3 || ^8.0",
"phpdocumentor/reflection-common": "^2.0",
"phpstan/phpdoc-parser": "^1.18"
"phpstan/phpdoc-parser": "^1.18|^2.0"
},
"require-dev": {
"ext-tokenizer": "*",
@@ -4120,9 +4167,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.9.0"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0"
},
"time": "2024-11-03T20:11:34+00:00"
"time": "2024-11-09T15:12:26+00:00"
},
{
"name": "phpspec/prophecy",
+1
View File
@@ -96,6 +96,7 @@ services:
- _APP_EDITION
- _APP_WORKER_PER_CORE
- _APP_LOCALE
- _APP_COMPRESSION_MIN_SIZE_BYTES
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
+1 -1
View File
@@ -1 +1 @@
Get a team member by the membership unique id. All team members have read access for this resource.
Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.
+1 -1
View File
@@ -1 +1 @@
Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.
Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.
+4
View File
@@ -121,6 +121,10 @@ class Audit extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -112,6 +112,10 @@ class Build extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -74,6 +74,10 @@ class Certificate extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -108,6 +108,10 @@ class Database extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
try {
$dsn = new DSN($this->getProject()->getAttribute('database'));
} catch (\InvalidArgumentException) {
+4
View File
@@ -140,6 +140,10 @@ class Delete extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+22
View File
@@ -65,6 +65,24 @@ class Event
{
}
/**
* Set paused state for this event.
*/
public function setPaused(bool $paused): self
{
$this->paused = $paused;
return $this;
}
/**
* Get paused state for this event.
*/
public function getPaused(): bool
{
return $this->paused;
}
/**
* Set queue used for this event.
*
@@ -302,6 +320,10 @@ class Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -213,6 +213,10 @@ class Func extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
$events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null;
+4
View File
@@ -404,6 +404,10 @@ class Mail extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -182,6 +182,10 @@ class Messaging extends Event
*/
public function trigger(): string | bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+3
View File
@@ -75,6 +75,9 @@ class Migration extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
+1 -1
View File
@@ -32,7 +32,7 @@ class Realtime extends Event
*/
public function trigger(): string|bool
{
if (empty($this->event)) {
if ($this->paused || empty($this->event)) {
return false;
}
+4
View File
@@ -57,6 +57,10 @@ class Usage extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
'project' => $this->getProject(),
+4
View File
@@ -38,6 +38,10 @@ class UsageDump extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
@@ -478,9 +478,7 @@ class Certificates extends Action
private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions): void
{
$rule = $dbForConsole->findOne('rules', [
Query::equal('domain', [$domain]),
]);
$rule = $dbForConsole->getDocument('rules', md5($domain));
if (!$rule->isEmpty()) {
$rule->setAttribute('certificateId', $certificateId);
@@ -37,4 +37,4 @@ class BulkOperation extends Model
{
return Response::MODEL_BULK_OPERATION;
}
}
}
@@ -36,13 +36,13 @@ class Membership extends Model
])
->addRule('userName', [
'type' => self::TYPE_STRING,
'description' => 'User name.',
'description' => 'User name. Hide this attribute by toggling membership privacy in the Console.',
'default' => '',
'example' => 'John Doe',
])
->addRule('userEmail', [
'type' => self::TYPE_STRING,
'description' => 'User email address.',
'description' => 'User email address. Hide this attribute by toggling membership privacy in the Console.',
'default' => '',
'example' => 'john@appwrite.io',
])
@@ -78,7 +78,7 @@ class Membership extends Model
])
->addRule('mfa', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Multi factor authentication status, true if the user has MFA enabled or false otherwise.',
'description' => 'Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.',
'default' => false,
'example' => false,
])
@@ -151,6 +151,24 @@ class Project extends Model
'default' => false,
'example' => true,
])
->addRule('authMembershipsUserName', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to show user names in the teams membership response.',
'default' => false,
'example' => true,
])
->addRule('authMembershipsUserEmail', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to show user emails in the teams membership response.',
'default' => false,
'example' => true,
])
->addRule('authMembershipsMfa', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to show user MFA status in the teams membership response.',
'default' => false,
'example' => true,
])
->addRule('oAuthProviders', [
'type' => Response::MODEL_AUTH_PROVIDER,
'description' => 'List of Auth Providers.',
@@ -348,6 +366,9 @@ class Project extends Model
$document->setAttribute('authPersonalDataCheck', $authValues['personalDataCheck'] ?? false);
$document->setAttribute('authMockNumbers', $authValues['mockNumbers'] ?? []);
$document->setAttribute('authSessionAlerts', $authValues['sessionAlerts'] ?? false);
$document->setAttribute('authMembershipsUserName', $authValues['membershipsUserName'] ?? true);
$document->setAttribute('authMembershipsUserEmail', $authValues['membershipsUserEmail'] ?? true);
$document->setAttribute('authMembershipsMfa', $authValues['membershipsMfa'] ?? true);
foreach ($auth as $index => $method) {
$key = $method['key'];
+9 -5
View File
@@ -179,9 +179,14 @@ class Client
default => http_build_query($params),
};
foreach ($headers as $i => $header) {
$headers[] = $i . ':' . $header;
unset($headers[$i]);
$formattedHeaders = [];
foreach ($headers as $key => $value) {
if (strtolower($key) === 'accept-encoding') {
curl_setopt($ch, CURLOPT_ENCODING, $value);
continue;
} else {
$formattedHeaders[] = $key . ': ' . $value;
}
}
curl_setopt($ch, CURLOPT_PATH_AS_IS, 1);
@@ -189,7 +194,7 @@ class Client
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_HTTPHEADER, $formattedHeaders);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders, &$cookies) {
@@ -220,7 +225,6 @@ class Client
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
$responseBody = curl_exec($ch);
$responseType = $responseHeaders['content-type'] ?? '';
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+137
View File
@@ -0,0 +1,137 @@
<?php
namespace Tests\E2E\General;
use Appwrite\ID;
use Appwrite\Permission;
use Appwrite\Role;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
class CompressionTest extends Scope
{
use ProjectCustom;
use SideServer;
public function testSmallResponse()
{
// with header
$response = $this->client->call(Client::METHOD_GET, '/ping', [
'accept-encoding' => 'gzip',
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Pong!', $response['body']);
$this->assertLessThan(1024, strlen($response['body']));
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
// without header
$response = $this->client->call(Client::METHOD_GET, '/ping', [
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Pong!', $response['body']);
$this->assertLessThan(1024, strlen($response['body']));
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
}
public function testLargeResponse()
{
// create an anonymous user
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
'content-type' => 'application/json',
], $this->getHeaders()), [
'userId' => ID::unique(),
'email' => 'test@localhost.test',
'password' => 'password',
'name' => 'User Name',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$userId = $response['body']['$id'];
// set prefs with 2000 bytes of data
$prefs = ["longValue" => str_repeat('a', 2000)];
$response = $this->client->call(Client::METHOD_PATCH, '/users/' . $userId . '/prefs', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
'content-type' => 'application/json',
], $this->getHeaders()), [
'prefs' => $prefs,
]);
$this->assertEquals(200, $response['headers']['status-code']);
// get prefs with compression
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId . '/prefs', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
'accept-encoding' => 'gzip',
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayHasKey('content-encoding', $response['headers'], 'Content encoding should be gzip, headers received: ' . json_encode($response['headers'], JSON_PRETTY_PRINT));
$this->assertLessThan(2000, intval($response['headers']['content-length']));
// get prefs without compression
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId . '/prefs', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThanOrEqual(2000, intval($response['headers']['content-length']));
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
}
public function testImageResponse()
{
// create bucket
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'bucketId' => ID::unique(),
'name' => 'Test Bucket',
'fileSecurity' => true,
]);
$bucketId = $bucket['body']['$id'];
$this->assertEquals(201, $bucket['headers']['status-code']);
// upload image
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'fileId' => ID::unique(),
'file' => new CURLFile(realpath(__DIR__ . '/../../resources/logo.png'), 'image/png', 'logo.png'),
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$fileId = $file['body']['$id'];
// get image with header
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
'content-type' => 'application/json',
'accept-encoding' => 'gzip',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
// get image without
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
}
}
@@ -1,12 +1,13 @@
<?php
namespace Tests\E2E\Services\Functions;
namespace Tests\E2E\Services\FunctionsSchedule;
use Appwrite\ID;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Tests\E2E\Services\Functions\FunctionsBase;
use Utopia\Database\Helpers\Role;
class FunctionsScheduleTest extends Scope
+8 -8
View File
@@ -30,8 +30,8 @@ trait TeamsBaseClient
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
$this->assertFalse($response['body']['memberships'][0]['mfa']);
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('userName', $response['body']['memberships'][0]);
$this->assertArrayHasKey('userEmail', $response['body']['memberships'][0]);
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
@@ -96,8 +96,8 @@ trait TeamsBaseClient
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]);
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('userName', $response['body']['memberships'][0]);
$this->assertArrayHasKey('userEmail', $response['body']['memberships'][0]);
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
@@ -112,8 +112,8 @@ trait TeamsBaseClient
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]);
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('userName', $response['body']['memberships'][0]);
$this->assertArrayHasKey('userEmail', $response['body']['memberships'][0]);
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
@@ -157,8 +157,8 @@ trait TeamsBaseClient
$this->assertNotEmpty($response['body']['$id']);
$this->assertFalse($response['body']['mfa']);
$this->assertNotEmpty($response['body']['userId']);
$this->assertNotEmpty($response['body']['userName']);
$this->assertNotEmpty($response['body']['userEmail']);
$this->assertArrayHasKey('userName', $response['body']);
$this->assertArrayHasKey('userEmail', $response['body']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertNotEmpty($response['body']['teamName']);
$this->assertCount(1, $response['body']['roles']);
+62 -1
View File
@@ -29,7 +29,7 @@ trait TeamsBaseServer
* Test for FAILURE
*/
return [];
return $data;
}
/**
@@ -60,6 +60,67 @@ trait TeamsBaseServer
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['joined'])); // is null in DB
$this->assertEquals(true, $response['body']['confirm']);
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => false,
'userEmail' => false,
'mfa' => false,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are not hidden, as we are on console
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are present
$this->assertNotEmpty($response['body']['memberships'][0]['userName']);
$this->assertNotEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('mfa', $response['body']['memberships'][0]);
/**
* Update project settings to show sensitive fields
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => true,
'userEmail' => true,
'mfa' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are shown
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are present
$this->assertNotEmpty($response['body']['memberships'][0]['userName']);
$this->assertNotEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('mfa', $response['body']['memberships'][0]);
/**
* Test for FAILURE
*/
@@ -14,6 +14,77 @@ class TeamsCustomClientTest extends Scope
use ProjectCustom;
use SideClient;
/**
* @depends testGetTeamMemberships
*/
public function testGetMembershipPrivacy($data)
{
$teamUid = $data['teamUid'] ?? '';
$projectId = $this->getProject()['$id'];
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $projectId . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => false,
'userEmail' => false,
'mfa' => false,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are hidden
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are not present
$this->assertEmpty($response['body']['memberships'][0]['userName']);
$this->assertEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertFalse($response['body']['memberships'][0]['mfa']);
/**
* Update project settings to show sensitive fields
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $projectId . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => true,
'userEmail' => true,
'mfa' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are shown
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are present
$this->assertNotEmpty($response['body']['memberships'][0]['userName']);
$this->assertNotEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('mfa', $response['body']['memberships'][0]);
}
/**
* @depends testUpdateTeamMembership
*/
@@ -62,6 +62,7 @@ services:
- redis
# - clamav
environment:
- _APP_COMPRESSION_MIN_SIZE_BYTES
- _APP_ENV
- _APP_OPTIONS_ABUSE
- _APP_OPTIONS_ROUTER_PROTECTION