Skip to content

Commit 2852bfb

Browse files
authored
Merge branch 'appwrite:master' into master
2 parents c74c9be + 0780426 commit 2852bfb

File tree

19 files changed

+1225
-7
lines changed

19 files changed

+1225
-7
lines changed

example.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ function getSSLPage($url) {
188188
->setDefaultHeaders([
189189
'X-Appwrite-Response-Format' => '1.7.0',
190190
])
191+
->setExclude([
192+
'services' => [
193+
['name' => 'assistant'],
194+
],
195+
])
191196
;
192197

193198
$sdk->generate(__DIR__ . '/examples/cli');

src/SDK/Language/CLI.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,51 @@ public function getFiles(): array
181181
'destination' => 'lib/sdks.js',
182182
'template' => 'cli/lib/sdks.js.twig',
183183
],
184+
[
185+
'scope' => 'default',
186+
'destination' => 'lib/type-generation/attribute.js',
187+
'template' => 'cli/lib/type-generation/attribute.js.twig',
188+
],
189+
[
190+
'scope' => 'default',
191+
'destination' => 'lib/type-generation/languages/language.js',
192+
'template' => 'cli/lib/type-generation/languages/language.js.twig',
193+
],
194+
[
195+
'scope' => 'default',
196+
'destination' => 'lib/type-generation/languages/php.js',
197+
'template' => 'cli/lib/type-generation/languages/php.js.twig',
198+
],
199+
[
200+
'scope' => 'default',
201+
'destination' => 'lib/type-generation/languages/typescript.js',
202+
'template' => 'cli/lib/type-generation/languages/typescript.js.twig',
203+
],
204+
[
205+
'scope' => 'default',
206+
'destination' => 'lib/type-generation/languages/javascript.js',
207+
'template' => 'cli/lib/type-generation/languages/javascript.js.twig',
208+
],
209+
[
210+
'scope' => 'default',
211+
'destination' => 'lib/type-generation/languages/kotlin.js',
212+
'template' => 'cli/lib/type-generation/languages/kotlin.js.twig',
213+
],
214+
[
215+
'scope' => 'default',
216+
'destination' => 'lib/type-generation/languages/swift.js',
217+
'template' => 'cli/lib/type-generation/languages/swift.js.twig',
218+
],
219+
[
220+
'scope' => 'default',
221+
'destination' => 'lib/type-generation/languages/java.js',
222+
'template' => 'cli/lib/type-generation/languages/java.js.twig',
223+
],
224+
[
225+
'scope' => 'default',
226+
'destination' => 'lib/type-generation/languages/dart.js',
227+
'template' => 'cli/lib/type-generation/languages/dart.js.twig',
228+
],
184229
[
185230
'scope' => 'default',
186231
'destination' => 'lib/questions.js',
@@ -275,6 +320,11 @@ public function getFiles(): array
275320
'scope' => 'default',
276321
'destination' => 'lib/commands/organizations.js',
277322
'template' => 'cli/lib/commands/organizations.js.twig',
323+
],
324+
[
325+
'scope' => 'default',
326+
'destination' => 'lib/commands/types.js',
327+
'template' => 'cli/lib/commands/types.js.twig',
278328
]
279329
];
280330
}

src/SDK/Language/Swift.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,9 @@ public function getFilters(): array
462462
new TwigFilter('propertyType', function (array $property, array $spec, string $generic = 'T') {
463463
return $this->getPropertyType($property, $spec, $generic);
464464
}),
465+
new TwigFilter('isAnyCodableArray', function (array $property, array $spec) {
466+
return $this->isAnyCodableArray($property, $spec);
467+
}),
465468
new TwigFilter('hasGenericType', function (string $model, array $spec) {
466469
return $this->hasGenericType($model, $spec);
467470
}),
@@ -533,6 +536,18 @@ protected function getPropertyType(array $property, array $spec, string $generic
533536
return $type;
534537
}
535538

539+
/**
540+
* Check if a property is an array that results in [AnyCodable] type
541+
*
542+
* @param array $property
543+
* @param array $spec
544+
* @return bool
545+
*/
546+
protected function isAnyCodableArray(array $property, array $spec): bool
547+
{
548+
return $property['type'] === 'array' && $this->getPropertyType($property, $spec, 'T') === '[AnyCodable]';
549+
}
550+
536551
protected function hasGenericType(?string $model, array $spec): string
537552
{
538553
if (empty($model) || $model === 'any') {

src/SDK/SDK.php

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ class SDK
7070
'test' => 'false'
7171
];
7272

73+
/**
74+
* @var array
75+
*/
76+
protected array $excludeRules = [
77+
'services' => [],
78+
'methods' => [],
79+
'definitions' => []
80+
];
81+
7382
/**
7483
* SDK constructor.
7584
*
@@ -528,6 +537,58 @@ public function getParams(): array
528537
return $this->params;
529538
}
530539

540+
/**
541+
* Get services filtered by exclusion rules
542+
*
543+
* @return array
544+
*/
545+
protected function getFilteredServices(): array
546+
{
547+
$allServices = $this->spec->getServices();
548+
$filteredServices = [];
549+
550+
// Extract exclusion rules for services
551+
$excludeServices = [];
552+
$excludeFeatures = [];
553+
foreach ($this->excludeRules['services'] ?? [] as $service) {
554+
if (isset($service['name'])) {
555+
$excludeServices[] = $service['name'];
556+
}
557+
if (isset($service['feature'])) {
558+
$excludeFeatures[] = $service['feature'];
559+
}
560+
}
561+
562+
foreach ($allServices as $serviceName => $service) {
563+
// Check if service is excluded by name
564+
if (in_array($serviceName, $excludeServices)) {
565+
continue;
566+
}
567+
568+
// Check if service is excluded by feature
569+
$methods = $this->spec->getMethods($serviceName);
570+
$serviceFeatures = [
571+
'upload' => $this->hasUploads($methods),
572+
'location' => $this->hasLocation($methods),
573+
'webAuth' => $this->hasWebAuth($methods),
574+
];
575+
576+
$shouldExclude = false;
577+
foreach ($excludeFeatures as $feature) {
578+
if ($serviceFeatures[$feature] ?? false) {
579+
$shouldExclude = true;
580+
break;
581+
}
582+
}
583+
584+
if (!$shouldExclude) {
585+
$filteredServices[$serviceName] = $service;
586+
}
587+
}
588+
589+
return $filteredServices;
590+
}
591+
531592
/**
532593
* @param string $target
533594
* @throws Throwable
@@ -552,7 +613,7 @@ public function generate(string $target): void
552613
'contactName' => $this->spec->getContactName(),
553614
'contactURL' => $this->spec->getContactURL(),
554615
'contactEmail' => $this->spec->getContactEmail(),
555-
'services' => $this->spec->getServices(),
616+
'services' => $this->getFilteredServices(),
556617
'enums' => $this->spec->getEnums(),
557618
'definitions' => $this->spec->getDefinitions(),
558619
'global' => [
@@ -586,7 +647,7 @@ public function generate(string $target): void
586647
copy(realpath(__DIR__ . '/../../templates/' . $file['template']), $destination);
587648
break;
588649
case 'service':
589-
foreach ($this->spec->getServices() as $key => $service) {
650+
foreach ($this->getFilteredServices() as $key => $service) {
590651
$methods = $this->spec->getMethods($key);
591652
$params['service'] = [
592653
'globalParams' => $service['globalParams'] ?? [],
@@ -619,7 +680,7 @@ public function generate(string $target): void
619680
}
620681
break;
621682
case 'method':
622-
foreach ($this->spec->getServices() as $key => $service) {
683+
foreach ($this->getFilteredServices() as $key => $service) {
623684
$methods = $this->spec->getMethods($key);
624685
$params['service'] = [
625686
'name' => $key,
@@ -654,6 +715,28 @@ public function generate(string $target): void
654715
}
655716
}
656717

718+
/**
719+
* Add additional exclusion rules for services, methods, or definitions.
720+
*
721+
* @param array $rules Array containing exclusion rules with format:
722+
* [
723+
* 'services' => [['name' => 'serviceName'], ['feature' => 'featureName']],
724+
* 'methods' => [['name' => 'methodName'], ['type' => 'methodType']],
725+
* 'definitions' => [['name' => 'definitionName']]
726+
* ]
727+
* @return $this
728+
*/
729+
public function setExclude(array $rules): SDK
730+
{
731+
foreach (['services', 'methods', 'definitions'] as $type) {
732+
if (isset($rules[$type]) && is_array($rules[$type])) {
733+
$this->excludeRules[$type] = array_merge($this->excludeRules[$type], $rules[$type]);
734+
}
735+
}
736+
737+
return $this;
738+
}
739+
657740
/**
658741
* Determine if a file should be excluded from generation.
659742
*
@@ -668,7 +751,7 @@ public function generate(string $target): void
668751
*/
669752
protected function exclude($file, $params): bool
670753
{
671-
$exclude = $file['exclude'] ?? [];
754+
$exclude = array_merge_recursive($file['exclude'] ?? [], $this->excludeRules);
672755

673756
$services = [];
674757
$features = [];

templates/cli/index.js.twig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const inquirer = require("inquirer");
1414
{% if sdk.test != "true" %}
1515
const { login, logout, whoami, migrate, register } = require("./lib/commands/generic");
1616
const { init } = require("./lib/commands/init");
17+
const { types } = require("./lib/commands/types");
1718
const { pull } = require("./lib/commands/pull");
1819
const { run } = require("./lib/commands/run");
1920
const { push, deploy } = require("./lib/commands/push");
@@ -68,6 +69,7 @@ program
6869
.addCommand(init)
6970
.addCommand(pull)
7071
.addCommand(push)
72+
.addCommand(types)
7173
.addCommand(deploy)
7274
.addCommand(run)
7375
.addCommand(logout)
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
const ejs = require("ejs");
2+
const fs = require("fs");
3+
const path = require("path");
4+
const { LanguageMeta, detectLanguage } = require("../type-generation/languages/language");
5+
const { Command, Option, Argument } = require("commander");
6+
const { localConfig } = require("../config");
7+
const { success, log, actionRunner } = require("../parser");
8+
const { PHP } = require("../type-generation/languages/php");
9+
const { TypeScript } = require("../type-generation/languages/typescript");
10+
const { Kotlin } = require("../type-generation/languages/kotlin");
11+
const { Swift } = require("../type-generation/languages/swift");
12+
const { Java } = require("../type-generation/languages/java");
13+
const { Dart } = require("../type-generation/languages/dart");
14+
const { JavaScript } = require("../type-generation/languages/javascript");
15+
16+
/**
17+
* @param {string} language
18+
* @returns {import("../type-generation/languages/language").LanguageMeta}
19+
*/
20+
function createLanguageMeta(language) {
21+
switch (language) {
22+
case "ts":
23+
return new TypeScript();
24+
case "js":
25+
return new JavaScript();
26+
case "php":
27+
return new PHP();
28+
case "kotlin":
29+
return new Kotlin();
30+
case "swift":
31+
return new Swift();
32+
case "java":
33+
return new Java();
34+
case "dart":
35+
return new Dart();
36+
default:
37+
throw new Error(`Language '${language}' is not supported`);
38+
}
39+
}
40+
41+
const templateHelpers = {
42+
toPascalCase: LanguageMeta.toPascalCase,
43+
toCamelCase: LanguageMeta.toCamelCase,
44+
toSnakeCase: LanguageMeta.toSnakeCase,
45+
toKebabCase: LanguageMeta.toKebabCase,
46+
toUpperSnakeCase: LanguageMeta.toUpperSnakeCase
47+
}
48+
49+
const typesOutputArgument = new Argument(
50+
"<output-directory>",
51+
"The directory to write the types to"
52+
);
53+
54+
const typesLanguageOption = new Option(
55+
"-l, --language <language>",
56+
"The language of the types"
57+
)
58+
.choices(["auto", "ts", "js", "php", "kotlin", "swift", "java", "dart"])
59+
.default("auto");
60+
61+
const typesCommand = actionRunner(async (rawOutputDirectory, {language}) => {
62+
if (language === "auto") {
63+
language = detectLanguage();
64+
log(`Detected language: ${language}`);
65+
}
66+
67+
const meta = createLanguageMeta(language);
68+
69+
const outputDirectory = path.resolve(rawOutputDirectory);
70+
if (!fs.existsSync(outputDirectory)) {
71+
log(`Directory: ${outputDirectory} does not exist, creating...`);
72+
fs.mkdirSync(outputDirectory, { recursive: true });
73+
}
74+
75+
if (!fs.existsSync("appwrite.json")) {
76+
throw new Error("appwrite.json not found in current directory");
77+
}
78+
79+
const collections = localConfig.getCollections();
80+
if (collections.length === 0) {
81+
throw new Error("No collections found in appwrite.json");
82+
}
83+
84+
log(`Found ${collections.length} collections: ${collections.map(c => c.name).join(", ")}`);
85+
86+
const totalAttributes = collections.reduce((count, collection) => count + collection.attributes.length, 0);
87+
log(`Found ${totalAttributes} attributes across all collections`);
88+
89+
const templater = ejs.compile(meta.getTemplate());
90+
91+
if (meta.isSingleFile()) {
92+
const content = templater({
93+
collections,
94+
...templateHelpers,
95+
getType: meta.getType
96+
});
97+
98+
const destination = path.join(outputDirectory, meta.getFileName());
99+
100+
fs.writeFileSync(destination, content);
101+
log(`Added types to ${destination}`);
102+
} else {
103+
for (const collection of collections) {
104+
const content = templater({
105+
collection,
106+
...templateHelpers,
107+
getType: meta.getType
108+
});
109+
110+
const destination = path.join(outputDirectory, meta.getFileName(collection));
111+
112+
fs.writeFileSync(destination, content);
113+
log(`Added types for ${collection.name} to ${destination}`);
114+
}
115+
}
116+
117+
success(`Generated types for all the listed collections`);
118+
});
119+
120+
const types = new Command("types")
121+
.description("Generate types for your Appwrite project")
122+
.addArgument(typesOutputArgument)
123+
.addOption(typesLanguageOption)
124+
.action(actionRunner(typesCommand));
125+
126+
module.exports = { types };

templates/cli/lib/parser.js.twig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ const commandDescriptions = {
221221
"whoami": `The whomai command gives information about the currently logged-in user.`,
222222
"register": `Outputs the link to create an {{ spec.title|caseUcfirst }} account.`,
223223
"console" : `The console command gives you access to the APIs used by the Appwrite Console.`,
224-
"assistant": `The assistant command allows you to interact with the Appwrite Assistant AI`,
225224
"messaging": `The messaging command allows you to manage topics and targets and send messages.`,
226225
"migrations": `The migrations command allows you to migrate data between services.`,
227226
"vcs": `The vcs command allows you to interact with VCS providers and manage your code repositories.`,

0 commit comments

Comments
 (0)