Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion example.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ function buildStaticSpec(): StaticSpec {
// Python
if (!$requestedSdk || $requestedSdk === 'python') {
$sdk = new SDK(new Python(), buildSpec($specFormat, $spec));
configureSDK($sdk);
configureSDK($sdk, ['platform' => $platform]);
$sdk->generate(__DIR__ . '/examples/python');
}

Expand Down
40 changes: 33 additions & 7 deletions src/SDK/Language/Python.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

class Python extends Language
{
#[Override]
protected $params = [
'pipPackage' => 'packageName',
];
Expand Down Expand Up @@ -156,6 +155,11 @@ public function getFiles(): array
'destination' => '{{ spec.title | caseSnake}}/client.py',
'template' => 'python/package/client.py.twig',
],
[
'scope' => 'default',
'destination' => 'test/test_client.py',
'template' => 'python/test/test_client.py.twig',
],
[
'scope' => 'default',
'destination' => '{{ spec.title | caseSnake}}/permission.py',
Expand Down Expand Up @@ -304,6 +308,27 @@ public function getFiles(): array
];
}

protected function isModelDefined(string $name, array $spec): bool
{
if (empty($name) || $spec === []) {
return false;
}
$pascal = $this->toPascalCase($name);
foreach (($spec['definitions'] ?? []) as $key => $definition) {
$modelName = \is_array($definition) && isset($definition['name']) ? $definition['name'] : (string) $key;
if ($this->toPascalCase($modelName) === $pascal) {
return true;
}
}
foreach (($spec['requestModels'] ?? []) as $key => $requestModel) {
$modelName = \is_array($requestModel) && isset($requestModel['name']) ? $requestModel['name'] : (string) $key;
if ($this->toPascalCase($modelName) === $pascal) {
return true;
}
}
return false;
}

/**
* @throws Exception
*/
Expand All @@ -319,11 +344,11 @@ public function getTypeName(array $parameter, array $spec = []): string
? \ucfirst($parameter['enumName'])
: \ucfirst((string) $parameter['name']);

$typeName = 'List[' . $enumType . ']';
$typeName = 'List[' . $enumType . 'Enum]';
} elseif (isset($parameter['enumName'])) {
$typeName = \ucfirst($parameter['enumName']);
$typeName = \ucfirst($parameter['enumName']) . 'Enum';
} elseif (!empty($parameter['enumValues'])) {
$typeName = \ucfirst((string) $parameter['name']);
$typeName = \ucfirst((string) $parameter['name']) . 'Enum';
} elseif (!empty($parameter['array']['model'])) {
$typeName = 'List[' . $this->toPascalCase($parameter['array']['model']) . ']';
} elseif (!empty($parameter['model'])) {
Expand Down Expand Up @@ -647,15 +672,15 @@ protected function getServiceModelTypeName(string $modelName, string $serviceNam
return $modelType;
}

protected function getServicePropertyType(array $parameter, string $serviceName): string
protected function getServicePropertyType(array $parameter, string $serviceName, array $spec = []): string
{
if (!empty($parameter['array']['model'])) {
$typeName = 'List[' . $this->getServiceModelTypeName($parameter['array']['model'], $serviceName) . ']';
} elseif (!empty($parameter['model'])) {
$modelType = $this->getServiceModelTypeName($parameter['model'], $serviceName);
$typeName = ($parameter['type'] ?? '') === self::TYPE_ARRAY ? 'List[' . $modelType . ']' : $modelType;
} else {
return $this->getTypeName($parameter);
return $this->getTypeName($parameter, $spec);
}

if (!($parameter['required'] ?? true) || ($parameter['nullable'] ?? false)) {
Expand Down Expand Up @@ -762,7 +787,8 @@ public function getFilters(): array
new TwigFilter('getPropertyType', fn(array $value, array $method = []): string => $this->getTypeName($value, $method)),
new TwigFilter('hasGenericType', fn(string $model, array $spec): bool => $this->hasGenericType($model, $spec)),
new TwigFilter('hasGenericTypeProperty', fn(array $properties, array $spec): bool => array_any($properties, fn($property): bool => !empty($property['sub_schema']) && $this->hasGenericType($property['sub_schema'], $spec))),
new TwigFilter('getServicePropertyType', fn(array $value, string $serviceName): string => $this->getServicePropertyType($value, $serviceName)),
new TwigFilter('hasModelDefinition', fn(string $name, array $spec): bool => $this->isModelDefined($name, $spec)),
new TwigFilter('getServicePropertyType', fn(array $value, string $serviceName, array $spec = []): string => $this->getServicePropertyType($value, $serviceName, $spec)),
new TwigFilter('getModelPropertyType', fn(array $value, string $ownerName = ''): string => $this->getModelPropertyType($value, $ownerName)),
new TwigFilter('getModelFieldName', fn(array $value, array $properties): string => $this->getModelFieldName($value, $properties)),
new TwigFilter('getResponseType', fn(array $method, string $serviceName = ''): string => $this->getResponseType($method, $serviceName)),
Expand Down
41 changes: 41 additions & 0 deletions templates/python/base/requests/api_async.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
response = await self.client.call_async('{{ method.method | caseLower }}', api_path, {
{%~ for key, security in method.securityHeaders %}
'{{ security.name }}': self.client.get_config('{{ key | caseLower }}'),
{%~ endfor %}
{% for parameter in method.parameters.header %}
'{{ parameter.name }}': self._normalize_value({{ parameter.name | escapeKeyword | caseSnake }}),
{% endfor %}
{% for key, header in method.headers %}
'{{ key }}': '{{ header }}',
{% endfor %}
}, api_params{% if method.type == 'webAuth' %}, response_type='location'{% endif %})
{% if method.responseModels is defined and method.responseModels|length > 1 %}
{% set hasResponseDiscriminator = method.responseDiscriminator is defined and method.responseDiscriminator is not empty %}
{% set responseDiscriminator = method.responseDiscriminator ?? {} %}
{% if hasResponseDiscriminator %}
if not isinstance(response, dict):
raise AppwriteException('Expected object response when hydrating a response model')
{% for modelName, conditions in responseDiscriminator %}

if {% for field, expectedValue in conditions %}response.get('{{ field }}') == '{{ expectedValue }}'{% if not loop.last %} and {% endif %}{% endfor %}:
return self._parse_response(response, model={% if (modelName | caseUcfirst) == (service.name | caseUcfirst) %}{{ modelName | caseUcfirst }}Model{% else %}{{ modelName | caseUcfirst }}{% endif %})
{% endfor %}

raise AppwriteException('Unable to match response to any known model')
{% else %}

return self._parse_response(response, model={% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %})
{% endif %}
{% elseif method.responseModel and method.responseModel != 'any' %}
{% set isGenericResponse = method.responseModel | hasGenericType(spec) %}
{% if isGenericResponse %}

return {% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %}.with_data(response, model_type)
{% else %}

return self._parse_response(response, model={% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %})
{% endif %}
{% else %}

return response
{% endif %}
6 changes: 6 additions & 0 deletions templates/python/base/requests/file.twig
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,14 @@
return self._parse_response(response, model={% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %})
{% endif %}
{% elseif method.responseModel and method.responseModel != 'any' %}
{% set isGenericResponse = method.responseModel | hasGenericType(spec) %}
{% if isGenericResponse %}

return {% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %}.with_data(response, model_type)
{% else %}

return self._parse_response(response, model={% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %})
{% endif %}
{% else %}

return response
Expand Down
62 changes: 62 additions & 0 deletions templates/python/base/requests/file_async.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{% for parameter in method.parameters.all %}
{% if parameter.type == 'file' %}
param_name = '{{ parameter.name }}'

{% endif %}
{% endfor %}

upload_id = ''
{% for parameter in method.parameters.all %}
{% if parameter.isUploadID %}
upload_id = {{ parameter.name | escapeKeyword | caseSnake }}
{% endif %}
{% endfor %}

response = await self.client.chunked_upload_async(
api_path,
{
{%~ for key, security in method.securityHeaders %}
'{{ security.name }}': self.client.get_config('{{ key | caseLower }}'),
{%~ endfor %}
{% for parameter in method.parameters.header %}
'{{ parameter.name }}': self._normalize_value({{ parameter.name | escapeKeyword | caseSnake }}),
{% endfor %}
{% for key, header in method.headers %}
'{{ key }}': '{{ header }}',
{% endfor %}
},
api_params,
param_name,
on_progress,
upload_id
)
{% if method.responseModels is defined and method.responseModels|length > 1 %}
{% set hasResponseDiscriminator = method.responseDiscriminator is defined and method.responseDiscriminator is not empty %}
{% set responseDiscriminator = method.responseDiscriminator ?? {} %}
{% if hasResponseDiscriminator %}
if not isinstance(response, dict):
raise AppwriteException('Expected object response when hydrating a response model')
{% for modelName, conditions in responseDiscriminator %}

if {% for field, expectedValue in conditions %}response.get('{{ field }}') == '{{ expectedValue }}'{% if not loop.last %} and {% endif %}{% endfor %}:
return self._parse_response(response, model={% if (modelName | caseUcfirst) == (service.name | caseUcfirst) %}{{ modelName | caseUcfirst }}Model{% else %}{{ modelName | caseUcfirst }}{% endif %})
{% endfor %}

raise AppwriteException('Unable to match response to any known model')
{% else %}

return self._parse_response(response, model={% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %})
{% endif %}
{% elseif method.responseModel and method.responseModel != 'any' %}
{% set isGenericResponse = method.responseModel | hasGenericType(spec) %}
{% if isGenericResponse %}

return {{ method.responseModel | caseUcfirst }}.with_data(response, model_type)
{% else %}

return self._parse_response(response, model={% if (method.responseModel | caseUcfirst) == (service.name | caseUcfirst) %}{{ method.responseModel | caseUcfirst }}Model{% else %}{{ method.responseModel | caseUcfirst }}{% endif %})
{% endif %}
{% else %}

return response
{% endif %}
Loading
Loading