Fix request fetching

This commit is contained in:
Jake Barnby
2026-01-13 22:01:16 +13:00
parent 8eb09a98fe
commit cd21140572
4 changed files with 162 additions and 57 deletions
Generated
+25 -25
View File
@@ -756,16 +756,16 @@
},
{
"name": "google/protobuf",
"version": "v4.33.3",
"version": "v4.33.4",
"source": {
"type": "git",
"url": "https://github.com/protocolbuffers/protobuf-php.git",
"reference": "281537d44d6c270606354e65bfa75a0969dbd629"
"reference": "22d28025cda0d223a2e48c2e16c5284ecc9f5402"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/281537d44d6c270606354e65bfa75a0969dbd629",
"reference": "281537d44d6c270606354e65bfa75a0969dbd629",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/22d28025cda0d223a2e48c2e16c5284ecc9f5402",
"reference": "22d28025cda0d223a2e48c2e16c5284ecc9f5402",
"shasum": ""
},
"require": {
@@ -794,9 +794,9 @@
"proto"
],
"support": {
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.3"
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.4"
},
"time": "2026-01-09T21:49:37+00:00"
"time": "2026-01-12T17:58:43+00:00"
},
{
"name": "league/csv",
@@ -4267,16 +4267,16 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.35",
"version": "0.33.36",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "82b139fb04f30045db51b0d322224f222da32313"
"reference": "fd835ed77e1cdf327067ce4e650cce86304e7098"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/82b139fb04f30045db51b0d322224f222da32313",
"reference": "82b139fb04f30045db51b0d322224f222da32313",
"url": "https://api.github.com/repos/utopia-php/http/zipball/fd835ed77e1cdf327067ce4e650cce86304e7098",
"reference": "fd835ed77e1cdf327067ce4e650cce86304e7098",
"shasum": ""
},
"require": {
@@ -4309,9 +4309,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.35"
"source": "https://github.com/utopia-php/http/tree/0.33.36"
},
"time": "2025-12-12T08:33:52+00:00"
"time": "2026-01-12T07:32:29+00:00"
},
{
"name": "utopia-php/image",
@@ -5014,22 +5014,22 @@
},
{
"name": "utopia-php/swoole",
"version": "0.8.5",
"version": "0.8.6",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/swoole.git",
"reference": "e42b6b8e44c457a7b35d8a857d7af1d67d667c58"
"reference": "14b00277c35a258cb263706fd4e05c50368feb4f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/e42b6b8e44c457a7b35d8a857d7af1d67d667c58",
"reference": "e42b6b8e44c457a7b35d8a857d7af1d67d667c58",
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/14b00277c35a258cb263706fd4e05c50368feb4f",
"reference": "14b00277c35a258cb263706fd4e05c50368feb4f",
"shasum": ""
},
"require": {
"ext-swoole": "*",
"php": ">=8.0",
"utopia-php/framework": "0.33.35"
"utopia-php/framework": "0.33.36"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -5059,9 +5059,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/swoole/issues",
"source": "https://github.com/utopia-php/swoole/tree/0.8.5"
"source": "https://github.com/utopia-php/swoole/tree/0.8.6"
},
"time": "2025-12-15T14:03:23+00:00"
"time": "2026-01-12T07:57:35+00:00"
},
{
"name": "utopia-php/system",
@@ -5439,16 +5439,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.8.10",
"version": "1.8.11",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "28217b1d722ad2b217559c3a371eafd4d070614d"
"reference": "936404bbcbf4cd692bac102f2912b6c97ac87215"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/28217b1d722ad2b217559c3a371eafd4d070614d",
"reference": "28217b1d722ad2b217559c3a371eafd4d070614d",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/936404bbcbf4cd692bac102f2912b6c97ac87215",
"reference": "936404bbcbf4cd692bac102f2912b6c97ac87215",
"shasum": ""
},
"require": {
@@ -5484,9 +5484,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/1.8.10"
"source": "https://github.com/appwrite/sdk-generator/tree/1.8.11"
},
"time": "2026-01-09T11:40:35+00:00"
"time": "2026-01-12T08:41:56+00:00"
},
{
"name": "brianium/paratest",
+2 -2
View File
@@ -1211,7 +1211,7 @@ services:
- MAILDEV_INCOMING_PASS=${_APP_SMTP_PASSWORD}
request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook)
image: appwrite/requestcatcher:1.0.0
image: appwrite/requestcatcher:1.1.0
container_name: appwrite-requestcatcher-webhook
<<: *x-logging
ports:
@@ -1220,7 +1220,7 @@ services:
- appwrite
request-catcher-sms: # used mainly for dev tests (mock SMS auth secret)
image: appwrite/requestcatcher:1.0.0
image: appwrite/requestcatcher:1.1.0
container_name: appwrite-requestcatcher-sms
<<: *x-logging
ports:
+95 -14
View File
@@ -152,7 +152,7 @@ abstract class Scope extends TestCase
}
/**
* @deprecated Use assertLastRequest instead. Used only historically in webhook tests
* @deprecated Use getLastRequestForProject instead. Used only historically in webhook tests
*/
protected function getLastRequest(): array
{
@@ -163,31 +163,112 @@ abstract class Scope extends TestCase
* Get the last webhook request for a specific project.
* Polls with retry to handle parallel test race conditions.
*/
protected function getLastRequestForProject(string $projectId, int $maxAttempts = 10, int $delayMs = 500): array
{
$hostname = 'request-catcher-webhook';
protected function getLastRequestForProject(
string $projectId,
string $type = self::REQUEST_TYPE_WEBHOOK,
array $queryParams = [],
int $maxAttempts = 10,
int $delayMs = 500,
?callable $probe = null
): array {
$hostname = match ($type) {
self::REQUEST_TYPE_WEBHOOK => 'request-catcher-webhook',
self::REQUEST_TYPE_SMS => 'request-catcher-sms',
default => throw new \Exception('Invalid request catcher type.'),
};
$enforceProjectId = $type === self::REQUEST_TYPE_WEBHOOK;
if (empty($queryParams)) {
$queryParams = [
'header_X-Appwrite-Webhook-Project-Id' => $projectId,
];
}
$query = http_build_query($queryParams);
sleep(2);
for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
$request = json_decode(file_get_contents('http://' . $hostname . ':5000/__last_request__'), true);
if ($request) {
$request['data'] = json_decode($request['data'], true);
$requests = json_decode(file_get_contents('http://' . $hostname . ':5000/__find_request__?' . $query), true);
if (is_array($requests)) {
for ($i = count($requests) - 1; $i >= 0; $i--) {
$request = $this->decodeRequestData($requests[$i]);
if ($probe !== null) {
try {
$probe($request);
return $request;
} catch (\Throwable $error) {
continue;
}
}
$requestProjectId = $request['headers']['X-Appwrite-Webhook-Project-Id'] ?? '';
if ($requestProjectId === $projectId) {
return $request;
if ($enforceProjectId) {
$requestProjectId = $request['headers']['X-Appwrite-Webhook-Project-Id'] ?? '';
if ($requestProjectId === $projectId) {
return $request;
}
} else {
return $request;
}
}
}
usleep($delayMs * 1000);
}
$request = json_decode(file_get_contents('http://' . $hostname . ':5000/__last_request__'), true);
if ($request) {
$request['data'] = json_decode($request['data'], true);
$requests = json_decode(file_get_contents('http://' . $hostname . ':5000/__find_request__?' . $query), true);
if (is_array($requests)) {
for ($i = count($requests) - 1; $i >= 0; $i--) {
$request = $this->decodeRequestData($requests[$i]);
if ($probe !== null) {
try {
$probe($request);
return $request;
} catch (\Throwable $error) {
continue;
}
}
if ($enforceProjectId) {
$requestProjectId = $request['headers']['X-Appwrite-Webhook-Project-Id'] ?? '';
if ($requestProjectId === $projectId) {
return $request;
}
} else {
return $request;
}
}
}
return $request ?? [];
return [];
}
protected function decodeRequestData(array $request): array
{
if (!array_key_exists('data', $request)) {
return $request;
}
if (is_array($request['data'])) {
return $request;
}
if (!is_string($request['data']) || $request['data'] === '') {
return $request;
}
$decoded = json_decode($request['data'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$request['data'] = $decoded;
return $request;
}
parse_str($request['data'], $parsed);
if (!empty($parsed)) {
$request['data'] = $parsed;
}
return $request;
}
/**
@@ -2530,14 +2530,23 @@ class AccountCustomClientTest extends Scope
$userId = $response['body']['userId'];
$smsRequest = $this->assertLastRequest(function (array $request) use ($number) {
$this->assertEquals('Appwrite Mock Message Sender', $request['headers']['User-Agent']);
$this->assertEquals('username', $request['headers']['X-Username']);
$this->assertEquals('password', $request['headers']['X-Key']);
$this->assertEquals('POST', $request['method']);
$this->assertEquals('+123456789', $request['data']['from']);
$this->assertEquals($number, $request['data']['to']);
}, Scope::REQUEST_TYPE_SMS);
$smsRequest = $this->getLastRequestForProject(
$this->getProject()['$id'],
Scope::REQUEST_TYPE_SMS,
[
'header_X-Username' => 'username',
'header_X-Key' => 'password',
'method' => 'POST',
],
probe: function (array $request) use ($number) {
$this->assertEquals('Appwrite Mock Message Sender', $request['headers']['User-Agent'] ?? null);
$this->assertEquals('username', $request['headers']['X-Username'] ?? null);
$this->assertEquals('password', $request['headers']['X-Key'] ?? null);
$this->assertEquals('POST', $request['method'] ?? null);
$this->assertEquals('+123456789', $request['data']['from'] ?? null);
$this->assertEquals($number, $request['data']['to'] ?? null);
}
);
$data['token'] = $smsRequest['data']['message'];
$data['id'] = $userId;
@@ -2887,15 +2896,30 @@ class AccountCustomClientTest extends Scope
$tokenCreatedAt = $response['body']['$createdAt'];
$smsRequest = $this->assertLastRequest(function ($request) use ($tokenCreatedAt) {
$this->assertArrayHasKey('data', $request);
$this->assertArrayHasKey('time', $request);
$this->assertArrayHasKey('message', $request['data'], "Last request missing message: " . \json_encode($request));
$phone = $data['phone'] ?? '';
$smsQuery = [
'header_X-Username' => 'username',
'header_X-Key' => 'password',
'method' => 'POST',
];
// Ensure we are not using token from last sms login
$tokenRecievedAt = $request['time'];
$this->assertGreaterThan($tokenCreatedAt, $tokenRecievedAt);
}, Scope::REQUEST_TYPE_SMS);
$smsRequest = $this->getLastRequestForProject(
$this->getProject()['$id'],
Scope::REQUEST_TYPE_SMS,
$smsQuery,
probe: function (array $request) use ($tokenCreatedAt, $phone) {
$this->assertArrayHasKey('data', $request);
$this->assertArrayHasKey('time', $request);
$this->assertArrayHasKey('message', $request['data'], "Last request missing message: " . \json_encode($request));
if (!empty($phone)) {
$this->assertEquals($phone, $request['data']['to'] ?? null);
}
// Ensure we are not using token from last sms login
$tokenRecievedAt = $request['time'];
$this->assertGreaterThan($tokenCreatedAt, $tokenRecievedAt);
}
);
/**
* Test for FAILURE