Merge branch '1.9.x' into fix-oauth-token-flow-provider-param

This commit is contained in:
Matej Bačo
2026-03-23 11:41:42 +01:00
6 changed files with 144 additions and 8 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ jobs:
- name: Build the Docker image
run: DOCKER_BUILDKIT=1 docker build . --target production -t appwrite_image:latest
- name: Run Trivy vulnerability scanner on image
uses: aquasecurity/trivy-action@0.20.0
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
image-ref: 'appwrite_image:latest'
format: 'sarif'
@@ -35,7 +35,7 @@ jobs:
- name: Check out code
uses: actions/checkout@v6
- name: Run Trivy vulnerability scanner on filesystem
uses: aquasecurity/trivy-action@0.20.0
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
scan-type: 'fs'
format: 'sarif'
+10 -1
View File
@@ -19,7 +19,16 @@ class Env
foreach ($data as &$row) {
$row = explode('=', $row, 2);
$key = (isset($row[0])) ? trim($row[0]) : null;
$value = (isset($row[1])) ? trim($row[1]) : null;
$value = (isset($row[1])) ? (function (string $v): string {
$v = trim($v);
if (
(\str_starts_with($v, '"') && \str_ends_with($v, '"')) ||
(\str_starts_with($v, "'") && \str_ends_with($v, "'"))
) {
return \substr($v, 1, -1);
}
return $v;
})(trim($row[1])) : null;
if ($key) {
$this->vars[$key] = $value;
+3 -2
View File
@@ -33,6 +33,7 @@ class Install extends Action
private const string APPWRITE_API_URL = 'http://appwrite';
private const string GROWTH_API_URL = 'https://growth.appwrite.io/v1';
protected bool $isUpgrade = false;
protected string $hostPath = '';
protected ?bool $isLocalInstall = null;
protected ?array $installerConfig = null;
@@ -66,7 +67,7 @@ class Install extends Action
bool $noStart,
string $database
): void {
$isUpgrade = false;
$isUpgrade = $this->isUpgrade;
$defaultHttpPort = '80';
$defaultHttpsPort = '443';
$config = Config::getParam('variables');
@@ -508,7 +509,7 @@ class Install extends Action
$this->applyLocalPaths($isLocalInstall, false);
$isCLI = php_sapi_name() === 'cli';
if ($isLocalInstall) {
if ($isLocalInstall || $isUpgrade) {
$useExistingConfig = false;
} else {
$useExistingConfig = file_exists($this->path . '/' . $this->getComposeFileName())
+2 -1
View File
@@ -42,6 +42,7 @@ class Upgrade extends Install
bool $noStart,
string $database
): void {
$this->isUpgrade = true;
$isLocalInstall = $this->isLocalInstall();
$this->applyLocalPaths($isLocalInstall, true);
@@ -72,7 +73,7 @@ class Upgrade extends Install
}
if ($database === null) {
$envData = @file_get_contents($this->path . '/.env');
$envData = @file_get_contents($this->path . '/' . $this->getEnvFileName());
if ($envData !== false) {
$envFile = new Env($envData);
$database = $envFile->list()['_APP_DB_ADAPTER'] ?? null;
@@ -3518,6 +3518,62 @@ trait DatabasesBase
$this->assertEquals(200, $response['headers']['status-code']);
}
public function testQueryBySequenceType(): void
{
$data = $this->setupDocuments();
$databaseId = $data['databaseId'];
$documents = $this->client->call(Client::METHOD_GET, $this->getRecordUrl($databaseId, $data['moviesId']), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$id', $data['documentIds'])->toString(),
],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
$this->assertGreaterThan(0, count($documents['body'][$this->getRecordResource()]));
$sequence = $documents['body'][$this->getRecordResource()][0]['$sequence'];
$this->assertIsString($sequence);
// Query with string $sequence value (supported by all adapters)
$response = $this->client->call(Client::METHOD_GET, $this->getRecordUrl($databaseId, $data['moviesId']), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body'][$this->getRecordResource()]);
$this->assertIsString($response['body'][$this->getRecordResource()][0]['$sequence']);
$this->assertSame($sequence, $response['body'][$this->getRecordResource()][0]['$sequence']);
// Query with int $sequence value (supported by SQL adapters, rejected by MongoDB)
$intSequence = (int)$sequence;
$response = $this->client->call(Client::METHOD_GET, $this->getRecordUrl($databaseId, $data['moviesId']), array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$intSequence])->toString(),
],
]);
$adapter = getenv('_APP_DB_ADAPTER');
if ($adapter === 'mongodb') {
$this->assertEquals(400, $response['headers']['status-code']);
} else {
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body'][$this->getRecordResource()]);
$this->assertIsString($response['body'][$this->getRecordResource()][0]['$sequence']);
}
}
public function testListDocumentsAfterPagination(): void
{
$data = $this->setupDocuments();
+71 -2
View File
@@ -430,7 +430,7 @@ class V21Test extends TestCase
{
$result = $this->filter->parse($content, Response::MODEL_DOCUMENT);
$this->assertEquals($expected, $result);
$this->assertSame($expected, $result);
}
#[DataProvider('documentProvider')]
@@ -438,7 +438,76 @@ class V21Test extends TestCase
{
$result = $this->filter->parse($content, Response::MODEL_ROW);
$this->assertEquals($expected, $result);
$this->assertSame($expected, $result);
}
public static function documentListProvider(): array
{
return [
'cast $sequence in document list' => [
[
'total' => 2,
'documents' => [
[
'$id' => 'doc1',
'$sequence' => '10',
'name' => 'first',
],
[
'$id' => 'doc2',
'$sequence' => '20',
'name' => 'second',
],
],
],
[
'total' => 2,
'documents' => [
[
'$id' => 'doc1',
'$sequence' => 10,
'name' => 'first',
],
[
'$id' => 'doc2',
'$sequence' => 20,
'name' => 'second',
],
],
]
],
'handle empty document list' => [
[
'total' => 0,
'documents' => [],
],
[
'total' => 0,
'documents' => [],
]
],
];
}
#[DataProvider('documentListProvider')]
public function testDocumentList(array $content, array $expected): void
{
$result = $this->filter->parse($content, Response::MODEL_DOCUMENT_LIST);
$this->assertSame($expected, $result);
}
#[DataProvider('documentListProvider')]
public function testRowList(array $content, array $expected): void
{
$content['rows'] = $content['documents'];
unset($content['documents']);
$expected['rows'] = $expected['documents'];
unset($expected['documents']);
$result = $this->filter->parse($content, Response::MODEL_ROW_LIST);
$this->assertSame($expected, $result);
}
public static function defaultPassthroughProvider(): array