Skip to content

Commit 07f82e7

Browse files
Merge pull request #326 from appwrite/feat-partial-argument-fixes
fix(web): typed arguments and service classes
2 parents 093486f + 1c67d71 commit 07f82e7

18 files changed

+550
-309
lines changed

specs/swagger-appwrite-0.12.0.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

specs/swagger-appwrite.0.12.0.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

specs/swagger2-latest-console.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/SDK/Language.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,13 @@ public function getParams()
7474
{
7575
return $this->params;
7676
}
77+
78+
/**
79+
* Language specific filters.
80+
* @return array
81+
*/
82+
public function getFilters(): array
83+
{
84+
return [];
85+
}
7786
}

src/SDK/Language/Web.php

Lines changed: 204 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace Appwrite\SDK\Language;
44

5-
class Web extends JS {
5+
use Twig\TwigFilter;
6+
7+
class Web extends JS
8+
{
69

710
/**
811
* @return string
@@ -20,8 +23,38 @@ public function getFiles()
2023
return [
2124
[
2225
'scope' => 'default',
23-
'destination' => 'src/sdk.ts',
24-
'template' => '/web/src/sdk.ts.twig',
26+
'destination' => 'src/index.ts',
27+
'template' => 'web/src/index.ts.twig',
28+
'minify' => false,
29+
],
30+
[
31+
'scope' => 'default',
32+
'destination' => 'src/client.ts',
33+
'template' => 'web/src/client.ts.twig',
34+
'minify' => false,
35+
],
36+
[
37+
'scope' => 'default',
38+
'destination' => 'src/service.ts',
39+
'template' => 'web/src/service.ts.twig',
40+
'minify' => false,
41+
],
42+
[
43+
'scope' => 'service',
44+
'destination' => 'src/services/{{service.name | caseDash}}.ts',
45+
'template' => 'web/src/services/template.ts.twig',
46+
'minify' => false,
47+
],
48+
[
49+
'scope' => 'default',
50+
'destination' => 'src/models.ts',
51+
'template' => 'web/src/models.ts.twig',
52+
'minify' => false,
53+
],
54+
[
55+
'scope' => 'default',
56+
'destination' => 'src/query.ts',
57+
'template' => 'web/src/query.ts.twig',
2558
'minify' => false,
2659
],
2760
[
@@ -98,7 +131,7 @@ public function getParamExample(array $param)
98131

99132
$output = '';
100133

101-
if(empty($example) && $example !== 0 && $example !== false) {
134+
if (empty($example) && $example !== 0 && $example !== false) {
102135
switch ($type) {
103136
case self::TYPE_NUMBER:
104137
case self::TYPE_INTEGER:
@@ -118,8 +151,7 @@ public function getParamExample(array $param)
118151
$output .= "document.getElementById('uploader').files[0]";
119152
break;
120153
}
121-
}
122-
else {
154+
} else {
123155
switch ($type) {
124156
case self::TYPE_NUMBER:
125157
case self::TYPE_INTEGER:
@@ -141,4 +173,170 @@ public function getParamExample(array $param)
141173

142174
return $output;
143175
}
176+
177+
public function getTypeName($type, $method = []): string
178+
{
179+
switch ($type) {
180+
case self::TYPE_INTEGER:
181+
case self::TYPE_NUMBER:
182+
return 'number';
183+
break;
184+
case self::TYPE_ARRAY:
185+
return 'string[]';
186+
case self::TYPE_FILE:
187+
return 'File';
188+
case self::TYPE_OBJECT:
189+
if (empty($method)) {
190+
return $type;
191+
}
192+
193+
switch ($method['responseModel']) {
194+
case 'user':
195+
return "Partial<Preferences>";
196+
break;
197+
case 'document':
198+
if ($method['method'] === 'post') {
199+
return "Omit<Document, keyof Models.Document>";
200+
}
201+
if ($method['method'] === 'patch') {
202+
return "Partial<Omit<Document, keyof Models.Document>>";
203+
}
204+
}
205+
break;
206+
}
207+
208+
return $type;
209+
}
210+
211+
protected function populateGenerics(string $model, array $spec, array &$generics, bool $skipFirst = false)
212+
{
213+
if (!$skipFirst && $spec['definitions'][$model]['additionalProperties']) {
214+
$generics[] = $this->toUpperCase($model);
215+
}
216+
217+
$properties = $spec['definitions'][$model]['properties'];
218+
219+
foreach ($properties as $property) {
220+
if (array_key_exists('sub_schema', $property) && $property['sub_schema']) {
221+
$this->populateGenerics($property['sub_schema'], $spec, $generics, false);
222+
}
223+
}
224+
}
225+
226+
public function getGenerics(string $model, array $spec, bool $skipFirst = false): string
227+
{
228+
$generics = [];
229+
230+
if (array_key_exists($model, $spec['definitions'])) {
231+
$this->populateGenerics($model, $spec, $generics, $skipFirst);
232+
}
233+
234+
if (empty($generics)) return '';
235+
236+
$generics = array_unique($generics);
237+
$generics = array_map(fn ($type) => "{$type} extends Models.{$type}", $generics);
238+
239+
return '<' . implode(', ', $generics) . '>';
240+
}
241+
242+
public function getReturn(array $method, array $spec): string
243+
{
244+
if ($method['type'] === 'webAuth') {
245+
return 'void | URL';
246+
} elseif ($method['type'] === 'location') {
247+
return 'URL';
248+
}
249+
250+
if (array_key_exists('responseModel', $method) && !empty($method['responseModel']) && $method['responseModel'] !== 'any') {
251+
$ret = 'Promise<';
252+
253+
if (
254+
array_key_exists($method['responseModel'], $spec['definitions']) &&
255+
array_key_exists('additionalProperties', $spec['definitions'][$method['responseModel']]) &&
256+
!$spec['definitions'][$method['responseModel']]['additionalProperties']
257+
) {
258+
$ret .= 'Models.';
259+
}
260+
261+
$ret .= $this->toUpperCase($method['responseModel']);
262+
263+
$models = [];
264+
265+
if ($method['responseModel']) {
266+
$this->populateGenerics($method['responseModel'], $spec, $models);
267+
}
268+
269+
$models = array_unique($models);
270+
$models = array_filter($models, fn ($model) => $model != $this->toUpperCase($method['responseModel']));
271+
272+
if (!empty($models)) {
273+
$ret .= '<' . implode(', ', $models) . '>';
274+
}
275+
276+
$ret .= '>';
277+
278+
return $ret;
279+
} else {
280+
return 'Promise<{}>';
281+
}
282+
283+
return "";
284+
}
285+
286+
public function toUpperCase(string $value): string
287+
{
288+
return ucfirst((string)$this->helperCamelCase($value));
289+
}
290+
291+
protected function helperCamelCase($str)
292+
{
293+
$str = preg_replace('/[^a-z0-9' . implode("", []) . ']+/i', ' ', $str);
294+
$str = trim($str);
295+
$str = ucwords($str);
296+
$str = str_replace(" ", "", $str);
297+
$str = lcfirst($str);
298+
299+
return $str;
300+
}
301+
302+
public function getSubSchema(array $property, array $spec): string
303+
{
304+
if (array_key_exists('sub_schema', $property)) {
305+
$ret = '';
306+
$generics = [];
307+
$this->populateGenerics($property['sub_schema'], $spec, $generics);
308+
309+
$generics = array_filter($generics, fn ($model) => $model != $this->toUpperCase($property['sub_schema']));
310+
311+
$ret .= $this->toUpperCase($property['sub_schema']);
312+
if (!empty($generics)) {
313+
$ret .= '<' . implode(', ', $generics) . '>';
314+
}
315+
if ($property['type'] === 'array') {
316+
$ret .= '[]';
317+
}
318+
319+
return $ret;
320+
}
321+
322+
return $this->getTypeName($property['type']);
323+
}
324+
325+
public function getFilters(): array
326+
{
327+
return [
328+
new TwigFilter('getPropertyType', function ($value, $method = []) {
329+
return $this->getTypeName($value, $method);
330+
}),
331+
new TwigFilter('getSubSchema', function (array $property, array $spec) {
332+
return $this->getSubSchema($property, $spec);
333+
}),
334+
new TwigFilter('getGenerics', function (string $model, array $spec, bool $skipAdditional = false) {
335+
return $this->getGenerics($model, $spec, $skipAdditional);
336+
}),
337+
new TwigFilter('getReturn', function (array $method, array $spec) {
338+
return $this->getReturn($method, $spec);
339+
}),
340+
];
341+
}
144342
}

src/SDK/SDK.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ public function __construct(Language $language, Spec $spec)
7575
$this->twig = new Environment(new FilesystemLoader(__DIR__ . '/../../templates'), [
7676
'debug' => true
7777
] );
78+
79+
/**
80+
* Add language specific filters
81+
*/
82+
foreach ($this->language->getFilters() as $filter) {
83+
$this->twig->addFilter($filter);
84+
}
85+
7886
$this->twig->addExtension(new \Twig\Extension\DebugExtension());
7987

8088
$this->twig->addFilter(new TwigFilter('caseLower', function ($value) {

templates/web/package.json.twig

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"./package.json": "./package.json"
1414
},
1515
"module": "dist/esm/sdk.js",
16-
"types": "types/sdk.d.ts",
16+
"types": "types/index.d.ts",
1717
"repository": {
1818
"type": "git",
1919
"url": "{{ sdk.gitURL }}"
@@ -24,12 +24,12 @@
2424
"build:libs": "rollup -c"
2525
},
2626
"devDependencies": {
27-
"@rollup/plugin-typescript": "8.2.5",
27+
"@rollup/plugin-typescript": "8.3.2",
2828
"playwright": "1.15.0",
29-
"rollup": "2.56.3",
30-
"serve-handler": "6.1.3",
31-
"tslib": "2.3.1",
32-
"typescript": "4.2.3"
29+
"rollup": "2.75.4",
30+
"serve-handler": "6.1.0",
31+
"tslib": "2.4.0",
32+
"typescript": "4.7.2"
3333
},
3434
"dependencies": {
3535
"cross-fetch": "3.1.5",

templates/web/rollup.config.js.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import typescript from "@rollup/plugin-typescript";
33

44
export default {
55
external: Object.keys(pkg.dependencies),
6-
input: "src/sdk.ts",
6+
input: "src/index.ts",
77
plugins: [typescript()],
88
output: [
99
{
@@ -20,7 +20,7 @@ export default {
2020
{
2121
format: "iife",
2222
file: pkg.jsdelivr,
23-
name: "window",
23+
name: "Appwrite",
2424
extend: true,
2525
globals: {
2626
"cross-fetch": "window",

0 commit comments

Comments
 (0)