Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c6dbc47
feat: add Query builder, aggregates, joins, and query normalize
abnegate Aug 21, 2026
4f009cf
fix: keep Query.normalize compatible with Go 1.13 and web realtime types
abnegate Aug 21, 2026
8a9dc95
refactor: keep query flattening internal to the client
abnegate Aug 21, 2026
bc7d6b4
feat: add Query.on and nested join ON overloads
abnegate Aug 21, 2026
4e3fef2
test: cover new query helpers in every SDK
abnegate Aug 21, 2026
0b1874f
refactor: flatten query values without a queries key check
abnegate Aug 21, 2026
844e3e1
fix: treat oneOf string enums as closed wire enums
abnegate Aug 21, 2026
4d81290
fix: name untitled oneOf enums from their parent property
abnegate Aug 21, 2026
f8897cd
refactor: nest query builders as Query.Builder
abnegate Aug 21, 2026
da191e4
fix: compile nested query builders and hydrate array enums
abnegate Aug 21, 2026
e85873f
fix: pin CLI to the locally generated Go SDK
abnegate Aug 21, 2026
ab9dd4f
refactor: flatten query values on Client instead of Query
abnegate Aug 21, 2026
4561f9c
merge: origin/main into feat-query-helpers
abnegate Aug 21, 2026
8fc3bd4
fix: flatten query trees without dropping numeric arrays
abnegate Aug 21, 2026
41cbbb8
test: assert flatten unwraps builders and keeps nested geometry
abnegate Aug 21, 2026
53e6e02
fix: sync CLI generator testdata with aggregate helpers
abnegate Aug 21, 2026
727aee8
fix: keep flatten tests off Flutter Client init and Swift array casts
abnegate Aug 21, 2026
287f439
fix: iterate Swift query arrays without an Any downcast
abnegate Aug 21, 2026
2314f09
fix: prefer typed Swift arrays before Any in flatten
abnegate Aug 21, 2026
b657276
merge: origin/main into feat-query-helpers
abnegate Aug 21, 2026
f6ef0ae
fix: Black-format Python query tests and unwrap Swift builder arrays
abnegate Aug 21, 2026
61da4d2
fix: flatten Swift query lists as Any, matching Client.call
abnegate Aug 21, 2026
a85ec6b
merge: origin/main into feat-query-helpers
abnegate Aug 21, 2026
6c0496c
fix: Pint PHP flatten tests and drop Swift Linux flatten e2e
abnegate Aug 21, 2026
e0a4ce5
fix: drop extra blank line in QueryTest setUp for Pint
abnegate Aug 21, 2026
5f38a7f
fix: Pint use grouping and foreach spacing in PHP flatten tests
abnegate Aug 21, 2026
ab519b8
fix: type Query::having as string or Query for PHPStan
abnegate Aug 21, 2026
c71f5f5
fix: drop redundant Query phpdocs so Rector dry-run is clean
abnegate Aug 21, 2026
c0a44d0
fix: apply remaining Rector cleanups on PHP Query tests
abnegate Aug 21, 2026
f29c5ce
fix: type QueryTest fixtures as array and use short syntax
abnegate Aug 21, 2026
90aff4b
Merge main into feat-query-helpers
abnegate Aug 26, 2026
060826a
(chore): make the web templates emit Prettier-clean output
abnegate Aug 26, 2026
0a68bd7
style(templates): satisfy the formatter and lint gates main added
abnegate Aug 26, 2026
0bfb6cb
Merge remote-tracking branch 'origin/main' into codex/query-lib-sdkge…
abnegate Sep 7, 2026
3be98b7
fix: drop wrapping parens on generated PHP Query constructors
abnegate Sep 7, 2026
55de80f
fix: use array_all for generated PHP string-list check
abnegate Sep 7, 2026
be56eed
fix: space the generated array_all arrow function for Pint
abnegate Sep 7, 2026
5a452a4
chore(merge): reconcile feat-query-helpers with main
abnegate Sep 9, 2026
3c05c5b
test(query): assert query helpers through the client boundary
abnegate Sep 9, 2026
4b82e66
test(query): compare parsed queries in the generated SDK tests
abnegate Sep 9, 2026
46977aa
Merge remote-tracking branch 'origin/main' into merge/main-v5
abnegate Sep 9, 2026
c97a317
chore: merge main into feat-query-helpers
abnegate Sep 22, 2026
5652f2f
fix(unity): emit Query/Builder.cs in the Unity file plan
abnegate Sep 22, 2026
618b6df
Merge branch 'main' into feat-query-helpers
abnegate Oct 15, 2026
8930180
fix(cli): build against a Go SDK with the same services as the CLI
abnegate Oct 15, 2026
f2714c1
fix(cli): keep the local go sdk replace out of released modules
abnegate Oct 15, 2026
a666f40
fix(kotlin): send built queries in json request bodies
abnegate Oct 15, 2026
f34dd86
fix(android): keep nested join and having queries intact under r8
abnegate Oct 15, 2026
4ad8227
feat(cli): expose nested join on in the typed query builder
abnegate Oct 15, 2026
be1e57e
fix: flatten only query builders, not nested string arrays
abnegate Oct 15, 2026
8bcead5
test(go): read request bodies with ioutil for go 1.13
abnegate Oct 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions example.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,11 @@ function configureSDK(SDK $sdk, array $overrides = []): SDK {
],
];

// Deep-merge exclude services so overrides add to defaults rather than replacing
if (isset($overrides['exclude']['services']) && isset($defaults['exclude']['services'])) {
$overrides['exclude']['services'] = [
...$defaults['exclude']['services'],
...$overrides['exclude']['services'],
];
$exclude = $defaults['exclude'];
foreach ($overrides['exclude'] ?? [] as $kind => $rules) {
$exclude[$kind] = [...($exclude[$kind] ?? []), ...$rules];
}
$overrides['exclude'] = $exclude;

$config = [...$defaults, ...$overrides];

Expand Down Expand Up @@ -264,11 +262,22 @@ function buildStaticSpecification(): Specification {

// CLI
if (!$requestedSdk || $requestedSdk === 'cli') {
// CLI go.mod replace-pins github.com/appwrite/sdk-for-go/v7 to ../go.
$go = new SDK(new Go(), buildSpecification($spec));
configureSDK($go, [
'platform' => 'server',
'gitUserName' => 'appwrite',
'gitRepoName' => 'sdk-for-go',
'version' => '7.2.0-rc.3',
]);
$go->generate(__DIR__ . '/examples/go');

$language = new CLI();
$language->setExecutableName('appwrite');
$language->setLogo($cliLogo);
$language->setLogoUnescaped($cliLogoUnescaped);
$language->setNPMPackage('appwrite-cli');
$language->setSdkPath('../go');

$sdk = new SDK($language, buildSpecification($spec));
$sdk->setTest(false);
Expand Down Expand Up @@ -332,11 +341,11 @@ function buildStaticSpecification(): Specification {
if (!$requestedSdk || $requestedSdk === 'go') {
$sdk = new SDK(new Go(), buildSpecification($spec));
// The version decides the major-version suffix Go requires from v2 on,
// so examples/go declares `sdk-for-go/v6` like the published module.
// so examples/go declares `sdk-for-go/v7` like the published module.
configureSDK($sdk, [
'gitUserName' => 'appwrite',
'gitRepoName' => 'sdk-for-go',
'version' => '6.2.0',
'version' => '7.2.0-rc.3',
]);
$sdk->generate(__DIR__ . '/examples/go');
}
Expand Down
10 changes: 8 additions & 2 deletions src/SDK/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -583,17 +583,23 @@ protected function getSchemaEnumName(Schema|Parameter $value, ?Specification $sp
return $name;
}

$source = $this->getSchema($value);
$source = $source instanceof ArraySchema ? $source->items : $source;

foreach ($spec?->schemas ?? [] as $modelName => $model) {
if (!$model instanceof ObjectSchema) {
continue;
}
foreach ($model->properties as $propertyName => $property) {
if ($this->getEnumSchema($property) === $enumSchema) {
$propertySource = $this->getSchema($property);
$propertySource = $propertySource instanceof ArraySchema ? $propertySource->items : $propertySource;
if ($propertySource === $source) {
return \ucfirst($modelName) . \ucfirst($propertyName);
}
}
}
return '';

return $value instanceof Parameter ? $value->name : '';
}

protected function getSpecificationSchemaName(Schema $schema, Specification $spec): string
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Android.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ public function getFiles(): array
'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/Query.kt',
'template' => '/android/library/src/main/java/io/package/Query.kt.twig',
],
[
'scope' => 'default',
'destination' => '/library/src/test/java/{{ sdk.namespace | caseSlash }}/QueryTest.kt',
'template' => '/kotlin/src/test/kotlin/io/appwrite/QueryTest.kt.twig',
],
[
'scope' => 'default',
'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/Operator.kt',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Apple.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ public function getFiles(): array
'destination' => '/Sources/{{ spec.info.title | caseUcfirst}}/Query.swift',
'template' => 'swift/Sources/Query.swift.twig',
],
[
'scope' => 'default',
'destination' => '/Sources/{{ spec.info.title | caseUcfirst}}/Query/Builder.swift',
'template' => 'swift/Sources/Query/Builder.swift.twig',
],
[
'scope' => 'default',
'destination' => '/Sources/{{ spec.info.title | caseUcfirst}}/Operator.swift',
Expand Down
11 changes: 11 additions & 0 deletions src/SDK/Language/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class CLI extends Go
// silently break every installer if they did not.
'npmPackage' => 'appwrite-cli',
'sdkImportPath' => self::SDK_MODULE,
'sdkPath' => '',
// ASCII art above the main help screen. Templates escape it for their
// target syntax rather than sharing one encoded representation.
'logo' => '',
Expand Down Expand Up @@ -98,6 +99,16 @@ public function setNPMPackage(string $name): self
return $this;
}

/**
* Local directory go.mod replaces the published Go SDK with. Empty keeps the published module.
*/
public function setSdkPath(string $path): self
{
$this->setParam('sdkPath', $path);

return $this;
}

/**
* Configure the Homebrew tap (`<owner>/homebrew-<name>`) hosting the formula.
*/
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Dart.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@ public function getFiles(): array
'destination' => '/lib/query.dart',
'template' => 'dart/lib/query.dart.twig',
],
[
'scope' => 'default',
'destination' => '/lib/query/builder.dart',
'template' => 'dart/lib/query/builder.dart.twig',
],
[
'scope' => 'default',
'destination' => '/lib/operator.dart',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/DotNet.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@ public function getFiles(): array
'destination' => '{{ spec.info.title | caseUcfirst }}/Query.cs',
'template' => 'dotnet/Package/Query.cs.twig',
],
[
'scope' => 'default',
'destination' => '{{ spec.info.title | caseUcfirst }}/Query/Builder.cs',
'template' => 'dotnet/Package/Query/Builder.cs.twig',
],
Comment thread
abnegate marked this conversation as resolved.
[
'scope' => 'default',
'destination' => '{{ spec.info.title | caseUcfirst }}/Operator.cs',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Flutter.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public function getFiles(): array
'destination' => '/lib/query.dart',
'template' => 'dart/lib/query.dart.twig',
],
[
'scope' => 'default',
'destination' => '/lib/query/builder.dart',
'template' => 'dart/lib/query/builder.dart.twig',
],
[
'scope' => 'default',
'destination' => '/lib/operator.dart',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Go.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ public function getFiles(): array
'destination' => 'query/query.go',
'template' => 'go/query.go.twig',
],
[
'scope' => 'default',
'destination' => 'query/builder.go',
'template' => 'go/query/builder.go.twig',
],
[
'scope' => 'default',
'destination' => 'operator/operator.go',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Kotlin.php
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,11 @@ public function getFiles(): array
'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/Query.kt',
'template' => '/kotlin/src/main/kotlin/io/appwrite/Query.kt.twig',
],
[
'scope' => 'default',
'destination' => '/src/test/kotlin/{{ sdk.namespace | caseSlash }}/QueryTest.kt',
'template' => '/kotlin/src/test/kotlin/io/appwrite/QueryTest.kt.twig',
],
[
'scope' => 'default',
'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/Operator.kt',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/PHP.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ public function getFiles(): array
'destination' => 'src/{{ namespace | caseNamespacePath }}/Query.php',
'template' => 'php/src/Query.php.twig',
],
[
'scope' => 'default',
'destination' => 'src/{{ namespace | caseNamespacePath }}/Query/Builder.php',
'template' => 'php/src/Query/Builder.php.twig',
],
[
'scope' => 'default',
'destination' => 'tests/{{ namespace | caseNamespacePath }}/QueryTest.php',
Expand Down
8 changes: 7 additions & 1 deletion src/SDK/Language/Ruby.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ public function getFiles(): array
'destination' => 'lib/{{ spec.info.title | caseDash }}/query.rb',
'template' => 'ruby/lib/container/query.rb.twig',
],
[
'scope' => 'default',
'destination' => 'lib/{{ spec.info.title | caseDash }}/query/builder.rb',
'template' => 'ruby/lib/container/query/builder.rb.twig',
],
[
'scope' => 'default',
'destination' => 'lib/{{ spec.info.title | caseDash }}/operator.rb',
Expand Down Expand Up @@ -283,7 +288,8 @@ public function getParamExample(Schema|Parameter $param, string $lang = ''): str
$output .= $this->isPermissionString($example) ? $this->getPermissionExample($example) : $example;
break;
case self::TYPE_OBJECT:
$output .= $this->jsonToHash(json_decode((string) $example, true));
$decoded = \is_array($example) ? $example : json_decode((string) $example, true);
$output .= $this->jsonToHash(\is_array($decoded) ? $decoded : []);
break;
case self::TYPE_BOOLEAN:
$output .= ($example) ? 'true' : 'false';
Expand Down
18 changes: 16 additions & 2 deletions src/SDK/Language/Rust.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,13 @@ public function getFiles(): array
],
[
"scope" => "default",
"destination" => "src/query.rs",
"template" => "rust/src/query.rs.twig",
"destination" => "src/query/mod.rs",
"template" => "rust/src/query/mod.rs.twig",
],
[
"scope" => "default",
"destination" => "src/query/builder.rs",
"template" => "rust/src/query/builder.rs.twig",
Comment thread
abnegate marked this conversation as resolved.
],
[
"scope" => "default",
Expand Down Expand Up @@ -955,4 +960,13 @@ protected function formatArrayItemExample(mixed $value, Schema $items): string
},
};
}

#[Override]
public function postGenerate(string $target): void
{
$legacy = $target . '/src/query.rs';
if (\is_file($legacy) && \is_dir($target . '/src/query')) {
\unlink($legacy);
}
}
}
5 changes: 5 additions & 0 deletions src/SDK/Language/Swift.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ public function getFiles(): array
'destination' => '/Sources/{{ spec.info.title | caseUcfirst}}/Query.swift',
'template' => 'swift/Sources/Query.swift.twig',
],
[
'scope' => 'default',
'destination' => '/Sources/{{ spec.info.title | caseUcfirst}}/Query/Builder.swift',
'template' => 'swift/Sources/Query/Builder.swift.twig',
],
[
'scope' => 'default',
'destination' => '/Sources/{{ spec.info.title | caseUcfirst}}/Operator.swift',
Expand Down
5 changes: 5 additions & 0 deletions src/SDK/Language/Unity.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ public function getFiles(): array
'destination' => 'Assets/Runtime/Core/Query.cs',
'template' => 'dotnet/Package/Query.cs.twig',
],
[
'scope' => 'default',
'destination' => 'Assets/Runtime/Core/Query/Builder.cs',
'template' => 'dotnet/Package/Query/Builder.cs.twig',
],
[
'scope' => 'default',
'destination' => 'Assets/Runtime/Core/Role.cs',
Expand Down
2 changes: 2 additions & 0 deletions src/SDK/SDK.php
Original file line number Diff line number Diff line change
Expand Up @@ -1697,13 +1697,15 @@ protected function getSchemaDependencies(Schema $schema): array

protected function getEnumName(Schema|Parameter $value): string
{
$original = $this->getSchema($value);
$enumSchema = $this->language->getEnumSchema($value);
if ($enumSchema->enum === []) {
return '';
}
return (string) (($enumSchema instanceof StringSchema ? $enumSchema->enumName : null)
?? $enumSchema->title
?? $this->schemaEnumNames[\spl_object_id($enumSchema)]
?? $this->schemaEnumNames[\spl_object_id($original)]
?? ($value instanceof Parameter ? $value->name : ''));
}

Expand Down
47 changes: 45 additions & 2 deletions templates/android/library/src/main/java/io/package/Client.kt.twig
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,10 @@ class Client @JvmOverloads constructor(
headers: Map<String, String> = mapOf(),
params: Map<String, Any?> = mapOf(),
): Request {
val filteredParams = params.filterValues { it != null }
// flatten() resolves Query/Query.Builder values before the null filter,
// so a builder that renders to null is dropped like any other null.
val flattenedParams = params.mapValues { flatten(it.value) }
val filteredParams = flattenedParams.filterValues { it != null }

val requestHeaders = this.headers.toHeaders().newBuilder()
.addAll(headers.toHeaders())
Expand Down Expand Up @@ -398,7 +401,7 @@ class Client @JvmOverloads constructor(
}
builder.build()
} else {
params
flattenedParams
.toJsonRequestBody()
.toRequestBody("application/json".toMediaType())
}
Expand Down Expand Up @@ -742,4 +745,44 @@ class Client @JvmOverloads constructor(
}
})
}

fun flatten(value: Any?): Any? {
if (value is Query.Builder) {
return value.build()
}
if (value is Query) {
return value.toString()
}

val items = when (value) {
is Array<*> -> value.asList()
is Collection<*> -> value
else -> return value
}

val out = ArrayList<Any?>(items.size)
var changed = false
for (item in items) {
val flat = flatten(item)
if (item is Query.Builder || item is Query) {
if (flat is Collection<*>) {
out.addAll(flat)
} else {
out.add(flat)
}
changed = true
continue
}
if ((item is Array<*> || item is Collection<*>) && flat is Collection<*> && flat.all { it is String }) {
out.addAll(flat)
changed = true
continue
}
if (flat !== item) {
changed = true
}
out.add(flat)
}
return if (changed) out else value
}
}
Loading
Loading