From c260ae9390ccd09b59a4440ff4a1b14e3fa08237 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 29 Oct 2021 23:56:11 +1300 Subject: [PATCH 01/62] Add dotnet double number type --- src/SDK/Language/DotNet.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index af3828710..b71bf2d64 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -141,22 +141,18 @@ public function getTypeName($type) switch ($type) { case self::TYPE_INTEGER: return 'int'; - break; + case self::TYPE_NUMBER: + return 'double'; case self::TYPE_STRING: return 'string'; - break; case self::TYPE_FILE: return 'FileInfo'; - break; case self::TYPE_BOOLEAN: return 'bool'; - break; case self::TYPE_ARRAY: return 'List'; - break; case self::TYPE_OBJECT: return 'object'; - break; } return $type; From 5a01e997e45b9a402b1bf1755be2175574eb9a56 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 29 Oct 2021 23:56:38 +1300 Subject: [PATCH 02/62] Use tuples in extensions --- .../src/Appwrite/Helpers/ExtensionMethods.cs | 14 +++++++------- .../dotnet/src/Appwrite/Models/Exception.cs.twig | 11 +++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs index 323137a7c..ab11f8d01 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs @@ -21,22 +21,22 @@ public static string ToJson(this Dictionary dict) public static string ToQueryString(this Dictionary parameters) { - List query = new List(); + var query = new List(); - foreach (KeyValuePair parameter in parameters) + foreach (var (key, value) in parameters) { - if (parameter.Value != null) + if (value != null) { - if (parameter.Value is List) + if (value is List) { - foreach(object entry in (dynamic) parameter.Value) + foreach(object entry in (dynamic) value) { - query.Add(parameter.Key + "[]=" + Uri.EscapeUriString(entry.ToString())); + query.Add(key + "[]=" + Uri.EscapeUriString(entry.ToString())); } } else { - query.Add(parameter.Key + "=" + Uri.EscapeUriString(parameter.Value.ToString())); + query.Add(key + "=" + Uri.EscapeUriString(value.ToString())); } } } diff --git a/templates/dotnet/src/Appwrite/Models/Exception.cs.twig b/templates/dotnet/src/Appwrite/Models/Exception.cs.twig index c53875da9..2fca3e544 100644 --- a/templates/dotnet/src/Appwrite/Models/Exception.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Exception.cs.twig @@ -4,10 +4,13 @@ namespace Appwrite { public class {{spec.title | caseUcfirst}}Exception : Exception { - public int? Code; - public string Response = null; - public {{spec.title | caseUcfirst}}Exception(string message = null, int? code = null, string response = null) - : base(message) + public int? Code { get; set; } + public string? Response { get; set; } = null; + + public {{spec.title | caseUcfirst}}Exception( + string message = null, + int? code = null, + string response = null) : base(message) { this.Code = code; this.Response = response; From 98f2362d45ba634afb9c0c44847bde66869f9986 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 29 Oct 2021 23:58:44 +1300 Subject: [PATCH 03/62] Add model template --- src/SDK/Language/DotNet.php | 6 ++ .../dotnet/src/Appwrite/Models/Model.cs.twig | 72 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 templates/dotnet/src/Appwrite/Models/Model.cs.twig diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index b71bf2d64..02f603ee7 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -359,6 +359,12 @@ public function getFiles() 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Services/{{service.name | caseUcfirst}}.cs', 'template' => 'dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig', 'minify' => false, + ], + [ + 'scope' => 'definition', + 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Models/{{definition.name | caseUcfirst}}.cs', + 'template' => 'dotnet/src/Appwrite/Models/Model.cs.twig', + 'minify' => false, ] ]; } diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig new file mode 100644 index 000000000..ec76e7d19 --- /dev/null +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -0,0 +1,72 @@ +{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% endmacro %} + +using System.Collections.Generic; +using System.Linq; + +namespace {{ spec.title | caseUcfirst }}.Models +{ + public class {{ definition.name | caseUcfirst }} + { +{% for property in definition.properties %} + public {{ _self.sub_schema(property) }} {{ property.name | caseUcfirst | escapeKeyword }} { get; set; } +{% endfor %} +{% if definition.additionalProperties %} + public Dictionary Data { get; set; } +{% endif %} + + public {{ definition.name | caseUcfirst }}( +{% for property in definition.properties %} + {{ _self.sub_schema(property) }} {{ property.name | caseCamel | escapeKeyword }}{% if not property.required %} = {{ property.default }}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + +{% endfor %} +{% if definition.additionalProperties %} + Dictionary data +{% endif %} + ) { +{% for property in definition.properties %} + {{ property.name | caseUcfirst | escapeKeyword }} = {{ property.name | caseCamel | escapeKeyword }}; +{% endfor %} +{% if definition.additionalProperties %} + Data = data +{% endif %} + } + + public static {{ definition.name | caseUcfirst }} From(Dictionary map) => new( +{% for property in definition.properties %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}map["{{ property.name }}"].Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)){% else %}{{property.sub_schema | caseUcfirst}}.From(map: map["{{property.name }}"]){% endif %}{% else %}map["{{ property.name }}"] as {{ property.type | typeName }}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + +{% endfor %} +{% if definition.additionalProperties %} + data: map["data"] +{% endif %} + ); + + public Dictionary ToMap() => new() + { +{% for property in definition.properties %} + { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ property.name | caseUcfirst | escapeKeyword | removeDollarSign }}.Select(it => it.ToMap()){% else %}{{property.name | caseUcfirst | escapeKeyword | removeDollarSign }}.ToMap(){% endif %}{% else %}{{property.name | caseUcfirst | escapeKeyword }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + +{% endfor %} +{% if definition.additionalProperties %} + { "data": Data } +{% endif %} + }; +{% if definition.additionalProperties %} + + public T ConvertTo(Func, T> fromJson) => + fromJson.Invoke(Data); +{% endif %} +{% for property in definition.properties %} +{% if property.sub_schema %} +{% for def in spec.definitions %} +{% if def.name == property.sub_schema and def.additionalProperties and property.type == 'array' %} + + public T ConvertTo(Func, T> fromJson) => + {{ property.name | caseUcfirst | escapeKeyword }}.Select(it => it.ConvertTo(fromJson)); + +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} + } +} \ No newline at end of file From 3251d8ecfaf81025cf6b3aa5df27672636cb6984 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 29 Oct 2021 23:58:56 +1300 Subject: [PATCH 04/62] WIP update client with response models --- templates/dotnet/src/Appwrite/Client.cs.twig | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index e34f399c7..093ed7340 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; @@ -80,7 +80,13 @@ namespace {{ spec.title | caseUcfirst }} return this; } - public async Task Call(string method, string path, Dictionary headers, Dictionary parameters) + public async Task Call( + string method, + string path, + Dictionary headers, + Dictionary parameters, + Func, T>? convert = null, + Type? responseType = null) { if (selfSigned) { @@ -176,7 +182,14 @@ namespace {{ spec.title | caseUcfirst }} throw new {{spec.title | caseUcfirst}}Exception(message, code, response.ToString()); } - return httpResponseMessage; + var dict = JsonConvert.DeserializeObject>(response.ToString()); + + if (convert != null) + { + return convert(dict); + } + + return default; } catch (System.Exception e) { From 27b7df8abc915f2643d0b244395b13c9ae6b140f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 29 Oct 2021 23:59:09 +1300 Subject: [PATCH 05/62] WIP update service with response models --- .../Appwrite/Services/ServiceTemplate.cs.twig | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 023be1d3e..4b290d033 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -12,7 +12,11 @@ {% macro methodNeedsSecurityParameters(method) %} {% if (method.type == "webAuth" or method.type == "location") and method.security|length > 0 %}{{ true }}{% else %}{{false}}{% endif %} {% endmacro %} +{% macro resultType(namespace, method) %} +{% if method.type == "webAuth" %}void{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}{{ namespace }}.Models.{{method.responseModel | caseUcfirst}}{% endif %} +{% endmacro %} +using System; using System.Collections.Generic; using System.IO; using System.Net.Http; @@ -31,15 +35,18 @@ namespace {{ spec.title | caseUcfirst }} {% endif %} {% if method.description %} /// -{{method.description|dotnetComment}} +{{ method.description | dotnetComment }} /// {% endif %} /// - public {% if method.type == "location" %}string{% else %}async Task{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters) }}) + public async Task{% if method.type != "webAuth" %}<{{ _self.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters) }}) { - string path = "{{ method.path }}"{% for parameter in method.parameters.path %}.Replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel | escapeKeyword }}){% endfor %}; + var path = "{{ method.path }}" +{% for parameter in method.parameters.path %} + .Replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel | escapeKeyword }}) +{% endfor -%}; - Dictionary parameters = new Dictionary() + var parameters = new Dictionary() { {% for parameter in method.parameters.query | merge(method.parameters.body) %} { "{{ parameter.name }}", {{ _self.map_parameter(parameter) }} }{% if not loop.last or _self.methodNeedsSecurityParameters(method) %},{% endif %} @@ -53,16 +60,37 @@ namespace {{ spec.title | caseUcfirst }} {% endfor %} {% endfor %} {% endif %} -{% if method.type == 'location' %} - return _client.GetEndPoint() + path + "?" + parameters.ToQueryString(); +{% if method.type == 'location' %} + return await _client.Call( + method: "{{ method.method | caseUpper }}", + path: path, + headers: headers, + parameters: parameters, + responseType: typeof({{ _self.resultType(spec.title, method) }})); {% else %} - Dictionary headers = new Dictionary() + var headers = new Dictionary() { {{ method.headers|map((header, key) => " { \"#{key}\", \"#{header}\" }")|join(',\n')|raw }} }; - return await _client.Call("{{ method.method | caseUpper }}", path, headers, parameters); +{% if method.responseModel %} + Func, {{ _self.resultType(spec.title, method) }}> convert = (Dictionary it) => { +{% if method.responseModel == 'any' %} + return it; +{% else %} + return {{ _self.resultType(spec.title, method) }}.From(map: it); +{% endif %} + } +{% endif %} + + return await _client.Call( + method: "{{ method.method | caseUpper }}", + path: path, + headers: headers, + parameters: parameters, + convert: convert, + responseType: typeof({{ _self.resultType(spec.title, method) }})); {% endif %} } {% if not loop.last %} From a4525a2808b8db7331d52dee72709844676d91ac Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Sat, 30 Oct 2021 00:06:51 +1300 Subject: [PATCH 06/62] Update target frameworks --- templates/dotnet/src/Appwrite/Appwrite.csproj.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index 7cec3a863..88ee3cd4f 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -1,6 +1,6 @@ - netcoreapp3.1;net461;netstandard2.0; + net5.0 {{spec.title}} {{sdk.version}} {{spec.contactName}} @@ -18,7 +18,7 @@ - + From 657b50903a9d2afa52d0711f2b9fc3ce36e9cf20 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 1 Nov 2021 23:04:15 +1300 Subject: [PATCH 07/62] Handle empty responses and non-returning methods --- templates/dotnet/docs/example.md.twig | 4 +- .../dotnet/src/Appwrite/Appwrite.csproj.twig | 1 + templates/dotnet/src/Appwrite/Client.cs.twig | 25 +++++++---- .../dotnet/src/Appwrite/Models/Model.cs.twig | 9 ++-- .../Appwrite/Services/ServiceTemplate.cs.twig | 43 +++++++++++-------- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/templates/dotnet/docs/example.md.twig b/templates/dotnet/docs/example.md.twig index a02ce8877..83b862800 100644 --- a/templates/dotnet/docs/example.md.twig +++ b/templates/dotnet/docs/example.md.twig @@ -1,6 +1,6 @@ using {{ spec.title | caseUcfirst }}; -Client client = new Client(); +var client = new Client(); {% if method.security|length > 0 %} client @@ -14,4 +14,4 @@ client {% endif %} {{ service.name | caseUcfirst }} {{ service.name | caseCamel }} = new {{ service.name | caseUcfirst }}(client); -{% if method.type == 'location' %}string{% else %}HttpResponseMessage{% endif %} result = await {{ service.name | caseCamel }}.{{ method.name | caseUcfirst }}({% for parameter in method.parameters.all %}{% if parameter.required %}{% if not loop.first %}, {% endif %}{{ parameter | paramExample }}{% endif %}{% endfor %}); +{% if method.type == 'location' %}byte[]{% else %}{{ method.responseModel }}{% endif %} result = await {{ service.name | caseCamel }}.{{ method.name | caseUcfirst }}({% for parameter in method.parameters.all %}{% if parameter.required %}{% if not loop.first %}, {% endif %}{{ parameter | paramExample }}{% endif %}{% endfor %}); diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index 88ee3cd4f..18fff9c38 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -14,6 +14,7 @@ git {{sdk.gitURL}} true + enable diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 093ed7340..15a73c697 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; @@ -80,6 +81,15 @@ namespace {{ spec.title | caseUcfirst }} return this; } + public Task Call( + string method, + string path, + Dictionary headers, + Dictionary parameters) + { + return Call(method, path, headers, parameters); + } + public async Task Call( string method, string path, @@ -93,29 +103,28 @@ namespace {{ spec.title | caseUcfirst }} ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; } - bool methodGet = "GET".Equals(method, StringComparison.InvariantCultureIgnoreCase); + var methodGet = "GET".Equals(method, StringComparison.InvariantCultureIgnoreCase); - string queryString = methodGet ? "?" + parameters.ToQueryString() : string.Empty; + var queryString = methodGet ? "?" + parameters.ToQueryString() : string.Empty; - HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(method), endPoint + path + queryString); + var request = new HttpRequestMessage(new HttpMethod(method), endPoint + path + queryString); if ("multipart/form-data".Equals(headers["content-type"], StringComparison.InvariantCultureIgnoreCase)) { - MultipartFormDataContent form = new MultipartFormDataContent(); + var form = new MultipartFormDataContent(); foreach (var parameter in parameters) { if (parameter.Key == "file") { - FileInfo fi = parameters["file"] as FileInfo; - + var fi = (FileInfo)parameters["file"]; var file = File.ReadAllBytes(fi.FullName); form.Add(new ByteArrayContent(file, 0, file.Length), "file", fi.Name); } - else if (parameter.Value is IEnumerable) + else if (parameter.Value is IEnumerable enumerable) { - List list = new List((IEnumerable) parameter.Value); + var list = new List(enumerable); for (int index = 0; index < list.Count; index++) { form.Add(new StringContent(list[index].ToString()), $"{parameter.Key}[{index}]"); diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index ec76e7d19..2911aaa35 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -1,7 +1,8 @@ {% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% endmacro %} -using System.Collections.Generic; +using System; using System.Linq; +using System.Collections.Generic; namespace {{ spec.title | caseUcfirst }}.Models { @@ -27,13 +28,13 @@ namespace {{ spec.title | caseUcfirst }}.Models {{ property.name | caseUcfirst | escapeKeyword }} = {{ property.name | caseCamel | escapeKeyword }}; {% endfor %} {% if definition.additionalProperties %} - Data = data + Data = data; {% endif %} } public static {{ definition.name | caseUcfirst }} From(Dictionary map) => new( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}map["{{ property.name }}"].Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)){% else %}{{property.sub_schema | caseUcfirst}}.From(map: map["{{property.name }}"]){% endif %}{% else %}map["{{ property.name }}"] as {{ property.type | typeName }}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}(map["{{ property.name }}"] as IEnumerable>).Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst}}.From(map: map["{{property.name }}"] as Dictionary){% endif %}{% else %}{% if property.type == 'integer' or property.type == 'number' or property.type == 'boolean' %}({{ property.type | typeName }})map["{{ property.name }}"]{% else %}map["{{ property.name }}"] as {{ property.type | typeName }}{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} @@ -48,7 +49,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endfor %} {% if definition.additionalProperties %} - { "data": Data } + { "data", Data } {% endif %} }; {% if definition.additionalProperties %} diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 4b290d033..a83397aef 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -1,6 +1,6 @@ {% macro parameter(parameter) %} {% if parameter.name == 'orderType' %}{{ 'OrderType orderType = OrderType.ASC' }}{% else %} -{{ parameter.type | typeName }}{% if not parameter.required and (parameter.type == 'boolean' or parameter.type == 'integer') %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{{ parameter | paramDefault }}{% endif %} +{{ parameter.type | typeName }}{% if not parameter.required and (parameter.type == 'boolean' or parameter.type == 'integer' or parameter.type == 'number') %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{{ parameter | paramDefault }}{% endif %} {% endmacro %} {% macro method_parameters(parameters) %} {% if parameters.all|length > 0 %}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% endif %} @@ -13,7 +13,7 @@ {% if (method.type == "webAuth" or method.type == "location") and method.security|length > 0 %}{{ true }}{% else %}{{false}}{% endif %} {% endmacro %} {% macro resultType(namespace, method) %} -{% if method.type == "webAuth" %}void{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}{{ namespace }}.Models.{{method.responseModel | caseUcfirst}}{% endif %} +{% if method.type == "webAuth" %}bool{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}Models.{{method.responseModel | caseUcfirst}}{% endif %} {% endmacro %} using System; @@ -39,12 +39,14 @@ namespace {{ spec.title | caseUcfirst }} /// {% endif %} /// - public async Task{% if method.type != "webAuth" %}<{{ _self.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters) }}) + public Task{% if method.type != "webAuth" %}<{{ _self.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters) }}) { - var path = "{{ method.path }}" + var path = "{{ method.path }}"{% if method.parameters.path | length == 0 %};{% endif %} + {% for parameter in method.parameters.path %} - .Replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel | escapeKeyword }}) -{% endfor -%}; + .Replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel | escapeKeyword }}){% if loop.last %};{% endif %} + +{% endfor %} var parameters = new Dictionary() { @@ -52,16 +54,23 @@ namespace {{ spec.title | caseUcfirst }} { "{{ parameter.name }}", {{ _self.map_parameter(parameter) }} }{% if not loop.last or _self.methodNeedsSecurityParameters(method) %},{% endif %} {% endfor %} - }; -{% if _self.methodNeedsSecurityParameters(method) %}{% for node in method.security %} +{% if _self.methodNeedsSecurityParameters(method) %} +{% for node in method.security %} {% for key,header in node|keys %} - // { "{{header|caseLower}}", _client.GetConfig().get("{{header|caseLower}}") }{% if not loop.last %},{% endif %} + { "{{header|caseLower}}", _client.GetConfig()["{{header|caseLower}}"] }{% if not loop.last or (loop.last and method.headers | length > 0) %},{% endif %} {% endfor %} {% endfor %} {% endif %} + }; + + var headers = new Dictionary() + { +{{ method.headers|map((header, key) => " { \"#{key}\", \"#{header}\" }")|join(',\n')|raw }} + }; + {% if method.type == 'location' %} - return await _client.Call( + return _client.Call( method: "{{ method.method | caseUpper }}", path: path, headers: headers, @@ -69,13 +78,9 @@ namespace {{ spec.title | caseUcfirst }} responseType: typeof({{ _self.resultType(spec.title, method) }})); {% else %} - var headers = new Dictionary() - { -{{ method.headers|map((header, key) => " { \"#{key}\", \"#{header}\" }")|join(',\n')|raw }} - }; - {% if method.responseModel %} - Func, {{ _self.resultType(spec.title, method) }}> convert = (Dictionary it) => { + static {{ _self.resultType(spec.title, method) }} convert(Dictionary it) + { {% if method.responseModel == 'any' %} return it; {% else %} @@ -84,13 +89,17 @@ namespace {{ spec.title | caseUcfirst }} } {% endif %} - return await _client.Call( + return _client.Call{% if method.type != 'webAuth' %}<{{ _self.resultType(spec.title, method) }}>{% endif %}( method: "{{ method.method | caseUpper }}", path: path, headers: headers, +{% if not method.responseModel %} + parameters: parameters); +{% else %} parameters: parameters, convert: convert, responseType: typeof({{ _self.resultType(spec.title, method) }})); +{% endif %} {% endif %} } {% if not loop.last %} From 4a857355c0b624d360f28832a7dfe3d3da587bd8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 1 Nov 2021 23:04:26 +1300 Subject: [PATCH 08/62] Fix model conversions --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 2911aaa35..42f743a4f 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -38,7 +38,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endfor %} {% if definition.additionalProperties %} - data: map["data"] + data: map["data"] as Dictionary {% endif %} ); @@ -63,7 +63,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% if def.name == property.sub_schema and def.additionalProperties and property.type == 'array' %} public T ConvertTo(Func, T> fromJson) => - {{ property.name | caseUcfirst | escapeKeyword }}.Select(it => it.ConvertTo(fromJson)); + (T){{ property.name | caseUcfirst | escapeKeyword }}.Select(it => it.ConvertTo(fromJson)); {% endif %} {% endfor %} From 4710e7b1ff4109310ba1dcb14da2dd0b5ec03c15 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 1 Nov 2021 23:07:48 +1300 Subject: [PATCH 09/62] WIP update test to C# + NUnit --- tests/SDKTest.php | 3 +- tests/languages/dotnet/Tests.cs | 94 +++++++++++++++++++++++++++++ tests/languages/dotnet/Tests.csproj | 16 +++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/languages/dotnet/Tests.cs create mode 100644 tests/languages/dotnet/Tests.csproj diff --git a/tests/SDKTest.php b/tests/SDKTest.php index a3b978354..4c93220e1 100644 --- a/tests/SDKTest.php +++ b/tests/SDKTest.php @@ -125,7 +125,8 @@ class SDKTest extends TestCase 'class' => 'Appwrite\SDK\Language\DotNet', 'build' => [ 'mkdir -p tests/sdks/dotnet/src/test', - 'cp tests/languages/dotnet/tests.ps1 tests/sdks/dotnet/src/test/tests.ps1', + 'cp tests/languages/dotnet/Tests.cs tests/sdks/dotnet/src/test/Tests.cs', + 'cp tests/languages/dotnet/Tests.csproj tests/sdks/dotnet/src/test/Tests.csproj', 'cp -R tests/sdks/dotnet/io/appwrite/src/* tests/sdks/dotnet/src', 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src mcr.microsoft.com/dotnet/sdk:5.0-alpine dotnet publish -c Release -o test -f netstandard2.0', ], diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs new file mode 100644 index 000000000..7ea664dd2 --- /dev/null +++ b/tests/languages/dotnet/Tests.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using Appwrite; +using NUnit.Framework; + +namespace AppwriteTests +{ + public class Tests + { + [SetUp] + public void Setup() + { + Console.WriteLine("Test Started"); + } + + [Test] + public void Test1() + { + var client = new Client(); + var foo = new Foo(); + var bar = new Bar(); + var general = new General(); + + Mock mock; + // Foo Tests + mock = await foo.Get("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await foo.Post("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await foo.Put("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await foo.Patch("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await foo.Delete("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + // Bar Tests + mock = await bar.Get("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await bar.Post("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await bar.Put("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await bar.Patch("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + mock = await bar.Delete("string", 123, new() { "string in array" }); + Console.WriteLine(mock.result); + + // General Tests + var result = general.redirect() + Console.WriteLine((result as Dictionary)["result"]); + + mock = general.upload("string", 123, new() { "string in array" }, new FileInfo("../../../resources/file.png")); + Console.WriteLine(mock.result); + + try + { + general.Error400(); + } + catch (AppwriteException e) + { + Console.WriteLine(e.Message); + } + + try + { + general.Error500(); + } + catch (AppwriteException e) + { + Console.WriteLine(e.Message); + } + + try + { + general.Error502(); + } + catch (AppwriteException e) + { + Console.WriteLine(e.Message); + } + } + } +} \ No newline at end of file diff --git a/tests/languages/dotnet/Tests.csproj b/tests/languages/dotnet/Tests.csproj new file mode 100644 index 000000000..4e0ecdf03 --- /dev/null +++ b/tests/languages/dotnet/Tests.csproj @@ -0,0 +1,16 @@ + + + + net5.0 + + false + + + + + + + + + + From 230a6d51c33457a2d0e776e6982d81e44de81f06 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Nov 2021 23:31:32 +1300 Subject: [PATCH 10/62] Test fixes --- tests/SDKTest.php | 4 +-- tests/languages/dotnet/Tests.cs | 52 +++++++++++++++-------------- tests/languages/dotnet/Tests.csproj | 5 +-- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/tests/SDKTest.php b/tests/SDKTest.php index 4c93220e1..f00bd10d5 100644 --- a/tests/SDKTest.php +++ b/tests/SDKTest.php @@ -131,8 +131,8 @@ class SDKTest extends TestCase 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src mcr.microsoft.com/dotnet/sdk:5.0-alpine dotnet publish -c Release -o test -f netstandard2.0', ], 'envs' => [ - 'dotnet-5.0' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:5.0-alpine pwsh tests.ps1', - 'dotnet-3.1' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:3.1-alpine pwsh tests.ps1', + 'dotnet-5.0' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:5.0-alpine dotnet test -v n', + 'dotnet-3.1' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:3.1-alpine dotnet test -v n', ], 'supportException' => true, ], diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index 7ea664dd2..293b474ff 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using Appwrite; +using Appwrite.Models; using NUnit.Framework; namespace AppwriteTests @@ -12,82 +14,82 @@ public class Tests [SetUp] public void Setup() { - Console.WriteLine("Test Started"); + TestContext.WriteLine("Test Started"); } [Test] - public void Test1() + public async Task Test1() { var client = new Client(); - var foo = new Foo(); - var bar = new Bar(); - var general = new General(); + var foo = new Foo(client); + var bar = new Bar(client); + var general = new General(client); Mock mock; // Foo Tests mock = await foo.Get("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await foo.Post("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await foo.Put("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await foo.Patch("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await foo.Delete("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); // Bar Tests mock = await bar.Get("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await bar.Post("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await bar.Put("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await bar.Patch("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); mock = await bar.Delete("string", 123, new() { "string in array" }); - Console.WriteLine(mock.result); + TestContext.WriteLine(mock.Result); // General Tests - var result = general.redirect() - Console.WriteLine((result as Dictionary)["result"]); + var result = await general.Redirect(); + TestContext.WriteLine((result as Dictionary)["result"]); - mock = general.upload("string", 123, new() { "string in array" }, new FileInfo("../../../resources/file.png")); - Console.WriteLine(mock.result); + mock = await general.Upload("string", 123, new() { "string in array" }, new FileInfo("../../../../../../../resources/file.png")); + TestContext.WriteLine(mock.Result); try { - general.Error400(); + await general.Error400(); } catch (AppwriteException e) { - Console.WriteLine(e.Message); + TestContext.WriteLine(e.Message); } try { - general.Error500(); + await general.Error500(); } catch (AppwriteException e) { - Console.WriteLine(e.Message); + TestContext.WriteLine(e.Message); } try { - general.Error502(); + await general.Error502(); } catch (AppwriteException e) { - Console.WriteLine(e.Message); + TestContext.WriteLine(e.Message); } } } diff --git a/tests/languages/dotnet/Tests.csproj b/tests/languages/dotnet/Tests.csproj index 4e0ecdf03..67f7b6b8b 100644 --- a/tests/languages/dotnet/Tests.csproj +++ b/tests/languages/dotnet/Tests.csproj @@ -1,8 +1,7 @@ - net5.0 - + net5.0 false @@ -11,6 +10,8 @@ + + From 852a024cd731cb30d0a74c404088bac3f8c6d4b3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Nov 2021 23:32:19 +1300 Subject: [PATCH 11/62] Replace default params with null filtering --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 4 ++-- .../src/Appwrite/Services/ServiceTemplate.cs.twig | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 42f743a4f..f441ed4c5 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -34,11 +34,11 @@ namespace {{ spec.title | caseUcfirst }}.Models public static {{ definition.name | caseUcfirst }} From(Dictionary map) => new( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}(map["{{ property.name }}"] as IEnumerable>).Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst}}.From(map: map["{{property.name }}"] as Dictionary){% endif %}{% else %}{% if property.type == 'integer' or property.type == 'number' or property.type == 'boolean' %}({{ property.type | typeName }})map["{{ property.name }}"]{% else %}map["{{ property.name }}"] as {{ property.type | typeName }}{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((IEnumerable>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst}}.From(map: (Dictionary)map["{{property.name }}"]){% endif %}{% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} - data: map["data"] as Dictionary + data: (Dictionary)map["data"] {% endif %} ); diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index a83397aef..04506f694 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -1,6 +1,6 @@ {% macro parameter(parameter) %} {% if parameter.name == 'orderType' %}{{ 'OrderType orderType = OrderType.ASC' }}{% else %} -{{ parameter.type | typeName }}{% if not parameter.required and (parameter.type == 'boolean' or parameter.type == 'integer' or parameter.type == 'number') %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{{ parameter | paramDefault }}{% endif %} +{{ parameter.type | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} {% endmacro %} {% macro method_parameters(parameters) %} {% if parameters.all|length > 0 %}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% endif %} @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -48,7 +49,7 @@ namespace {{ spec.title | caseUcfirst }} {% endfor %} - var parameters = new Dictionary() + var parameters = new Dictionary() { {% for parameter in method.parameters.query | merge(method.parameters.body) %} { "{{ parameter.name }}", {{ _self.map_parameter(parameter) }} }{% if not loop.last or _self.methodNeedsSecurityParameters(method) %},{% endif %} @@ -74,7 +75,7 @@ namespace {{ spec.title | caseUcfirst }} method: "{{ method.method | caseUpper }}", path: path, headers: headers, - parameters: parameters, + parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, responseType: typeof({{ _self.resultType(spec.title, method) }})); {% else %} @@ -94,9 +95,9 @@ namespace {{ spec.title | caseUcfirst }} path: path, headers: headers, {% if not method.responseModel %} - parameters: parameters); + parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); {% else %} - parameters: parameters, + parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, convert: convert, responseType: typeof({{ _self.resultType(spec.title, method) }})); {% endif %} From 4b1037ef55b5f0ba4cca14a3297e674427e03e1d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 Nov 2021 23:32:39 +1300 Subject: [PATCH 12/62] Warnings fixes --- .../dotnet/src/Appwrite/Appwrite.csproj.twig | 1 + templates/dotnet/src/Appwrite/Client.cs.twig | 42 +++++++++++++------ .../src/Appwrite/Helpers/ExtensionMethods.cs | 8 ++-- .../src/Appwrite/Models/Exception.cs.twig | 4 +- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index 18fff9c38..0d40ba5a3 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -14,6 +14,7 @@ git {{sdk.gitURL}} true + latest enable diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 15a73c697..fa7ec373b 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -96,7 +96,7 @@ namespace {{ spec.title | caseUcfirst }} Dictionary headers, Dictionary parameters, Func, T>? convert = null, - Type? responseType = null) + Type? responseType = null) where T : class { if (selfSigned) { @@ -127,12 +127,12 @@ namespace {{ spec.title | caseUcfirst }} var list = new List(enumerable); for (int index = 0; index < list.Count; index++) { - form.Add(new StringContent(list[index].ToString()), $"{parameter.Key}[{index}]"); + form.Add(new StringContent(list[index].ToString()!), $"{parameter.Key}[{index}]"); } } else { - form.Add(new StringContent(parameter.Value.ToString()), parameter.Key); + form.Add(new StringContent(parameter.Value.ToString()!), parameter.Key); } } request.Content = form; @@ -178,27 +178,43 @@ namespace {{ spec.title | caseUcfirst }} { var httpResponseMessage = await http.SendAsync(request); var code = (int) httpResponseMessage.StatusCode; - var response = await httpResponseMessage.Content.ReadAsStringAsync(); + var contentType = httpResponseMessage.Content.Headers + .GetValues("Content-Type") + .FirstOrDefault() ?? string.Empty; + + var isJson = contentType.Contains("application/json"); + var isBytes = contentType.Contains("application/octet-stream"); if (code >= 400) { - var message = response.ToString(); - var isJson = httpResponseMessage.Content.Headers.GetValues("Content-Type").FirstOrDefault().Contains("application/json"); + var message = await httpResponseMessage.Content.ReadAsStringAsync(); if (isJson) { - message = (JObject.Parse(message))["message"].ToString(); + message = JObject.Parse(message)["message"]!.ToString(); } - throw new {{spec.title | caseUcfirst}}Exception(message, code, response.ToString()); + throw new {{spec.title | caseUcfirst}}Exception(message, code); } - var dict = JsonConvert.DeserializeObject>(response.ToString()); + if (isJson) + { + var responseString = await httpResponseMessage.Content.ReadAsStringAsync(); + var dict = JsonConvert.DeserializeObject>(responseString); + + if (convert != null) + { + return convert(dict!); + } - if (convert != null) + return (dict as T)!; + } + else if (isBytes) { - return convert(dict); + return ((await httpResponseMessage.Content.ReadAsByteArrayAsync()) as T)!; + } + else + { + return default!; } - - return default; } catch (System.Exception e) { diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs index ab11f8d01..55655b8ca 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs @@ -31,12 +31,12 @@ public static string ToQueryString(this Dictionary parameters) { foreach(object entry in (dynamic) value) { - query.Add(key + "[]=" + Uri.EscapeUriString(entry.ToString())); + query.Add($"{key}[]={Uri.EscapeUriString(entry.ToString()!)}"); } - } - else + } + else { - query.Add(key + "=" + Uri.EscapeUriString(value.ToString())); + query.Add($"{key}={Uri.EscapeUriString(value.ToString()!)}"); } } } diff --git a/templates/dotnet/src/Appwrite/Models/Exception.cs.twig b/templates/dotnet/src/Appwrite/Models/Exception.cs.twig index 2fca3e544..86567f0c9 100644 --- a/templates/dotnet/src/Appwrite/Models/Exception.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Exception.cs.twig @@ -8,9 +8,9 @@ namespace Appwrite public string? Response { get; set; } = null; public {{spec.title | caseUcfirst}}Exception( - string message = null, + string? message = null, int? code = null, - string response = null) : base(message) + string? response = null) : base(message) { this.Code = code; this.Response = response; From 262e62552bebbb0507944540b83f89c1599a5eb0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 4 Nov 2021 22:36:47 +1300 Subject: [PATCH 13/62] Fix output directory --- src/SDK/Language/DotNet.php | 16 ++++++++-------- tests/SDKTest.php | 2 -- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 02f603ee7..1fe23f4ed 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -320,49 +320,49 @@ public function getFiles() ], [ 'scope' => 'default', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Client.cs', + 'destination' => '/src/Appwrite/Client.cs', 'template' => 'dotnet/src/Appwrite/Client.cs.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Helpers/ExtensionMethods.cs', + 'destination' => '/src/Appwrite/Helpers/ExtensionMethods.cs', 'template' => 'dotnet/src/Appwrite/Helpers/ExtensionMethods.cs', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Models/OrderType.cs', + 'destination' => '/src/Appwrite/Models/OrderType.cs', 'template' => 'dotnet/src/Appwrite/Models/OrderType.cs.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Models/Rule.cs', + 'destination' => '/src/Appwrite/Models/Rule.cs', 'template' => 'dotnet/src/Appwrite/Models/Rule.cs.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Models/Exception.cs', + 'destination' => '/src/Appwrite/Models/Exception.cs', 'template' => 'dotnet/src/Appwrite/Models/Exception.cs.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Services/Service.cs', + 'destination' => '/src/Appwrite/Services/Service.cs', 'template' => 'dotnet/src/Appwrite/Services/Service.cs.twig', 'minify' => false, ], [ 'scope' => 'service', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Services/{{service.name | caseUcfirst}}.cs', + 'destination' => '/src/Appwrite/Services/{{service.name | caseUcfirst}}.cs', 'template' => 'dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig', 'minify' => false, ], [ 'scope' => 'definition', - 'destination' => '/{{ sdk.namespace | caseSlash }}/src/Appwrite/Models/{{definition.name | caseUcfirst}}.cs', + 'destination' => '/src/Appwrite/Models/{{definition.name | caseUcfirst}}.cs', 'template' => 'dotnet/src/Appwrite/Models/Model.cs.twig', 'minify' => false, ] diff --git a/tests/SDKTest.php b/tests/SDKTest.php index 6a60273c2..0b84406ac 100644 --- a/tests/SDKTest.php +++ b/tests/SDKTest.php @@ -127,8 +127,6 @@ class SDKTest extends TestCase 'mkdir -p tests/sdks/dotnet/src/test', 'cp tests/languages/dotnet/Tests.cs tests/sdks/dotnet/src/test/Tests.cs', 'cp tests/languages/dotnet/Tests.csproj tests/sdks/dotnet/src/test/Tests.csproj', - 'cp -R tests/sdks/dotnet/io/appwrite/src/* tests/sdks/dotnet/src', - 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src mcr.microsoft.com/dotnet/sdk:5.0-alpine dotnet publish -c Release -o test -f netstandard2.0', ], 'envs' => [ 'dotnet-5.0' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:5.0-alpine dotnet test -v n', From 77683cbc1e2d16f6d7da0b1c1582156f3781773f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 4 Nov 2021 22:39:42 +1300 Subject: [PATCH 14/62] Support .NET Framework 4.8, .NET Core App 3.1, 5.0, .NET Standard 1.3, 2.0, 2.1 --- .../dotnet/src/Appwrite/Appwrite.csproj.twig | 2 +- templates/dotnet/src/Appwrite/Client.cs.twig | 36 ++++++++++--------- .../src/Appwrite/Helpers/ExtensionMethods.cs | 12 +++---- .../dotnet/src/Appwrite/Models/Model.cs.twig | 4 +-- tests/languages/dotnet/Tests.cs | 22 ++++++------ tests/languages/dotnet/Tests.csproj | 3 +- 6 files changed, 42 insertions(+), 37 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index 0d40ba5a3..7bda00fbc 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -1,6 +1,6 @@ - net5.0 + net48;net5.0;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1 {{spec.title}} {{sdk.version}} {{spec.contactName}} diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index fa7ec373b..45ed09950 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -14,20 +14,20 @@ namespace {{ spec.title | caseUcfirst }} { public class Client { - private readonly HttpClient http; + private HttpClient http; private readonly Dictionary headers; private readonly Dictionary config; private string endPoint; private bool selfSigned; - - public Client() : this("https://appwrite.io/v1", false, new HttpClient()) - { - } - public Client(string endPoint, bool selfSigned, HttpClient http) + public Client( + string endPoint = "https://appwrite.io/v1", + bool selfSigned = false, + HttpClient? http = null) { this.endPoint = endPoint; this.selfSigned = selfSigned; + this.http = http ?? new HttpClient(); this.headers = new Dictionary() { { "content-type", "application/json" }, @@ -39,12 +39,21 @@ namespace {{ spec.title | caseUcfirst }} }; this.config = new Dictionary(); - this.http = http; } public Client SetSelfSigned(bool selfSigned) { this.selfSigned = selfSigned; + + var handler = new HttpClientHandler() + { + ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => true + }; + + http = selfSigned + ? new HttpClient(handler) + : new HttpClient(); + return this; } @@ -98,18 +107,13 @@ namespace {{ spec.title | caseUcfirst }} Func, T>? convert = null, Type? responseType = null) where T : class { - if (selfSigned) - { - ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; - } - - var methodGet = "GET".Equals(method, StringComparison.InvariantCultureIgnoreCase); + var methodGet = "GET".Equals(method, StringComparison.OrdinalIgnoreCase); var queryString = methodGet ? "?" + parameters.ToQueryString() : string.Empty; var request = new HttpRequestMessage(new HttpMethod(method), endPoint + path + queryString); - if ("multipart/form-data".Equals(headers["content-type"], StringComparison.InvariantCultureIgnoreCase)) + if ("multipart/form-data".Equals(headers["content-type"], StringComparison.OrdinalIgnoreCase)) { var form = new MultipartFormDataContent(); @@ -147,7 +151,7 @@ namespace {{ spec.title | caseUcfirst }} foreach (var header in this.headers) { - if (header.Key.Equals("content-type", StringComparison.InvariantCultureIgnoreCase)) + if (header.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase)) { http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Value)); } @@ -162,7 +166,7 @@ namespace {{ spec.title | caseUcfirst }} foreach (var header in headers) { - if (header.Key.Equals("content-type", StringComparison.InvariantCultureIgnoreCase)) + if (header.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase)) { request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Value)); } diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs index 55655b8ca..0bfb9a88f 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs @@ -23,20 +23,20 @@ public static string ToQueryString(this Dictionary parameters) { var query = new List(); - foreach (var (key, value) in parameters) + foreach (var kvp in parameters) { - if (value != null) + if (kvp.Value != null) { - if (value is List) + if (kvp.Value is List list) { - foreach(object entry in (dynamic) value) + foreach(object entry in list) { - query.Add($"{key}[]={Uri.EscapeUriString(entry.ToString()!)}"); + query.Add($"{kvp.Key}[]={Uri.EscapeUriString(entry.ToString()!)}"); } } else { - query.Add($"{key}={Uri.EscapeUriString(value.ToString()!)}"); + query.Add($"{kvp.Key}={Uri.EscapeUriString(kvp.Value.ToString()!)}"); } } } diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index f441ed4c5..946acebc1 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -32,7 +32,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} } - public static {{ definition.name | caseUcfirst }} From(Dictionary map) => new( + public static {{ definition.name | caseUcfirst }} From(Dictionary map) => new {{ definition.name | caseUcfirst }}( {% for property in definition.properties %} {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((IEnumerable>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst}}.From(map: (Dictionary)map["{{property.name }}"]){% endif %}{% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} @@ -42,7 +42,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} ); - public Dictionary ToMap() => new() + public Dictionary ToMap() => new Dictionary() { {% for property in definition.properties %} { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ property.name | caseUcfirst | escapeKeyword | removeDollarSign }}.Select(it => it.ToMap()){% else %}{{property.name | caseUcfirst | escapeKeyword | removeDollarSign }}.ToMap(){% endif %}{% else %}{{property.name | caseUcfirst | escapeKeyword }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index 293b474ff..de4126c3b 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -27,42 +27,42 @@ public async Task Test1() Mock mock; // Foo Tests - mock = await foo.Get("string", 123, new() { "string in array" }); + mock = await foo.Get("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Post("string", 123, new() { "string in array" }); + mock = await foo.Post("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Put("string", 123, new() { "string in array" }); + mock = await foo.Put("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Patch("string", 123, new() { "string in array" }); + mock = await foo.Patch("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Delete("string", 123, new() { "string in array" }); + mock = await foo.Delete("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); // Bar Tests - mock = await bar.Get("string", 123, new() { "string in array" }); + mock = await bar.Get("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Post("string", 123, new() { "string in array" }); + mock = await bar.Post("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Put("string", 123, new() { "string in array" }); + mock = await bar.Put("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Patch("string", 123, new() { "string in array" }); + mock = await bar.Patch("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Delete("string", 123, new() { "string in array" }); + mock = await bar.Delete("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); // General Tests var result = await general.Redirect(); TestContext.WriteLine((result as Dictionary)["result"]); - mock = await general.Upload("string", 123, new() { "string in array" }, new FileInfo("../../../../../../../resources/file.png")); + mock = await general.Upload("string", 123, new List() { "string in array" }, new FileInfo("../../../../../../../resources/file.png")); TestContext.WriteLine(mock.Result); try diff --git a/tests/languages/dotnet/Tests.csproj b/tests/languages/dotnet/Tests.csproj index 67f7b6b8b..0db7d13dc 100644 --- a/tests/languages/dotnet/Tests.csproj +++ b/tests/languages/dotnet/Tests.csproj @@ -1,7 +1,8 @@ - net5.0 + net48;net5.0;netcoreapp1.0;netcoreapp2.1;netcoreapp3.1 + latest false From 0679c55b2a817f9f04cd2d5bf0257470974ad62d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 4 Nov 2021 22:40:05 +1300 Subject: [PATCH 15/62] Fix rule nullability warnings --- .../dotnet/src/Appwrite/Models/Rule.cs.twig | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Rule.cs.twig b/templates/dotnet/src/Appwrite/Models/Rule.cs.twig index 71234033c..3c0427d7d 100644 --- a/templates/dotnet/src/Appwrite/Models/Rule.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Rule.cs.twig @@ -9,8 +9,24 @@ namespace {{ spec.title | caseUcfirst }} public string Label { get; set; } public string Key { get; set; } public string Type { get; set; } - public string Default { get; set; } + public string? Default { get; set; } public bool Required { get; set; } public bool Array { get; set; } + + public Rule( + string label, + string key, + string type, + string? @default = null, + bool required = false, + bool array = false) + { + Label = label; + Key = key; + Type = type; + Default = @default; + Required = required; + Array = array; + } } } From 24faaf670d3073dd502ef73934e4699caf7b33ed Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 4 Nov 2021 22:40:53 +1300 Subject: [PATCH 16/62] Run tests targeting current SDK --- tests/SDKTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/SDKTest.php b/tests/SDKTest.php index 0b84406ac..cc1bef0ac 100644 --- a/tests/SDKTest.php +++ b/tests/SDKTest.php @@ -129,8 +129,8 @@ class SDKTest extends TestCase 'cp tests/languages/dotnet/Tests.csproj tests/sdks/dotnet/src/test/Tests.csproj', ], 'envs' => [ - 'dotnet-5.0' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:5.0-alpine dotnet test -v n', - 'dotnet-3.1' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:3.1-alpine dotnet test -v n', + 'dotnet-3.1' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:3.1-alpine dotnet test -v n -f netcoreapp3.1', + 'dotnet-5.0' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:5.0-alpine dotnet test -v n -f net5.0', ], 'supportException' => true, ], From 23ed00f482ddfc0c177fa299fa990ad1569f25c9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Nov 2021 22:58:52 +1300 Subject: [PATCH 17/62] Disable metadata property handling so `$` prefixed vars are deserialised --- templates/dotnet/src/Appwrite/Client.cs.twig | 80 +++++++++---------- .../dotnet/src/Appwrite/Models/Model.cs.twig | 4 + 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 45ed09950..b1a19495e 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -14,21 +14,26 @@ namespace {{ spec.title | caseUcfirst }} { public class Client { - private HttpClient http; - private readonly Dictionary headers; - private readonly Dictionary config; - private string endPoint; - private bool selfSigned; + public string EndPoint => _endPoint; + public Dictionary Config => _config; + + private HttpClient _http; + private readonly Dictionary _headers; + private readonly Dictionary _config; + private string _endPoint; + + private JsonSerializerSettings _serializerSettings = new JsonSerializerSettings { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + }; public Client( string endPoint = "https://appwrite.io/v1", bool selfSigned = false, HttpClient? http = null) { - this.endPoint = endPoint; - this.selfSigned = selfSigned; - this.http = http ?? new HttpClient(); - this.headers = new Dictionary() + _endPoint = endPoint; + _http = http ?? new HttpClient(); + _headers = new Dictionary() { { "content-type", "application/json" }, { "x-sdk-version", "{{spec.title | caseDash}}:{{ language.name | caseLower }}:{{sdk.version}}" }{% if spec.global.defaultHeaders | length > 0 %},{% endif %} @@ -38,19 +43,19 @@ namespace {{ spec.title | caseUcfirst }} {% endfor %} }; - this.config = new Dictionary(); + _config = new Dictionary(); + + } public Client SetSelfSigned(bool selfSigned) { - this.selfSigned = selfSigned; - var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => true }; - http = selfSigned + _http = selfSigned ? new HttpClient(handler) : new HttpClient(); @@ -59,18 +64,9 @@ namespace {{ spec.title | caseUcfirst }} public Client SetEndPoint(string endPoint) { - this.endPoint = endPoint; - return this; - } - - public string GetEndPoint() - { - return endPoint; - } + _endPoint = endPoint; - public Dictionary GetConfig() - { - return config; + return this; } {% for header in spec.global.headers %} @@ -78,15 +74,17 @@ namespace {{ spec.title | caseUcfirst }} /// {{header.description}} {% endif %} public Client Set{{header.key | caseUcfirst}}(string value) { - config.Add("{{ header.key | caseCamel }}", value); + _config.Add("{{ header.key | caseCamel }}", value); AddHeader("{{header.name}}", value); + return this; } {% endfor %} public Client AddHeader(String key, String value) { - headers.Add(key, value); + _headers.Add(key, value); + return this; } @@ -111,7 +109,7 @@ namespace {{ spec.title | caseUcfirst }} var queryString = methodGet ? "?" + parameters.ToQueryString() : string.Empty; - var request = new HttpRequestMessage(new HttpMethod(method), endPoint + path + queryString); + var request = new HttpRequestMessage(new HttpMethod(method), _endPoint + path + queryString); if ("multipart/form-data".Equals(headers["content-type"], StringComparison.OrdinalIgnoreCase)) { @@ -149,18 +147,18 @@ namespace {{ spec.title | caseUcfirst }} request.Content = new StringContent(body, Encoding.UTF8, "application/json"); } - foreach (var header in this.headers) + foreach (var header in _headers) { if (header.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase)) { - http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Value)); + _http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Value)); } else { - if (http.DefaultRequestHeaders.Contains(header.Key)) { - http.DefaultRequestHeaders.Remove(header.Key); + if (_http.DefaultRequestHeaders.Contains(header.Key)) { + _http.DefaultRequestHeaders.Remove(header.Key); } - http.DefaultRequestHeaders.Add(header.Key, header.Value); + _http.DefaultRequestHeaders.Add(header.Key, header.Value); } } @@ -180,9 +178,9 @@ namespace {{ spec.title | caseUcfirst }} } try { - var httpResponseMessage = await http.SendAsync(request); - var code = (int) httpResponseMessage.StatusCode; - var contentType = httpResponseMessage.Content.Headers + var response = await _http.SendAsync(request); + var code = (int)response.StatusCode; + var contentType = response.Content.Headers .GetValues("Content-Type") .FirstOrDefault() ?? string.Empty; @@ -190,7 +188,7 @@ namespace {{ spec.title | caseUcfirst }} var isBytes = contentType.Contains("application/octet-stream"); if (code >= 400) { - var message = await httpResponseMessage.Content.ReadAsStringAsync(); + var message = await response.Content.ReadAsStringAsync(); if (isJson) { message = JObject.Parse(message)["message"]!.ToString(); @@ -201,8 +199,11 @@ namespace {{ spec.title | caseUcfirst }} if (isJson) { - var responseString = await httpResponseMessage.Content.ReadAsStringAsync(); - var dict = JsonConvert.DeserializeObject>(responseString); + var responseString = await response.Content.ReadAsStringAsync(); + + var dict = JsonConvert.DeserializeObject>( + responseString, + _serializerSettings); if (convert != null) { @@ -213,7 +214,7 @@ namespace {{ spec.title | caseUcfirst }} } else if (isBytes) { - return ((await httpResponseMessage.Content.ReadAsByteArrayAsync()) as T)!; + return ((await response.Content.ReadAsByteArrayAsync()) as T)!; } else { @@ -224,7 +225,6 @@ namespace {{ spec.title | caseUcfirst }} { throw new {{spec.title | caseUcfirst}}Exception(e.Message, e); } - } } } diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 946acebc1..d8c7f371c 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -4,12 +4,16 @@ using System; using System.Linq; using System.Collections.Generic; +using Newtonsoft.Json; + namespace {{ spec.title | caseUcfirst }}.Models { public class {{ definition.name | caseUcfirst }} { {% for property in definition.properties %} + [JsonProperty("{{ property.name }}")] public {{ _self.sub_schema(property) }} {{ property.name | caseUcfirst | escapeKeyword }} { get; set; } + {% endfor %} {% if definition.additionalProperties %} public Dictionary Data { get; set; } From 1813ad0ea8bfea466f61609e3d78dcb655afcf5a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Nov 2021 23:03:17 +1300 Subject: [PATCH 18/62] Add Int32 converter to stop exceptions from default deserialiser Int64 narrowing --- src/SDK/Language/DotNet.php | 6 +++ templates/dotnet/src/Appwrite/Client.cs.twig | 3 ++ .../Appwrite/Helpers/Int32JsonConverter.cs | 51 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 1fe23f4ed..d07dbb692 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -330,6 +330,12 @@ public function getFiles() 'template' => 'dotnet/src/Appwrite/Helpers/ExtensionMethods.cs', 'minify' => false, ], + [ + 'scope' => 'default', + 'destination' => '/src/Appwrite/Helpers/Int32JsonConverter.cs', + 'template' => 'dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs', + 'minify' => false, + ], [ 'scope' => 'default', 'destination' => '/src/Appwrite/Models/OrderType.cs', diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index b1a19495e..156bcb899 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -24,6 +24,9 @@ namespace {{ spec.title | caseUcfirst }} private JsonSerializerSettings _serializerSettings = new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + Converters = new List { + new Int32JsonConverter() + } }; public Client( diff --git a/templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs b/templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs new file mode 100644 index 000000000..df9a1fe8a --- /dev/null +++ b/templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace {{ spec.title | caseUcfirst }} +{ + public class Int32JsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) => + objectType == typeof(Dictionary); + + public override bool CanWrite => false; + + public override object ReadJson( + JsonReader reader, + Type objectType, + object? existingValue, + JsonSerializer serializer) + { + var result = new Dictionary(); + reader.Read(); + + while (reader.TokenType == JsonToken.PropertyName) + { + string? propertyName = reader.Value as string; + reader.Read(); + + object? value; + if (reader.TokenType == JsonToken.Integer) + { + // Convert to Int32 instead of Int64 + value = Convert.ToInt32(reader.Value); + } + else + { + value = serializer.Deserialize(reader); + } + result.Add(propertyName!, value!); + reader.Read(); + } + + return result; + } + + public override void WriteJson( + JsonWriter writer, + object? value, + JsonSerializer serializer) => + throw new NotImplementedException(); + } +} \ No newline at end of file From abdd5bb292577250f4e825003c76f29eb9f43618 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Nov 2021 23:03:32 +1300 Subject: [PATCH 19/62] Fix config reference --- templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 04506f694..c98dbdb82 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -58,7 +58,7 @@ namespace {{ spec.title | caseUcfirst }} {% if _self.methodNeedsSecurityParameters(method) %} {% for node in method.security %} {% for key,header in node|keys %} - { "{{header|caseLower}}", _client.GetConfig()["{{header|caseLower}}"] }{% if not loop.last or (loop.last and method.headers | length > 0) %},{% endif %} + { "{{header|caseLower}}", _client.Config["{{header|caseLower}}"] }{% if not loop.last or (loop.last and method.headers | length > 0) %},{% endif %} {% endfor %} {% endfor %} From 8711826522808c0feb7303f8cfa55eb7cdd3e6cc Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Nov 2021 23:04:41 +1300 Subject: [PATCH 20/62] Support .NET Framework 4.7,4.7.1,4.7.2 --- templates/dotnet/src/Appwrite/Appwrite.csproj.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index 7bda00fbc..ee79c8987 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -1,6 +1,6 @@ - net48;net5.0;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1 + net47;net48;net5.0;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1 {{spec.title}} {{sdk.version}} {{spec.contactName}} From 8eebfe9588664eba69f09372948c49b1d8be3b47 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 8 Nov 2021 23:04:56 +1300 Subject: [PATCH 21/62] Remove redundant dependency --- templates/dotnet/src/Appwrite/Appwrite.csproj.twig | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index ee79c8987..8353cb7ca 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -19,7 +19,6 @@ - From 3bf326213075b7a52c7eb1eb285bc1c897dc1eda Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 12 Nov 2021 21:07:20 +1300 Subject: [PATCH 22/62] Fix overlapping rule models --- src/SDK/Language/DotNet.php | 6 ---- .../dotnet/src/Appwrite/Models/Rule.cs.twig | 32 ------------------- 2 files changed, 38 deletions(-) delete mode 100644 templates/dotnet/src/Appwrite/Models/Rule.cs.twig diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index d07dbb692..7c0a6ee51 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -342,12 +342,6 @@ public function getFiles() 'template' => 'dotnet/src/Appwrite/Models/OrderType.cs.twig', 'minify' => false, ], - [ - 'scope' => 'default', - 'destination' => '/src/Appwrite/Models/Rule.cs', - 'template' => 'dotnet/src/Appwrite/Models/Rule.cs.twig', - 'minify' => false, - ], [ 'scope' => 'default', 'destination' => '/src/Appwrite/Models/Exception.cs', diff --git a/templates/dotnet/src/Appwrite/Models/Rule.cs.twig b/templates/dotnet/src/Appwrite/Models/Rule.cs.twig deleted file mode 100644 index 3c0427d7d..000000000 --- a/templates/dotnet/src/Appwrite/Models/Rule.cs.twig +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace {{ spec.title | caseUcfirst }} -{ - public class Rule - { - public string Label { get; set; } - public string Key { get; set; } - public string Type { get; set; } - public string? Default { get; set; } - public bool Required { get; set; } - public bool Array { get; set; } - - public Rule( - string label, - string key, - string type, - string? @default = null, - bool required = false, - bool array = false) - { - Label = label; - Key = key; - Type = type; - Default = @default; - Required = required; - Array = array; - } - } -} From c3f30f28d5b1ec3ec3c1959f350f75c85d36bb84 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 12 Nov 2021 21:07:42 +1300 Subject: [PATCH 23/62] Escape attributes matching type names --- templates/dotnet/src/Appwrite/Client.cs.twig | 2 +- .../src/Appwrite/Helpers/ExtensionMethods.cs | 21 ++++++++++--------- .../dotnet/src/Appwrite/Models/Model.cs.twig | 13 ++++++------ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 156bcb899..6e6c0c462 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -84,7 +84,7 @@ namespace {{ spec.title | caseUcfirst }} } {% endfor %} - public Client AddHeader(String key, String value) + public Client AddHeader(string key, string value) { _headers.Add(key, value); diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs index 0bfb9a88f..07960ef6c 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs @@ -25,20 +25,21 @@ public static string ToQueryString(this Dictionary parameters) foreach (var kvp in parameters) { - if (kvp.Value != null) + if (kvp.Value == null) { - if (kvp.Value is List list) - { - foreach(object entry in list) - { - query.Add($"{kvp.Key}[]={Uri.EscapeUriString(entry.ToString()!)}"); - } - } - else + continue; + } + if (kvp.Value is List list) + { + foreach(object entry in list) { - query.Add($"{kvp.Key}={Uri.EscapeUriString(kvp.Value.ToString()!)}"); + query.Add($"{kvp.Key}[]={Uri.EscapeUriString(entry.ToString()!)}"); } } + else + { + query.Add($"{kvp.Key}={Uri.EscapeUriString(kvp.Value.ToString()!)}"); + } } return string.Join("&", query); } diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index d8c7f371c..654b6b715 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -1,4 +1,5 @@ -{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% endmacro %} +{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %} +{% macro property_name(definition, property) %}{% if (definition.name | caseUcfirst) == (property.name | caseUcfirst) %}X{% endif %}{{ property.name | caseUcfirst | removeDollarSign | escapeKeyword}}{% endmacro %} using System; using System.Linq; @@ -12,7 +13,7 @@ namespace {{ spec.title | caseUcfirst }}.Models { {% for property in definition.properties %} [JsonProperty("{{ property.name }}")] - public {{ _self.sub_schema(property) }} {{ property.name | caseUcfirst | escapeKeyword }} { get; set; } + public {{ _self.sub_schema(property) }} {{ _self.property_name(definition, property) }} { get; set; } {% endfor %} {% if definition.additionalProperties %} @@ -29,7 +30,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} ) { {% for property in definition.properties %} - {{ property.name | caseUcfirst | escapeKeyword }} = {{ property.name | caseCamel | escapeKeyword }}; + {{ _self.property_name(definition, property) }} = {{ property.name | caseCamel | escapeKeyword }}; {% endfor %} {% if definition.additionalProperties %} Data = data; @@ -38,18 +39,18 @@ namespace {{ spec.title | caseUcfirst }}.Models public static {{ definition.name | caseUcfirst }} From(Dictionary map) => new {{ definition.name | caseUcfirst }}( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((IEnumerable>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst}}.From(map: (Dictionary)map["{{property.name }}"]){% endif %}{% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((IEnumerable>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst}}.From(map: ((JObject)map["{{property.name }}"]).ToObject>()!){% endif %}{% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} - data: (Dictionary)map["data"] + data: ((JObject)map["data"]).ToObject>() {% endif %} ); public Dictionary ToMap() => new Dictionary() { {% for property in definition.properties %} - { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ property.name | caseUcfirst | escapeKeyword | removeDollarSign }}.Select(it => it.ToMap()){% else %}{{property.name | caseUcfirst | escapeKeyword | removeDollarSign }}.ToMap(){% endif %}{% else %}{{property.name | caseUcfirst | escapeKeyword }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ _self.property_name(definition, property) }}.Select(it => it.ToMap()){% else %}{{ _self.property_name(definition, property) }}.ToMap(){% endif %}{% else %}{{ _self.property_name(definition, property) }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} From a9ddb24936df3bf1fc84740924ec19eb78ee93c4 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Nov 2021 19:49:14 +1300 Subject: [PATCH 24/62] Use identifier overrides for dotnet response models --- src/SDK/Language/DotNet.php | 2 +- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 8d877d1f5..f4aa54b2c 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -372,7 +372,7 @@ public function getFiles() ], [ 'scope' => 'definition', - 'destination' => '/src/Appwrite/Models/{{definition.name | caseUcfirst}}.cs', + 'destination' => '/src/Appwrite/Models/{{ definition.name | caseUcfirst | overrideIdentifier }}.cs', 'template' => 'dotnet/src/Appwrite/Models/Model.cs.twig', 'minify' => false, ] diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 654b6b715..fafce88f4 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -1,5 +1,5 @@ {% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %} -{% macro property_name(definition, property) %}{% if (definition.name | caseUcfirst) == (property.name | caseUcfirst) %}X{% endif %}{{ property.name | caseUcfirst | removeDollarSign | escapeKeyword}}{% endmacro %} +{% macro property_name(definition, property) %}{{ property.name | caseUcfirst | removeDollarSign | escapeKeyword}}{% endmacro %} using System; using System.Linq; @@ -9,7 +9,7 @@ using Newtonsoft.Json; namespace {{ spec.title | caseUcfirst }}.Models { - public class {{ definition.name | caseUcfirst }} + public class {{ definition.name | caseUcfirst | overrideIdentifier }} { {% for property in definition.properties %} [JsonProperty("{{ property.name }}")] From 7677c90e984bf98ec3c9596736faa83efdd29a04 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Nov 2021 22:10:58 +1300 Subject: [PATCH 25/62] Fix parsing empty data map --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index fafce88f4..6b5963713 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -43,7 +43,9 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endfor %} {% if definition.additionalProperties %} - data: ((JObject)map["data"]).ToObject>() + data: map.ContainsKey("data") + ? ((JObject)map["data"]).ToObject>()! + : new Dictionary() {% endif %} ); From 6879e965e9c17a044be322f66f3eb35c857aa474 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Nov 2021 22:11:12 +1300 Subject: [PATCH 26/62] Add missing using --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 6b5963713..96249bf70 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -1,4 +1,4 @@ -{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %} +{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst | overrideIdentifier}}>{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %} {% macro property_name(definition, property) %}{{ property.name | caseUcfirst | removeDollarSign | escapeKeyword}}{% endmacro %} using System; @@ -6,6 +6,7 @@ using System.Linq; using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace {{ spec.title | caseUcfirst }}.Models { From e5e9bb96bd2b5a245d249df66dcfade32ed0c148 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Nov 2021 22:49:02 +1300 Subject: [PATCH 27/62] Add missing identifier overrides --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 6 +++--- .../dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 96249bf70..81ac6d718 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -21,7 +21,7 @@ namespace {{ spec.title | caseUcfirst }}.Models public Dictionary Data { get; set; } {% endif %} - public {{ definition.name | caseUcfirst }}( + public {{ definition.name | caseUcfirst | overrideIdentifier }}( {% for property in definition.properties %} {{ _self.sub_schema(property) }} {{ property.name | caseCamel | escapeKeyword }}{% if not property.required %} = {{ property.default }}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} @@ -38,9 +38,9 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} } - public static {{ definition.name | caseUcfirst }} From(Dictionary map) => new {{ definition.name | caseUcfirst }}( + public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new {{ definition.name | caseUcfirst | overrideIdentifier }}( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((IEnumerable>)map["{{ property.name }}"]).Select(it => {{ property.sub_schema | caseUcfirst }}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst}}.From(map: ((JObject)map["{{property.name }}"]).ToObject>()!){% endif %}{% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>()!{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index c98dbdb82..51b870db5 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -13,7 +13,7 @@ {% if (method.type == "webAuth" or method.type == "location") and method.security|length > 0 %}{{ true }}{% else %}{{false}}{% endif %} {% endmacro %} {% macro resultType(namespace, method) %} -{% if method.type == "webAuth" %}bool{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}Models.{{method.responseModel | caseUcfirst}}{% endif %} +{% if method.type == "webAuth" %}bool{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}Models.{{method.responseModel | caseUcfirst | overrideIdentifier }}{% endif %} {% endmacro %} using System; From 5feb6f28f782b62d82693f287271a94102641b97 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Nov 2021 23:03:22 +1300 Subject: [PATCH 28/62] Parse non schema list type as JArray --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 81ac6d718..8c62cb05c 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -40,7 +40,7 @@ namespace {{ spec.title | caseUcfirst }}.Models public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new {{ definition.name | caseUcfirst | overrideIdentifier }}( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>()!{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>()!{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} From 869866a574c3768d8a844efb01863f3db4f14471 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Nov 2021 23:03:40 +1300 Subject: [PATCH 29/62] Remove obsolete target frameworks --- tests/languages/dotnet/Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/languages/dotnet/Tests.csproj b/tests/languages/dotnet/Tests.csproj index 0db7d13dc..2f1cea22e 100644 --- a/tests/languages/dotnet/Tests.csproj +++ b/tests/languages/dotnet/Tests.csproj @@ -1,7 +1,7 @@ - net48;net5.0;netcoreapp1.0;netcoreapp2.1;netcoreapp3.1 + net48;net5.0;netcoreapp3.1 latest false From 2eaa1340ac253556bd05148b49ec3aa6bf66dd8c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 24 Nov 2021 22:18:03 +1300 Subject: [PATCH 30/62] Fix sub-schema deserialization --- templates/dotnet/src/Appwrite/Client.cs.twig | 29 +++++++++++++++---- .../src/Appwrite/Helpers/ExtensionMethods.cs | 8 +---- .../dotnet/src/Appwrite/Models/Model.cs.twig | 6 ++-- .../Appwrite/Services/ServiceTemplate.cs.twig | 14 ++++----- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 6e6c0c462..41e73718b 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -1,5 +1,7 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.IO; @@ -22,10 +24,23 @@ namespace {{ spec.title | caseUcfirst }} private readonly Dictionary _config; private string _endPoint; - private JsonSerializerSettings _serializerSettings = new JsonSerializerSettings { + public static JsonSerializerSettings DeserializerSettings { get; set; } = new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new List { - new Int32JsonConverter() + new Int32JsonConverter(), + new StringEnumConverter() + } + }; + + public static JsonSerializerSettings SerializerSettings { get; set; } = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Converters = new List { + new Int32JsonConverter(), + new StringEnumConverter() } }; @@ -48,7 +63,12 @@ namespace {{ spec.title | caseUcfirst }} }; _config = new Dictionary(); + if (selfSigned) + { + SetSelfSigned(true); + } + JsonConvert.DefaultSettings = () => DeserializerSettings; } public Client SetSelfSigned(bool selfSigned) @@ -105,8 +125,7 @@ namespace {{ spec.title | caseUcfirst }} string path, Dictionary headers, Dictionary parameters, - Func, T>? convert = null, - Type? responseType = null) where T : class + Func, T>? convert = null) where T : class { var methodGet = "GET".Equals(method, StringComparison.OrdinalIgnoreCase); @@ -206,7 +225,7 @@ namespace {{ spec.title | caseUcfirst }} var dict = JsonConvert.DeserializeObject>( responseString, - _serializerSettings); + DeserializerSettings); if (convert != null) { diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs index 07960ef6c..8efde9f02 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs @@ -10,13 +10,7 @@ public static class ExtensionMethods { public static string ToJson(this Dictionary dict) { - var settings = new JsonSerializerSettings - { - ContractResolver = new CamelCasePropertyNamesContractResolver(), - Converters = new List { new StringEnumConverter() } - }; - - return JsonConvert.SerializeObject(dict, settings); + return JsonConvert.SerializeObject(dict, Client.SerializerSettings); } public static string ToQueryString(this Dictionary parameters) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 8c62cb05c..ce16b3f21 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -40,13 +40,11 @@ namespace {{ spec.title | caseUcfirst }}.Models public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new {{ definition.name | caseUcfirst | overrideIdentifier }}( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>()!{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} - data: map.ContainsKey("data") - ? ((JObject)map["data"]).ToObject>()! - : new Dictionary() + data: map {% endif %} ); diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 51b870db5..d2bec700d 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -75,19 +75,16 @@ namespace {{ spec.title | caseUcfirst }} method: "{{ method.method | caseUpper }}", path: path, headers: headers, - parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, - responseType: typeof({{ _self.resultType(spec.title, method) }})); + parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); {% else %} {% if method.responseModel %} - static {{ _self.resultType(spec.title, method) }} convert(Dictionary it) - { + static {{ _self.resultType(spec.title, method) }} Convert(Dictionary it) => {% if method.responseModel == 'any' %} - return it; + it; {% else %} - return {{ _self.resultType(spec.title, method) }}.From(map: it); + {{ _self.resultType(spec.title, method) }}.From(map: it); {% endif %} - } {% endif %} return _client.Call{% if method.type != 'webAuth' %}<{{ _self.resultType(spec.title, method) }}>{% endif %}( @@ -98,8 +95,7 @@ namespace {{ spec.title | caseUcfirst }} parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); {% else %} parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, - convert: convert, - responseType: typeof({{ _self.resultType(spec.title, method) }})); + convert: Convert); {% endif %} {% endif %} } From 40097b8c984abcf9ac9e3c4cbcc951c0832fe9ab Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 29 Nov 2021 19:49:44 +1300 Subject: [PATCH 31/62] Update dotnet doc template --- templates/dotnet/docs/example.md.twig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/templates/dotnet/docs/example.md.twig b/templates/dotnet/docs/example.md.twig index 83b862800..29afcc679 100644 --- a/templates/dotnet/docs/example.md.twig +++ b/templates/dotnet/docs/example.md.twig @@ -1,17 +1,17 @@ using {{ spec.title | caseUcfirst }}; +using {{ spec.title | caseUcfirst }}.Models; -var client = new Client(); - +Client client = new Client() {% if method.security|length > 0 %} -client - .SetEndPoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint + .SetEndPoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint {% for node in method.security %} {% for key,header in node|keys %} - .Set{{header | caseUcfirst}}("{{node[header]["x-appwrite"]["demo"]}}") // {{node[header].description}} -{% endfor %} -{% endfor %}; + .Set{{header | caseUcfirst}}("{{node[header]['x-appwrite']['demo']}}"){% if loop.last %};{% endif %} // {{node[header].description}} +{% endfor %}{% endfor %}{% endif %} -{% endif %} {{ service.name | caseUcfirst }} {{ service.name | caseCamel }} = new {{ service.name | caseUcfirst }}(client); -{% if method.type == 'location' %}byte[]{% else %}{{ method.responseModel }}{% endif %} result = await {{ service.name | caseCamel }}.{{ method.name | caseUcfirst }}({% for parameter in method.parameters.all %}{% if parameter.required %}{% if not loop.first %}, {% endif %}{{ parameter | paramExample }}{% endif %}{% endfor %}); +{% if method.type == 'location' %}byte[]{% else %}{{ method.responseModel | caseUcfirst | overrideIdentifier }}{% endif %} result = await {{ service.name | caseCamel }}.{{ method.name | caseUcfirst }}({% if method.parameters.all | length == 0 %});{% endif %} +{% for parameter in method.parameters.all %}{% if parameter.required %}{% if not loop.first %},{% endif %} + + {{ parameter.name }}: {{ parameter | paramExample }}{% endif %}{% endfor %}{% if method.parameters.all | length > 0 %});{% endif %} From 8ebed28957b3915067dd47be3e430535d8c666db Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 1 Dec 2021 20:25:22 +1300 Subject: [PATCH 32/62] Fix un-templated output paths --- src/SDK/Language/DotNet.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index f4aa54b2c..b103bfda0 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -313,7 +313,7 @@ public function getFiles() ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite.sln', + 'destination' => '/src/{{ spec.title | caseUcfirst }}.sln', 'template' => 'dotnet/src/Appwrite.sln', 'minify' => false, ], @@ -324,55 +324,55 @@ public function getFiles() ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite/Appwrite.csproj', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/{{ spec.title | caseUcfirst }}.csproj', 'template' => 'dotnet/src/Appwrite/Appwrite.csproj.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite/Client.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Client.cs', 'template' => 'dotnet/src/Appwrite/Client.cs.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite/Helpers/ExtensionMethods.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Helpers/ExtensionMethods.cs', 'template' => 'dotnet/src/Appwrite/Helpers/ExtensionMethods.cs', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite/Helpers/Int32JsonConverter.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Helpers/Int32JsonConverter.cs', 'template' => 'dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite/Models/OrderType.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/OrderType.cs', 'template' => 'dotnet/src/Appwrite/Models/OrderType.cs.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite/Models/Exception.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/Exception.cs', 'template' => 'dotnet/src/Appwrite/Models/Exception.cs.twig', 'minify' => false, ], [ 'scope' => 'default', - 'destination' => '/src/Appwrite/Services/Service.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Services/Service.cs', 'template' => 'dotnet/src/Appwrite/Services/Service.cs.twig', 'minify' => false, ], [ 'scope' => 'service', - 'destination' => '/src/Appwrite/Services/{{service.name | caseUcfirst}}.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Services/{{service.name | caseUcfirst}}.cs', 'template' => 'dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig', 'minify' => false, ], [ 'scope' => 'definition', - 'destination' => '/src/Appwrite/Models/{{ definition.name | caseUcfirst | overrideIdentifier }}.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/{{ definition.name | caseUcfirst | overrideIdentifier }}.cs', 'template' => 'dotnet/src/Appwrite/Models/Model.cs.twig', 'minify' => false, ] From 547a01b4667a9409a5e6a0f4d79445aa17e2d43a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 23 Mar 2022 23:59:59 +1300 Subject: [PATCH 33/62] Update tests for new structure --- src/SDK/Language/DotNet.php | 3 ++- templates/dotnet/src/Appwrite/Client.cs.twig | 12 +++++++--- tests/DotNet31Test.php | 24 ++++++++++++++++++++ tests/DotNet50Test.php | 24 ++++++++++++++++++++ tests/DotNet60Test.php | 24 ++++++++++++++++++++ tests/languages/dotnet/Tests.cs | 9 ++++++++ 6 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 tests/DotNet31Test.php create mode 100644 tests/DotNet50Test.php create mode 100644 tests/DotNet60Test.php diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index b103bfda0..825356c40 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -138,7 +138,8 @@ public function getKeywords() public function getIdentifierOverrides() { return [ - 'Jwt' => 'JWT' + 'Jwt' => 'JWT', + 'Domain' => 'XDomain', ]; } diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 41e73718b..8b0ab1a57 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -129,11 +129,17 @@ namespace {{ spec.title | caseUcfirst }} { var methodGet = "GET".Equals(method, StringComparison.OrdinalIgnoreCase); - var queryString = methodGet ? "?" + parameters.ToQueryString() : string.Empty; + var queryString = methodGet ? + "?" + parameters.ToQueryString() : + string.Empty; - var request = new HttpRequestMessage(new HttpMethod(method), _endPoint + path + queryString); + var request = new HttpRequestMessage( + new HttpMethod(method), + _endPoint + path + queryString); - if ("multipart/form-data".Equals(headers["content-type"], StringComparison.OrdinalIgnoreCase)) + if ("multipart/form-data".Equals( + headers["content-type"], + StringComparison.OrdinalIgnoreCase)) { var form = new MultipartFormDataContent(); diff --git a/tests/DotNet31Test.php b/tests/DotNet31Test.php new file mode 100644 index 000000000..ce90956fc --- /dev/null +++ b/tests/DotNet31Test.php @@ -0,0 +1,24 @@ +() { "string in array" }, new FileInfo("../../../../../../../resources/file.png")); TestContext.WriteLine(mock.Result); + mock = await general.Upload("string", 123, new List() { "string in array" }, new FileInfo("../../../../../../../resources/large_file.mp4")); + TestContext.WriteLine(mock.Result); + try { await general.Error400(); @@ -91,6 +94,12 @@ public async Task Test1() { TestContext.WriteLine(e.Message); } + + mock = await general.SetCookie(); + TestContext.WriteLine(mock.Result); + + mock = await general.GetCookie(); + TestContext.WriteLine(mock.Result); } } } \ No newline at end of file From b54b680309d9ca70a408584c108206748d3c17f3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 24 Mar 2022 00:00:35 +1300 Subject: [PATCH 34/62] Fix model nullables in fromMap --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index ce16b3f21..bc92ecf09 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -40,7 +40,7 @@ namespace {{ spec.title | caseUcfirst }}.Models public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new {{ definition.name | caseUcfirst | overrideIdentifier }}( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}({{ property.type | typeName }})map["{{ property.name }}"]{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}({{ property.type | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"]{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} From 6251d56f610e0c9822df9be8256055b9ef3a980d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 24 Mar 2022 00:00:47 +1300 Subject: [PATCH 35/62] Fix model param defaults --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index bc92ecf09..e57aac767 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -23,7 +23,7 @@ namespace {{ spec.title | caseUcfirst }}.Models public {{ definition.name | caseUcfirst | overrideIdentifier }}( {% for property in definition.properties %} - {{ _self.sub_schema(property) }} {{ property.name | caseCamel | escapeKeyword }}{% if not property.required %} = {{ property.default }}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ _self.sub_schema(property) }} {{ property.name | caseCamel | escapeKeyword }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} From c0bf48a5406f6ae013263022090bd3ccf1cbed65 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 24 Mar 2022 00:28:22 +1300 Subject: [PATCH 36/62] Use long instead of int --- src/SDK/Language/DotNet.php | 22 +------- templates/dotnet/src/Appwrite/Client.cs.twig | 2 - .../Appwrite/Helpers/Int32JsonConverter.cs | 51 ------------------- 3 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 825356c40..88e710d1a 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -151,7 +151,7 @@ public function getTypeName($type) { switch ($type) { case self::TYPE_INTEGER: - return 'int'; + return 'long'; case self::TYPE_NUMBER: return 'double'; case self::TYPE_STRING: @@ -286,37 +286,31 @@ public function getFiles() 'scope' => 'default', 'destination' => 'README.md', 'template' => 'dotnet/README.md.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => 'CHANGELOG.md', 'template' => 'dotnet/CHANGELOG.md.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => 'LICENSE', 'template' => 'dotnet/LICENSE.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => '.travis.yml', 'template' => 'dotnet/.travis.yml.twig', - 'minify' => false, ], [ 'scope' => 'method', 'destination' => 'docs/examples/{{service.name | caseLower}}/{{method.name | caseDash}}.md', 'template' => 'dotnet/docs/example.md.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}.sln', 'template' => 'dotnet/src/Appwrite.sln', - 'minify' => false, ], [ 'scope' => 'copy', @@ -327,55 +321,41 @@ public function getFiles() 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/{{ spec.title | caseUcfirst }}.csproj', 'template' => 'dotnet/src/Appwrite/Appwrite.csproj.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Client.cs', 'template' => 'dotnet/src/Appwrite/Client.cs.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Helpers/ExtensionMethods.cs', 'template' => 'dotnet/src/Appwrite/Helpers/ExtensionMethods.cs', - 'minify' => false, - ], - [ - 'scope' => 'default', - 'destination' => '/src/{{ spec.title | caseUcfirst }}/Helpers/Int32JsonConverter.cs', - 'template' => 'dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/OrderType.cs', 'template' => 'dotnet/src/Appwrite/Models/OrderType.cs.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/Exception.cs', 'template' => 'dotnet/src/Appwrite/Models/Exception.cs.twig', - 'minify' => false, ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Services/Service.cs', 'template' => 'dotnet/src/Appwrite/Services/Service.cs.twig', - 'minify' => false, ], [ 'scope' => 'service', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Services/{{service.name | caseUcfirst}}.cs', 'template' => 'dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig', - 'minify' => false, ], [ 'scope' => 'definition', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/{{ definition.name | caseUcfirst | overrideIdentifier }}.cs', 'template' => 'dotnet/src/Appwrite/Models/Model.cs.twig', - 'minify' => false, ] ]; } diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 8b0ab1a57..03f93b665 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -29,7 +29,6 @@ namespace {{ spec.title | caseUcfirst }} NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new List { - new Int32JsonConverter(), new StringEnumConverter() } }; @@ -39,7 +38,6 @@ namespace {{ spec.title | caseUcfirst }} NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new List { - new Int32JsonConverter(), new StringEnumConverter() } }; diff --git a/templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs b/templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs deleted file mode 100644 index df9a1fe8a..000000000 --- a/templates/dotnet/src/Appwrite/Helpers/Int32JsonConverter.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace {{ spec.title | caseUcfirst }} -{ - public class Int32JsonConverter : JsonConverter - { - public override bool CanConvert(Type objectType) => - objectType == typeof(Dictionary); - - public override bool CanWrite => false; - - public override object ReadJson( - JsonReader reader, - Type objectType, - object? existingValue, - JsonSerializer serializer) - { - var result = new Dictionary(); - reader.Read(); - - while (reader.TokenType == JsonToken.PropertyName) - { - string? propertyName = reader.Value as string; - reader.Read(); - - object? value; - if (reader.TokenType == JsonToken.Integer) - { - // Convert to Int32 instead of Int64 - value = Convert.ToInt32(reader.Value); - } - else - { - value = serializer.Deserialize(reader); - } - result.Add(propertyName!, value!); - reader.Read(); - } - - return result; - } - - public override void WriteJson( - JsonWriter writer, - object? value, - JsonSerializer serializer) => - throw new NotImplementedException(); - } -} \ No newline at end of file From cdde5a1136561d22dd2e42e73c13d7da22d771a3 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 31 Mar 2022 23:11:20 +1300 Subject: [PATCH 37/62] WIP add chunked upload --- templates/dotnet/src/Appwrite/Client.cs.twig | 79 +++++++++++++++++++ .../Appwrite/Services/ServiceTemplate.cs.twig | 24 +++++- tests/DotNet50Test.php | 3 +- tests/languages/dotnet/Tests.cs | 10 +-- 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 03f93b665..92ac69f10 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -24,6 +24,8 @@ namespace {{ spec.title | caseUcfirst }} private readonly Dictionary _config; private string _endPoint; + private static int ChunkSize = 5 * 1024 * 1024; + public static JsonSerializerSettings DeserializerSettings { get; set; } = new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, @@ -252,5 +254,82 @@ namespace {{ spec.title | caseUcfirst }} throw new {{spec.title | caseUcfirst}}Exception(e.Message, e); } } + + public async Task ChunkedUpload( + string path, + Dictionary headers, + Dictionary parameters, + Func, T>? converter, + string paramName, + string? idParamName = null, + Action? onProgress = null) where T : class + { + var file = parameters[paramName] as FileInfo + var size = file.Length + var offset = 0; + var input = file.OpenRead(); + var buffer = new byte[ChunkSize]; + var result = new Dictionary(); + + if (size < ChunkSize) { + await input.ReadAsync(buffer, 0, (int)size); + var content = new MultipartFormDataContent(); + content.Add(new ByteArrayContent(buffer), paramName, file.Name); + parameters[paramName] = content; + return await Call( + method: "POST", + path, + headers, + parameters, + converter + ) + } + + if (idParamName?.IsNullOrEmpty() && parameters[idParamName] != "unique()") { + // Make a request to check if a file already exists + var current = await Call>( + method: "GET", + path: "$path/${params[idParamName]}", + headers, + parameters = new Dictionary() + ) + var chunksUploaded = (long)current["chunksUploaded"] + offset = Math.Min((chunksUploaded * ChunkSize), size) + } + + while (offset < size) { + input.Seek(offset, SeekOrigin.Begin); + + await input.ReadAsync(buffer, 0, ChunkSize); + + var content = new MultipartFormDataContent(); + content.Add(new ByteArrayContent(buffer), paramName, file.Name); + + parameters[paramName] = content; + + headers["Content-Range"] = + $"bytes {offset}-{Math.Min(((offset + CHUNK_SIZE) - 1), size)}/{size}"; + + result = Call>( + method: "POST", + path, + headers, + parameters + ) + + offset += ChunkSize + headers["x-appwrite-id"] = result["$id"].ToString() + + onProgress?.Invoke( + UploadProgress( + id = result["$id"].ToString(), + progress = Math.Min(offset, size) / size * 100, + sizeUploaded = Math.Min(offset, size), + chunksTotal = long.Parse(result["chunksTotal"].ToString()), + chunksUploaded = long.Parse(["chunksUploaded"].ToString()))) + } + + return converter(result); + } } } diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index d2bec700d..6ee21e0c5 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -87,6 +87,27 @@ namespace {{ spec.title | caseUcfirst }} {% endif %} {% endif %} +{% if 'multipart/form-data' in method.consumes %} + var idParamName: String? = {% if method.parameters.all | filter(p => p.isUploadID) | length > 0 %}{% for parameter in method.parameters.all | filter(parameter => parameter.isUploadID) %}"{{ parameter.name }}"{% endfor %}{% else %}null{% endif %}; + +{% for parameter in method.parameters.all %} +{% if parameter.type == 'file' %} + var paramName = "{{ parameter.name }}"; +{% endif %} +{% endfor %} + + return _client.ChunkedUpload( + path, + headers, + params, +{% if method.responseModel %} + converter, +{% endif %} + paramName, + idParamName, + onProgress); +{% else %} + return _client.Call{% if method.type != 'webAuth' %}<{{ _self.resultType(spec.title, method) }}>{% endif %}( method: "{{ method.method | caseUpper }}", path: path, @@ -97,11 +118,12 @@ namespace {{ spec.title | caseUcfirst }} parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, convert: Convert); {% endif %} +{% endif %} {% endif %} } {% if not loop.last %} {% endif %} {% endfor %} - }; + } } \ No newline at end of file diff --git a/tests/DotNet50Test.php b/tests/DotNet50Test.php index b6c845c36..8479b1044 100644 --- a/tests/DotNet50Test.php +++ b/tests/DotNet50Test.php @@ -18,7 +18,8 @@ class DotNet50Test extends Base ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, -// ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, +// ...Base::COOKIE_RESPONSES, ]; } diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index 1526b9257..ad5ad580d 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -95,11 +95,11 @@ public async Task Test1() TestContext.WriteLine(e.Message); } - mock = await general.SetCookie(); - TestContext.WriteLine(mock.Result); - - mock = await general.GetCookie(); - TestContext.WriteLine(mock.Result); +// mock = await general.SetCookie(); +// TestContext.WriteLine(mock.Result); +// +// mock = await general.GetCookie(); +// TestContext.WriteLine(mock.Result); } } } \ No newline at end of file From 475f59c2e7315ec2cf33e0df132b19d9d90006e9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 13 Apr 2022 23:15:40 +1200 Subject: [PATCH 38/62] Chunked upload updates --- src/SDK/Language/DotNet.php | 5 ++ .../dotnet/src/Appwrite/Appwrite.csproj.twig | 2 +- templates/dotnet/src/Appwrite/Client.cs.twig | 85 +++++++++++-------- .../src/Appwrite/Helpers/ExtensionMethods.cs | 4 +- .../Appwrite/Models/UploadProgress.cs.twig | 19 +++++ .../Appwrite/Services/ServiceTemplate.cs.twig | 12 +-- tests/DotNet31Test.php | 2 +- tests/DotNet50Test.php | 1 - tests/DotNet60Test.php | 2 +- 9 files changed, 84 insertions(+), 48 deletions(-) create mode 100644 templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 88e710d1a..9ee0f96ba 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -337,6 +337,11 @@ public function getFiles() 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/OrderType.cs', 'template' => 'dotnet/src/Appwrite/Models/OrderType.cs.twig', ], + [ + 'scope' => 'default', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/UploadProgress.cs', + 'template' => 'dotnet/src/Appwrite/Models/UploadProgress.cs.twig', + ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/Exception.cs', diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index 8353cb7ca..f4da089f5 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -1,6 +1,6 @@ - net47;net48;net5.0;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1 + net47;net48;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1 {{spec.title}} {{sdk.version}} {{spec.contactName}} diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 92ac69f10..c3d57f1b9 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -115,7 +115,7 @@ namespace {{ spec.title | caseUcfirst }} string method, string path, Dictionary headers, - Dictionary parameters) + Dictionary parameters) { return Call(method, path, headers, parameters); } @@ -124,7 +124,7 @@ namespace {{ spec.title | caseUcfirst }} string method, string path, Dictionary headers, - Dictionary parameters, + Dictionary parameters, Func, T>? convert = null) where T : class { var methodGet = "GET".Equals(method, StringComparison.OrdinalIgnoreCase); @@ -147,10 +147,7 @@ namespace {{ spec.title | caseUcfirst }} { if (parameter.Key == "file") { - var fi = (FileInfo)parameters["file"]; - var file = File.ReadAllBytes(fi.FullName); - - form.Add(new ByteArrayContent(file, 0, file.Length), "file", fi.Name); + form.Add(((MultipartFormDataContent)parameters["file"]).First()!); } else if (parameter.Value is IEnumerable enumerable) { @@ -196,6 +193,10 @@ namespace {{ spec.title | caseUcfirst }} { request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Value)); } + else if (header.Key.Equals("content-range", StringComparison.OrdinalIgnoreCase)) + { + request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value); + } else { if (request.Headers.Contains(header.Key)) { @@ -255,46 +256,47 @@ namespace {{ spec.title | caseUcfirst }} } } - public async Task ChunkedUpload( + public async Task ChunkedUpload( string path, Dictionary headers, - Dictionary parameters, - Func, T>? converter, + Dictionary parameters, + Func, T> converter, string paramName, string? idParamName = null, Action? onProgress = null) where T : class { - var file = parameters[paramName] as FileInfo - var size = file.Length - var offset = 0; - var input = file.OpenRead(); - var buffer = new byte[ChunkSize]; - var result = new Dictionary(); + var file = parameters[paramName] as FileInfo; + var size = file!.Length; + var offset = 0L; + var input = file!.OpenRead(); + var buffer = new byte[Math.Min(size, ChunkSize)]; + var result = new Dictionary(); if (size < ChunkSize) { await input.ReadAsync(buffer, 0, (int)size); - var content = new MultipartFormDataContent(); - content.Add(new ByteArrayContent(buffer), paramName, file.Name); + var content = new MultipartFormDataContent { + { new ByteArrayContent(buffer), paramName, file.Name } + }; parameters[paramName] = content; - return await Call( + return await Call( method: "POST", path, headers, parameters, converter - ) + ); } - if (idParamName?.IsNullOrEmpty() && parameters[idParamName] != "unique()") { + if (!string.IsNullOrEmpty(idParamName) && (string)parameters[idParamName] != "unique()") { // Make a request to check if a file already exists - var current = await Call>( + var current = await Call>( method: "GET", path: "$path/${params[idParamName]}", headers, - parameters = new Dictionary() - ) - var chunksUploaded = (long)current["chunksUploaded"] - offset = Math.Min((chunksUploaded * ChunkSize), size) + parameters = new Dictionary() + ); + var chunksUploaded = (long)current["chunksUploaded"]; + offset = Math.Min(chunksUploaded * ChunkSize, size); } while (offset < size) { @@ -308,25 +310,36 @@ namespace {{ spec.title | caseUcfirst }} parameters[paramName] = content; headers["Content-Range"] = - $"bytes {offset}-{Math.Min(((offset + CHUNK_SIZE) - 1), size)}/{size}"; + $"bytes {offset}-{Math.Min(offset + ChunkSize - 1, size)}/{size}"; - result = Call>( + result = await Call>( method: "POST", path, headers, parameters - ) + ); + + offset += ChunkSize; + + var id = result.ContainsKey("$id") + ? result["$id"]?.ToString() ?? string.Empty + : string.Empty; + var chunksTotal = result.ContainsKey("chunksTotal") + ? (long)result["chunksTotal"] + : 0; + var chunksUploaded = result.ContainsKey("chunksUploaded") + ? (long)result["chunksUploaded"] + : 0; - offset += ChunkSize - headers["x-appwrite-id"] = result["$id"].ToString() + headers["x-appwrite-id"] = id; onProgress?.Invoke( - UploadProgress( - id = result["$id"].ToString(), - progress = Math.Min(offset, size) / size * 100, - sizeUploaded = Math.Min(offset, size), - chunksTotal = long.Parse(result["chunksTotal"].ToString()), - chunksUploaded = long.Parse(["chunksUploaded"].ToString()))) + new UploadProgress( + id: id, + progress: Math.Min(offset, size) / size * 100, + sizeUploaded: Math.Min(offset, size), + chunksTotal: chunksTotal, + chunksUploaded: chunksUploaded)); } return converter(result); diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs index 8efde9f02..1d5d9c579 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs @@ -8,12 +8,12 @@ namespace {{ spec.title | caseUcfirst }} { public static class ExtensionMethods { - public static string ToJson(this Dictionary dict) + public static string ToJson(this Dictionary dict) { return JsonConvert.SerializeObject(dict, Client.SerializerSettings); } - public static string ToQueryString(this Dictionary parameters) + public static string ToQueryString(this Dictionary parameters) { var query = new List(); diff --git a/templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig b/templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig new file mode 100644 index 000000000..bbb502ce8 --- /dev/null +++ b/templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig @@ -0,0 +1,19 @@ +namespace {{ spec.title | caseUcfirst }} +{ + public class UploadProgress + { + public UploadProgress(string id, double progress, long sizeUploaded, long chunksTotal, long chunksUploaded) + { + Id = id; + Progress = progress; + SizeUploaded = sizeUploaded; + ChunksTotal = chunksTotal; + ChunksUploaded = chunksUploaded; + } + public string Id { get; private set; } + public double Progress { get; private set; } + public long SizeUploaded { get; private set; } + public long ChunksTotal { get; private set; } + public long ChunksUploaded { get; private set; } + } +} \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 6ee21e0c5..989198d9b 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -2,8 +2,8 @@ {% if parameter.name == 'orderType' %}{{ 'OrderType orderType = OrderType.ASC' }}{% else %} {{ parameter.type | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} {% endmacro %} -{% macro method_parameters(parameters) %} -{% if parameters.all|length > 0 %}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% endif %} +{% macro method_parameters(parameters, consumes) %} +{% if parameters.all|length > 0 %}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} {% endmacro %} {% macro map_parameter(parameter) %} {% if parameter.name == 'orderType' %}{{ parameter.name | caseCamel ~ '.ToString()'}}{% else %} @@ -40,7 +40,7 @@ namespace {{ spec.title | caseUcfirst }} /// {% endif %} /// - public Task{% if method.type != "webAuth" %}<{{ _self.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters) }}) + public Task{% if method.type != "webAuth" %}<{{ _self.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters, method.consumes) }}) { var path = "{{ method.path }}"{% if method.parameters.path | length == 0 %};{% endif %} @@ -88,7 +88,7 @@ namespace {{ spec.title | caseUcfirst }} {% endif %} {% if 'multipart/form-data' in method.consumes %} - var idParamName: String? = {% if method.parameters.all | filter(p => p.isUploadID) | length > 0 %}{% for parameter in method.parameters.all | filter(parameter => parameter.isUploadID) %}"{{ parameter.name }}"{% endfor %}{% else %}null{% endif %}; + string? idParamName = {% if method.parameters.all | filter(p => p.isUploadID) | length > 0 %}{% for parameter in method.parameters.all | filter(parameter => parameter.isUploadID) %}"{{ parameter.name }}"{% endfor %}{% else %}null{% endif %}; {% for parameter in method.parameters.all %} {% if parameter.type == 'file' %} @@ -99,9 +99,9 @@ namespace {{ spec.title | caseUcfirst }} return _client.ChunkedUpload( path, headers, - params, + parameters, {% if method.responseModel %} - converter, + Convert, {% endif %} paramName, idParamName, diff --git a/tests/DotNet31Test.php b/tests/DotNet31Test.php index ce90956fc..a2e87725b 100644 --- a/tests/DotNet31Test.php +++ b/tests/DotNet31Test.php @@ -18,7 +18,7 @@ class DotNet31Test extends Base ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, -// ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, ]; } diff --git a/tests/DotNet50Test.php b/tests/DotNet50Test.php index 8479b1044..291b09260 100644 --- a/tests/DotNet50Test.php +++ b/tests/DotNet50Test.php @@ -20,6 +20,5 @@ class DotNet50Test extends Base ...Base::GENERAL_RESPONSES, ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, -// ...Base::COOKIE_RESPONSES, ]; } diff --git a/tests/DotNet60Test.php b/tests/DotNet60Test.php index eddb6298a..20bc5d989 100644 --- a/tests/DotNet60Test.php +++ b/tests/DotNet60Test.php @@ -18,7 +18,7 @@ class DotNet60Test extends Base ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, -// ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, ]; } From a1fff24baf3c3a48b2d6d730f4919cbb77790c17 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 13 Apr 2022 23:23:09 +1200 Subject: [PATCH 39/62] Fix .NET 6 test --- tests/languages/dotnet/Tests.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/languages/dotnet/Tests.csproj b/tests/languages/dotnet/Tests.csproj index 2f1cea22e..2a9e95d77 100644 --- a/tests/languages/dotnet/Tests.csproj +++ b/tests/languages/dotnet/Tests.csproj @@ -1,16 +1,16 @@ - net48;net5.0;netcoreapp3.1 + net48;net5.0;net6.0;netcoreapp3.1 latest false - - - - + + + + From 5bab0f0cfa817d374b742239a0993bba80e92b1b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 13 Apr 2022 23:44:45 +1200 Subject: [PATCH 40/62] Properly fix tests --- tests/DotNet31Test.php | 2 +- tests/DotNet50Test.php | 2 +- tests/DotNet60Test.php | 2 +- .../dotnet/{Tests.csproj => Tests31.csproj} | 6 +----- tests/languages/dotnet/Tests50.csproj | 14 ++++++++++++++ tests/languages/dotnet/Tests60.csproj | 14 ++++++++++++++ 6 files changed, 32 insertions(+), 8 deletions(-) rename tests/languages/dotnet/{Tests.csproj => Tests31.csproj} (86%) create mode 100644 tests/languages/dotnet/Tests50.csproj create mode 100644 tests/languages/dotnet/Tests60.csproj diff --git a/tests/DotNet31Test.php b/tests/DotNet31Test.php index a2e87725b..e3306737a 100644 --- a/tests/DotNet31Test.php +++ b/tests/DotNet31Test.php @@ -9,7 +9,7 @@ class DotNet31Test extends Base protected array $build = [ 'mkdir -p tests/sdks/dotnet/src/test', 'cp tests/languages/dotnet/Tests.cs tests/sdks/dotnet/src/test/Tests.cs', - 'cp tests/languages/dotnet/Tests.csproj tests/sdks/dotnet/src/test/Tests.csproj', + 'cp tests/languages/dotnet/Tests31.csproj tests/sdks/dotnet/src/test/Tests.csproj', ]; protected string $command = 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:3.1 dotnet test --verbosity normal --framework netcoreapp3.1'; diff --git a/tests/DotNet50Test.php b/tests/DotNet50Test.php index 291b09260..1be527c59 100644 --- a/tests/DotNet50Test.php +++ b/tests/DotNet50Test.php @@ -9,7 +9,7 @@ class DotNet50Test extends Base protected array $build = [ 'mkdir -p tests/sdks/dotnet/src/test', 'cp tests/languages/dotnet/Tests.cs tests/sdks/dotnet/src/test/Tests.cs', - 'cp tests/languages/dotnet/Tests.csproj tests/sdks/dotnet/src/test/Tests.csproj', + 'cp tests/languages/dotnet/Tests50.csproj tests/sdks/dotnet/src/test/Tests.csproj', ]; protected string $command = 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim dotnet test --verbosity normal --framework net5.0'; diff --git a/tests/DotNet60Test.php b/tests/DotNet60Test.php index 20bc5d989..29c944c4e 100644 --- a/tests/DotNet60Test.php +++ b/tests/DotNet60Test.php @@ -9,7 +9,7 @@ class DotNet60Test extends Base protected array $build = [ 'mkdir -p tests/sdks/dotnet/src/test', 'cp tests/languages/dotnet/Tests.cs tests/sdks/dotnet/src/test/Tests.cs', - 'cp tests/languages/dotnet/Tests.csproj tests/sdks/dotnet/src/test/Tests.csproj', + 'cp tests/languages/dotnet/Tests60.csproj tests/sdks/dotnet/src/test/Tests.csproj', ]; protected string $command = 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:6.0-alpine dotnet test --verbosity normal --framework net6.0'; diff --git a/tests/languages/dotnet/Tests.csproj b/tests/languages/dotnet/Tests31.csproj similarity index 86% rename from tests/languages/dotnet/Tests.csproj rename to tests/languages/dotnet/Tests31.csproj index 2a9e95d77..06f9420ba 100644 --- a/tests/languages/dotnet/Tests.csproj +++ b/tests/languages/dotnet/Tests31.csproj @@ -1,18 +1,14 @@ - - net48;net5.0;net6.0;netcoreapp3.1 + netcoreapp3.1 latest false - - - diff --git a/tests/languages/dotnet/Tests50.csproj b/tests/languages/dotnet/Tests50.csproj new file mode 100644 index 000000000..8f77f7f7c --- /dev/null +++ b/tests/languages/dotnet/Tests50.csproj @@ -0,0 +1,14 @@ + + + net5.0 + latest + false + + + + + + + + + diff --git a/tests/languages/dotnet/Tests60.csproj b/tests/languages/dotnet/Tests60.csproj new file mode 100644 index 000000000..15ffbc6b4 --- /dev/null +++ b/tests/languages/dotnet/Tests60.csproj @@ -0,0 +1,14 @@ + + + net6.0 + latest + false + + + + + + + + + From 838d90f3389ca3f24d7de9cef7a31c5236f1b60a Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 13 Apr 2022 23:47:58 +1200 Subject: [PATCH 41/62] Update travis --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 683a43ed8..58095bab6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,9 @@ env: - SDK=DartBeta - SDK=DartStable - SDK=Deno1171 + - SDK=DotNet31 + - SDK=DotNet50 + - SDK=DotNet60 - SDK=FlutterStable - SDK=KotlinJava8 - SDK=KotlinJava11 From edc4a46e7fc3cdd4f64b4234bc3fd8b39e3586f6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 14 Apr 2022 22:58:19 +1200 Subject: [PATCH 42/62] Fix models --- templates/dotnet/src/Appwrite/Client.cs.twig | 6 +++--- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index c3d57f1b9..43811cf5c 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -24,7 +24,7 @@ namespace {{ spec.title | caseUcfirst }} private readonly Dictionary _config; private string _endPoint; - private static int ChunkSize = 5 * 1024 * 1024; + private static readonly int ChunkSize = 5 * 1024 * 1024; public static JsonSerializerSettings DeserializerSettings { get; set; } = new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore, @@ -111,13 +111,13 @@ namespace {{ spec.title | caseUcfirst }} return this; } - public Task Call( + public Task> Call( string method, string path, Dictionary headers, Dictionary parameters) { - return Call(method, path, headers, parameters); + return Call>(method, path, headers, parameters); } public async Task Call( diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index e57aac767..784263f10 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -38,9 +38,9 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} } - public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new {{ definition.name | caseUcfirst | overrideIdentifier }}( + public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new( {% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}({{ property.type | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"]{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}{% if property.type == "integer" or property.type == "number" %}{% if not property.required %}map["{{ property.name }}"] == null ? null : {% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]){% else %}({{ property.type | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"]{% endif %}{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} {% endfor %} {% if definition.additionalProperties %} @@ -48,7 +48,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} ); - public Dictionary ToMap() => new Dictionary() + public Dictionary ToMap() => new() { {% for property in definition.properties %} { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ _self.property_name(definition, property) }}.Select(it => it.ToMap()){% else %}{{ _self.property_name(definition, property) }}.ToMap(){% endif %}{% else %}{{ _self.property_name(definition, property) }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} From 0665d54f3a057b1152de06dbfe3439c25747a181 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 28 Apr 2022 20:38:30 +1200 Subject: [PATCH 43/62] Fix .NET 3.1 target typed creation --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index 784263f10..ad3169927 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -48,7 +48,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} ); - public Dictionary ToMap() => new() + public Dictionary ToMap() => new Dictionary() { {% for property in definition.properties %} { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ _self.property_name(definition, property) }}.Select(it => it.ToMap()){% else %}{{ _self.property_name(definition, property) }}.ToMap(){% endif %}{% else %}{{ _self.property_name(definition, property) }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} From 1debc762960e68111703a564d6ec1a04d59404af Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 28 Apr 2022 21:24:28 +1200 Subject: [PATCH 44/62] Fix other target typed creation ref --- templates/dotnet/src/Appwrite/Models/Model.cs.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index ad3169927..f19f4aeff 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -38,7 +38,7 @@ namespace {{ spec.title | caseUcfirst }}.Models {% endif %} } - public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new( + public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new {{ definition.name | caseUcfirst | overrideIdentifier}}( {% for property in definition.properties %} {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}{% if property.type == "integer" or property.type == "number" %}{% if not property.required %}map["{{ property.name }}"] == null ? null : {% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]){% else %}({{ property.type | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"]{% endif %}{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} From 1953ceeb1ad646bf95dead56351fb004e67965e9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 29 Jun 2022 22:09:40 +1200 Subject: [PATCH 45/62] Update for 0.15 --- src/SDK/Language/DotNet.php | 7 +- templates/dotnet/src/Appwrite/Client.cs.twig | 69 +- .../src/Appwrite/Helpers/ExtensionMethods.cs | 590 ++++++++++++++++++ .../src/Appwrite/Models/InputFile.cs.twig | 42 ++ .../Appwrite/Services/ServiceTemplate.cs.twig | 21 +- tests/DotNet31Test.php | 2 + tests/DotNet50Test.php | 2 + tests/DotNet60Test.php | 2 + tests/languages/dotnet/Tests.cs | 24 +- 9 files changed, 734 insertions(+), 25 deletions(-) create mode 100644 templates/dotnet/src/Appwrite/Models/InputFile.cs.twig diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 109751574..1e5fa5627 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -158,7 +158,7 @@ public function getTypeName($type) case self::TYPE_STRING: return 'string'; case self::TYPE_FILE: - return 'FileInfo'; + return 'InputFile'; case self::TYPE_BOOLEAN: return 'bool'; case self::TYPE_ARRAY: @@ -348,6 +348,11 @@ public function getFiles() 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/Exception.cs', 'template' => 'dotnet/src/Appwrite/Models/Exception.cs.twig', ], + [ + 'scope' => 'default', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/InputFile.cs', + 'template' => 'dotnet/src/Appwrite/Models/InputFile.cs.twig', + ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Services/Service.cs', diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 43811cf5c..5a149a860 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -11,6 +11,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; +using {{ spec.title | caseUcfirst }}.Models; namespace {{ spec.title | caseUcfirst }} { @@ -265,19 +266,46 @@ namespace {{ spec.title | caseUcfirst }} string? idParamName = null, Action? onProgress = null) where T : class { - var file = parameters[paramName] as FileInfo; - var size = file!.Length; + var input = parameters[paramName] as InputFile; + var size = 0L; + switch(input.SourceType) + { + case "path": + var info = new FileInfo(input.Path); + input.Data = info.OpenRead(); + size = info.Length; + break; + case "stream": + size = (input.Data as Stream).Length; + break; + case "bytes": + size = ((byte[])input.Data).Length; + break; + }; + var offset = 0L; - var input = file!.OpenRead(); var buffer = new byte[Math.Min(size, ChunkSize)]; var result = new Dictionary(); - if (size < ChunkSize) { - await input.ReadAsync(buffer, 0, (int)size); + if (size < ChunkSize) + { + switch(input.SourceType) + { + case "path": + case "stream": + await (input.Data as Stream).ReadAsync(buffer, 0, (int)size); + break; + case "bytes": + buffer = (byte[])input.Data; + break; + } + var content = new MultipartFormDataContent { - { new ByteArrayContent(buffer), paramName, file.Name } + { new ByteArrayContent(buffer), paramName, input.Filename } }; + parameters[paramName] = content; + return await Call( method: "POST", path, @@ -287,7 +315,8 @@ namespace {{ spec.title | caseUcfirst }} ); } - if (!string.IsNullOrEmpty(idParamName) && (string)parameters[idParamName] != "unique()") { + if (!string.IsNullOrEmpty(idParamName) && (string)parameters[idParamName] != "unique()") + { // Make a request to check if a file already exists var current = await Call>( method: "GET", @@ -299,13 +328,27 @@ namespace {{ spec.title | caseUcfirst }} offset = Math.Min(chunksUploaded * ChunkSize, size); } - while (offset < size) { - input.Seek(offset, SeekOrigin.Begin); - - await input.ReadAsync(buffer, 0, ChunkSize); + while (offset < size) + { + switch(input.SourceType) + { + case "path": + case "stream": + var stream = input.Data as Stream; + stream.Seek(offset, SeekOrigin.Begin); + await stream.ReadAsync(buffer, 0, ChunkSize); + break; + case "bytes": + buffer = ((byte[])input.Data) + .Skip((int)offset) + .Take((int)Math.Min(size - offset, ChunkSize)) + .ToArray(); + break; + } - var content = new MultipartFormDataContent(); - content.Add(new ByteArrayContent(buffer), paramName, file.Name); + var content = new MultipartFormDataContent { + { new ByteArrayContent(buffer), paramName, input.Filename } + }; parameters[paramName] = content; diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs index 1d5d9c579..5ead38429 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System; +using System.IO; using System.Collections.Generic; namespace {{ spec.title | caseUcfirst }} @@ -37,5 +38,594 @@ public static string ToQueryString(this Dictionary parameters) } return string.Join("&", query); } + + private static IDictionary _mappings = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { + + #region Big freaking list of mime type + {".323", "text/h323"}, + {".3g2", "video/3gpp2"}, + {".3gp", "video/3gpp"}, + {".3gp2", "video/3gpp2"}, + {".3gpp", "video/3gpp"}, + {".7z", "application/x-7z-compressed"}, + {".aa", "audio/audible"}, + {".AAC", "audio/aac"}, + {".aaf", "application/octet-stream"}, + {".aax", "audio/vnd.audible.aax"}, + {".ac3", "audio/ac3"}, + {".aca", "application/octet-stream"}, + {".accda", "application/msaccess.addin"}, + {".accdb", "application/msaccess"}, + {".accdc", "application/msaccess.cab"}, + {".accde", "application/msaccess"}, + {".accdr", "application/msaccess.runtime"}, + {".accdt", "application/msaccess"}, + {".accdw", "application/msaccess.webapplication"}, + {".accft", "application/msaccess.ftemplate"}, + {".acx", "application/internet-property-stream"}, + {".AddIn", "text/xml"}, + {".ade", "application/msaccess"}, + {".adobebridge", "application/x-bridge-url"}, + {".adp", "application/msaccess"}, + {".ADT", "audio/vnd.dlna.adts"}, + {".ADTS", "audio/aac"}, + {".afm", "application/octet-stream"}, + {".ai", "application/postscript"}, + {".aif", "audio/x-aiff"}, + {".aifc", "audio/aiff"}, + {".aiff", "audio/aiff"}, + {".air", "application/vnd.adobe.air-application-installer-package+zip"}, + {".amc", "application/x-mpeg"}, + {".application", "application/x-ms-application"}, + {".art", "image/x-jg"}, + {".asa", "application/xml"}, + {".asax", "application/xml"}, + {".ascx", "application/xml"}, + {".asd", "application/octet-stream"}, + {".asf", "video/x-ms-asf"}, + {".ashx", "application/xml"}, + {".asi", "application/octet-stream"}, + {".asm", "text/plain"}, + {".asmx", "application/xml"}, + {".aspx", "application/xml"}, + {".asr", "video/x-ms-asf"}, + {".asx", "video/x-ms-asf"}, + {".atom", "application/atom+xml"}, + {".au", "audio/basic"}, + {".avi", "video/x-msvideo"}, + {".axs", "application/olescript"}, + {".bas", "text/plain"}, + {".bcpio", "application/x-bcpio"}, + {".bin", "application/octet-stream"}, + {".bmp", "image/bmp"}, + {".c", "text/plain"}, + {".cab", "application/octet-stream"}, + {".caf", "audio/x-caf"}, + {".calx", "application/vnd.ms-office.calx"}, + {".cat", "application/vnd.ms-pki.seccat"}, + {".cc", "text/plain"}, + {".cd", "text/plain"}, + {".cdda", "audio/aiff"}, + {".cdf", "application/x-cdf"}, + {".cer", "application/x-x509-ca-cert"}, + {".chm", "application/octet-stream"}, + {".class", "application/x-java-applet"}, + {".clp", "application/x-msclip"}, + {".cmx", "image/x-cmx"}, + {".cnf", "text/plain"}, + {".cod", "image/cis-cod"}, + {".config", "application/xml"}, + {".contact", "text/x-ms-contact"}, + {".coverage", "application/xml"}, + {".cpio", "application/x-cpio"}, + {".cpp", "text/plain"}, + {".crd", "application/x-mscardfile"}, + {".crl", "application/pkix-crl"}, + {".crt", "application/x-x509-ca-cert"}, + {".cs", "text/plain"}, + {".csdproj", "text/plain"}, + {".csh", "application/x-csh"}, + {".csproj", "text/plain"}, + {".css", "text/css"}, + {".csv", "text/csv"}, + {".cur", "application/octet-stream"}, + {".cxx", "text/plain"}, + {".dat", "application/octet-stream"}, + {".datasource", "application/xml"}, + {".dbproj", "text/plain"}, + {".dcr", "application/x-director"}, + {".def", "text/plain"}, + {".deploy", "application/octet-stream"}, + {".der", "application/x-x509-ca-cert"}, + {".dgml", "application/xml"}, + {".dib", "image/bmp"}, + {".dif", "video/x-dv"}, + {".dir", "application/x-director"}, + {".disco", "text/xml"}, + {".dll", "application/x-msdownload"}, + {".dll.config", "text/xml"}, + {".dlm", "text/dlm"}, + {".doc", "application/msword"}, + {".docm", "application/vnd.ms-word.document.macroEnabled.12"}, + {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {".dot", "application/msword"}, + {".dotm", "application/vnd.ms-word.template.macroEnabled.12"}, + {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, + {".dsp", "application/octet-stream"}, + {".dsw", "text/plain"}, + {".dtd", "text/xml"}, + {".dtsConfig", "text/xml"}, + {".dv", "video/x-dv"}, + {".dvi", "application/x-dvi"}, + {".dwf", "drawing/x-dwf"}, + {".dwp", "application/octet-stream"}, + {".dxr", "application/x-director"}, + {".eml", "message/rfc822"}, + {".emz", "application/octet-stream"}, + {".eot", "application/octet-stream"}, + {".eps", "application/postscript"}, + {".etl", "application/etl"}, + {".etx", "text/x-setext"}, + {".evy", "application/envoy"}, + {".exe", "application/octet-stream"}, + {".exe.config", "text/xml"}, + {".fdf", "application/vnd.fdf"}, + {".fif", "application/fractals"}, + {".filters", "Application/xml"}, + {".fla", "application/octet-stream"}, + {".flr", "x-world/x-vrml"}, + {".flv", "video/x-flv"}, + {".fsscript", "application/fsharp-script"}, + {".fsx", "application/fsharp-script"}, + {".generictest", "application/xml"}, + {".gif", "image/gif"}, + {".group", "text/x-ms-group"}, + {".gsm", "audio/x-gsm"}, + {".gtar", "application/x-gtar"}, + {".gz", "application/x-gzip"}, + {".h", "text/plain"}, + {".hdf", "application/x-hdf"}, + {".hdml", "text/x-hdml"}, + {".hhc", "application/x-oleobject"}, + {".hhk", "application/octet-stream"}, + {".hhp", "application/octet-stream"}, + {".hlp", "application/winhlp"}, + {".hpp", "text/plain"}, + {".hqx", "application/mac-binhex40"}, + {".hta", "application/hta"}, + {".htc", "text/x-component"}, + {".htm", "text/html"}, + {".html", "text/html"}, + {".htt", "text/webviewhtml"}, + {".hxa", "application/xml"}, + {".hxc", "application/xml"}, + {".hxd", "application/octet-stream"}, + {".hxe", "application/xml"}, + {".hxf", "application/xml"}, + {".hxh", "application/octet-stream"}, + {".hxi", "application/octet-stream"}, + {".hxk", "application/xml"}, + {".hxq", "application/octet-stream"}, + {".hxr", "application/octet-stream"}, + {".hxs", "application/octet-stream"}, + {".hxt", "text/html"}, + {".hxv", "application/xml"}, + {".hxw", "application/octet-stream"}, + {".hxx", "text/plain"}, + {".i", "text/plain"}, + {".ico", "image/x-icon"}, + {".ics", "application/octet-stream"}, + {".idl", "text/plain"}, + {".ief", "image/ief"}, + {".iii", "application/x-iphone"}, + {".inc", "text/plain"}, + {".inf", "application/octet-stream"}, + {".inl", "text/plain"}, + {".ins", "application/x-internet-signup"}, + {".ipa", "application/x-itunes-ipa"}, + {".ipg", "application/x-itunes-ipg"}, + {".ipproj", "text/plain"}, + {".ipsw", "application/x-itunes-ipsw"}, + {".iqy", "text/x-ms-iqy"}, + {".isp", "application/x-internet-signup"}, + {".ite", "application/x-itunes-ite"}, + {".itlp", "application/x-itunes-itlp"}, + {".itms", "application/x-itunes-itms"}, + {".itpc", "application/x-itunes-itpc"}, + {".IVF", "video/x-ivf"}, + {".jar", "application/java-archive"}, + {".java", "application/octet-stream"}, + {".jck", "application/liquidmotion"}, + {".jcz", "application/liquidmotion"}, + {".jfif", "image/pjpeg"}, + {".jnlp", "application/x-java-jnlp-file"}, + {".jpb", "application/octet-stream"}, + {".jpe", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".jpg", "image/jpeg"}, + {".js", "application/x-javascript"}, + {".json", "application/json"}, + {".jsx", "text/jscript"}, + {".jsxbin", "text/plain"}, + {".latex", "application/x-latex"}, + {".library-ms", "application/windows-library+xml"}, + {".lit", "application/x-ms-reader"}, + {".loadtest", "application/xml"}, + {".lpk", "application/octet-stream"}, + {".lsf", "video/x-la-asf"}, + {".lst", "text/plain"}, + {".lsx", "video/x-la-asf"}, + {".lzh", "application/octet-stream"}, + {".m13", "application/x-msmediaview"}, + {".m14", "application/x-msmediaview"}, + {".m1v", "video/mpeg"}, + {".m2t", "video/vnd.dlna.mpeg-tts"}, + {".m2ts", "video/vnd.dlna.mpeg-tts"}, + {".m2v", "video/mpeg"}, + {".m3u", "audio/x-mpegurl"}, + {".m3u8", "audio/x-mpegurl"}, + {".m4a", "audio/m4a"}, + {".m4b", "audio/m4b"}, + {".m4p", "audio/m4p"}, + {".m4r", "audio/x-m4r"}, + {".m4v", "video/x-m4v"}, + {".mac", "image/x-macpaint"}, + {".mak", "text/plain"}, + {".man", "application/x-troff-man"}, + {".manifest", "application/x-ms-manifest"}, + {".map", "text/plain"}, + {".master", "application/xml"}, + {".mda", "application/msaccess"}, + {".mdb", "application/x-msaccess"}, + {".mde", "application/msaccess"}, + {".mdp", "application/octet-stream"}, + {".me", "application/x-troff-me"}, + {".mfp", "application/x-shockwave-flash"}, + {".mht", "message/rfc822"}, + {".mhtml", "message/rfc822"}, + {".mid", "audio/mid"}, + {".midi", "audio/mid"}, + {".mix", "application/octet-stream"}, + {".mk", "text/plain"}, + {".mmf", "application/x-smaf"}, + {".mno", "text/xml"}, + {".mny", "application/x-msmoney"}, + {".mod", "video/mpeg"}, + {".mov", "video/quicktime"}, + {".movie", "video/x-sgi-movie"}, + {".mp2", "video/mpeg"}, + {".mp2v", "video/mpeg"}, + {".mp3", "audio/mpeg"}, + {".mp4", "video/mp4"}, + {".mp4v", "video/mp4"}, + {".mpa", "video/mpeg"}, + {".mpe", "video/mpeg"}, + {".mpeg", "video/mpeg"}, + {".mpf", "application/vnd.ms-mediapackage"}, + {".mpg", "video/mpeg"}, + {".mpp", "application/vnd.ms-project"}, + {".mpv2", "video/mpeg"}, + {".mqv", "video/quicktime"}, + {".ms", "application/x-troff-ms"}, + {".msi", "application/octet-stream"}, + {".mso", "application/octet-stream"}, + {".mts", "video/vnd.dlna.mpeg-tts"}, + {".mtx", "application/xml"}, + {".mvb", "application/x-msmediaview"}, + {".mvc", "application/x-miva-compiled"}, + {".mxp", "application/x-mmxp"}, + {".nc", "application/x-netcdf"}, + {".nsc", "video/x-ms-asf"}, + {".nws", "message/rfc822"}, + {".ocx", "application/octet-stream"}, + {".oda", "application/oda"}, + {".odc", "text/x-ms-odc"}, + {".odh", "text/plain"}, + {".odl", "text/plain"}, + {".odp", "application/vnd.oasis.opendocument.presentation"}, + {".ods", "application/oleobject"}, + {".odt", "application/vnd.oasis.opendocument.text"}, + {".one", "application/onenote"}, + {".onea", "application/onenote"}, + {".onepkg", "application/onenote"}, + {".onetmp", "application/onenote"}, + {".onetoc", "application/onenote"}, + {".onetoc2", "application/onenote"}, + {".orderedtest", "application/xml"}, + {".osdx", "application/opensearchdescription+xml"}, + {".p10", "application/pkcs10"}, + {".p12", "application/x-pkcs12"}, + {".p7b", "application/x-pkcs7-certificates"}, + {".p7c", "application/pkcs7-mime"}, + {".p7m", "application/pkcs7-mime"}, + {".p7r", "application/x-pkcs7-certreqresp"}, + {".p7s", "application/pkcs7-signature"}, + {".pbm", "image/x-portable-bitmap"}, + {".pcast", "application/x-podcast"}, + {".pct", "image/pict"}, + {".pcx", "application/octet-stream"}, + {".pcz", "application/octet-stream"}, + {".pdf", "application/pdf"}, + {".pfb", "application/octet-stream"}, + {".pfm", "application/octet-stream"}, + {".pfx", "application/x-pkcs12"}, + {".pgm", "image/x-portable-graymap"}, + {".pic", "image/pict"}, + {".pict", "image/pict"}, + {".pkgdef", "text/plain"}, + {".pkgundef", "text/plain"}, + {".pko", "application/vnd.ms-pki.pko"}, + {".pls", "audio/scpls"}, + {".pma", "application/x-perfmon"}, + {".pmc", "application/x-perfmon"}, + {".pml", "application/x-perfmon"}, + {".pmr", "application/x-perfmon"}, + {".pmw", "application/x-perfmon"}, + {".png", "image/png"}, + {".pnm", "image/x-portable-anymap"}, + {".pnt", "image/x-macpaint"}, + {".pntg", "image/x-macpaint"}, + {".pnz", "image/png"}, + {".pot", "application/vnd.ms-powerpoint"}, + {".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"}, + {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"}, + {".ppa", "application/vnd.ms-powerpoint"}, + {".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"}, + {".ppm", "image/x-portable-pixmap"}, + {".pps", "application/vnd.ms-powerpoint"}, + {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"}, + {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, + {".ppt", "application/vnd.ms-powerpoint"}, + {".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"}, + {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {".prf", "application/pics-rules"}, + {".prm", "application/octet-stream"}, + {".prx", "application/octet-stream"}, + {".ps", "application/postscript"}, + {".psc1", "application/PowerShell"}, + {".psd", "application/octet-stream"}, + {".psess", "application/xml"}, + {".psm", "application/octet-stream"}, + {".psp", "application/octet-stream"}, + {".pub", "application/x-mspublisher"}, + {".pwz", "application/vnd.ms-powerpoint"}, + {".qht", "text/x-html-insertion"}, + {".qhtm", "text/x-html-insertion"}, + {".qt", "video/quicktime"}, + {".qti", "image/x-quicktime"}, + {".qtif", "image/x-quicktime"}, + {".qtl", "application/x-quicktimeplayer"}, + {".qxd", "application/octet-stream"}, + {".ra", "audio/x-pn-realaudio"}, + {".ram", "audio/x-pn-realaudio"}, + {".rar", "application/octet-stream"}, + {".ras", "image/x-cmu-raster"}, + {".rat", "application/rat-file"}, + {".rc", "text/plain"}, + {".rc2", "text/plain"}, + {".rct", "text/plain"}, + {".rdlc", "application/xml"}, + {".resx", "application/xml"}, + {".rf", "image/vnd.rn-realflash"}, + {".rgb", "image/x-rgb"}, + {".rgs", "text/plain"}, + {".rm", "application/vnd.rn-realmedia"}, + {".rmi", "audio/mid"}, + {".rmp", "application/vnd.rn-rn_music_package"}, + {".roff", "application/x-troff"}, + {".rpm", "audio/x-pn-realaudio-plugin"}, + {".rqy", "text/x-ms-rqy"}, + {".rtf", "application/rtf"}, + {".rtx", "text/richtext"}, + {".ruleset", "application/xml"}, + {".s", "text/plain"}, + {".safariextz", "application/x-safari-safariextz"}, + {".scd", "application/x-msschedule"}, + {".sct", "text/scriptlet"}, + {".sd2", "audio/x-sd2"}, + {".sdp", "application/sdp"}, + {".sea", "application/octet-stream"}, + {".searchConnector-ms", "application/windows-search-connector+xml"}, + {".setpay", "application/set-payment-initiation"}, + {".setreg", "application/set-registration-initiation"}, + {".settings", "application/xml"}, + {".sgimb", "application/x-sgimb"}, + {".sgml", "text/sgml"}, + {".sh", "application/x-sh"}, + {".shar", "application/x-shar"}, + {".shtml", "text/html"}, + {".sit", "application/x-stuffit"}, + {".sitemap", "application/xml"}, + {".skin", "application/xml"}, + {".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"}, + {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"}, + {".slk", "application/vnd.ms-excel"}, + {".sln", "text/plain"}, + {".slupkg-ms", "application/x-ms-license"}, + {".smd", "audio/x-smd"}, + {".smi", "application/octet-stream"}, + {".smx", "audio/x-smd"}, + {".smz", "audio/x-smd"}, + {".snd", "audio/basic"}, + {".snippet", "application/xml"}, + {".snp", "application/octet-stream"}, + {".sol", "text/plain"}, + {".sor", "text/plain"}, + {".spc", "application/x-pkcs7-certificates"}, + {".spl", "application/futuresplash"}, + {".src", "application/x-wais-source"}, + {".srf", "text/plain"}, + {".SSISDeploymentManifest", "text/xml"}, + {".ssm", "application/streamingmedia"}, + {".sst", "application/vnd.ms-pki.certstore"}, + {".stl", "application/vnd.ms-pki.stl"}, + {".sv4cpio", "application/x-sv4cpio"}, + {".sv4crc", "application/x-sv4crc"}, + {".svc", "application/xml"}, + {".swf", "application/x-shockwave-flash"}, + {".t", "application/x-troff"}, + {".tar", "application/x-tar"}, + {".tcl", "application/x-tcl"}, + {".testrunconfig", "application/xml"}, + {".testsettings", "application/xml"}, + {".tex", "application/x-tex"}, + {".texi", "application/x-texinfo"}, + {".texinfo", "application/x-texinfo"}, + {".tgz", "application/x-compressed"}, + {".thmx", "application/vnd.ms-officetheme"}, + {".thn", "application/octet-stream"}, + {".tif", "image/tiff"}, + {".tiff", "image/tiff"}, + {".tlh", "text/plain"}, + {".tli", "text/plain"}, + {".toc", "application/octet-stream"}, + {".tr", "application/x-troff"}, + {".trm", "application/x-msterminal"}, + {".trx", "application/xml"}, + {".ts", "video/vnd.dlna.mpeg-tts"}, + {".tsv", "text/tab-separated-values"}, + {".ttf", "application/octet-stream"}, + {".tts", "video/vnd.dlna.mpeg-tts"}, + {".txt", "text/plain"}, + {".u32", "application/octet-stream"}, + {".uls", "text/iuls"}, + {".user", "text/plain"}, + {".ustar", "application/x-ustar"}, + {".vb", "text/plain"}, + {".vbdproj", "text/plain"}, + {".vbk", "video/mpeg"}, + {".vbproj", "text/plain"}, + {".vbs", "text/vbscript"}, + {".vcf", "text/x-vcard"}, + {".vcproj", "Application/xml"}, + {".vcs", "text/plain"}, + {".vcxproj", "Application/xml"}, + {".vddproj", "text/plain"}, + {".vdp", "text/plain"}, + {".vdproj", "text/plain"}, + {".vdx", "application/vnd.ms-visio.viewer"}, + {".vml", "text/xml"}, + {".vscontent", "application/xml"}, + {".vsct", "text/xml"}, + {".vsd", "application/vnd.visio"}, + {".vsi", "application/ms-vsi"}, + {".vsix", "application/vsix"}, + {".vsixlangpack", "text/xml"}, + {".vsixmanifest", "text/xml"}, + {".vsmdi", "application/xml"}, + {".vspscc", "text/plain"}, + {".vss", "application/vnd.visio"}, + {".vsscc", "text/plain"}, + {".vssettings", "text/xml"}, + {".vssscc", "text/plain"}, + {".vst", "application/vnd.visio"}, + {".vstemplate", "text/xml"}, + {".vsto", "application/x-ms-vsto"}, + {".vsw", "application/vnd.visio"}, + {".vsx", "application/vnd.visio"}, + {".vtx", "application/vnd.visio"}, + {".wav", "audio/wav"}, + {".wave", "audio/wav"}, + {".wax", "audio/x-ms-wax"}, + {".wbk", "application/msword"}, + {".wbmp", "image/vnd.wap.wbmp"}, + {".wcm", "application/vnd.ms-works"}, + {".wdb", "application/vnd.ms-works"}, + {".wdp", "image/vnd.ms-photo"}, + {".webarchive", "application/x-safari-webarchive"}, + {".webtest", "application/xml"}, + {".wiq", "application/xml"}, + {".wiz", "application/msword"}, + {".wks", "application/vnd.ms-works"}, + {".WLMP", "application/wlmoviemaker"}, + {".wlpginstall", "application/x-wlpg-detect"}, + {".wlpginstall3", "application/x-wlpg3-detect"}, + {".wm", "video/x-ms-wm"}, + {".wma", "audio/x-ms-wma"}, + {".wmd", "application/x-ms-wmd"}, + {".wmf", "application/x-msmetafile"}, + {".wml", "text/vnd.wap.wml"}, + {".wmlc", "application/vnd.wap.wmlc"}, + {".wmls", "text/vnd.wap.wmlscript"}, + {".wmlsc", "application/vnd.wap.wmlscriptc"}, + {".wmp", "video/x-ms-wmp"}, + {".wmv", "video/x-ms-wmv"}, + {".wmx", "video/x-ms-wmx"}, + {".wmz", "application/x-ms-wmz"}, + {".wpl", "application/vnd.ms-wpl"}, + {".wps", "application/vnd.ms-works"}, + {".wri", "application/x-mswrite"}, + {".wrl", "x-world/x-vrml"}, + {".wrz", "x-world/x-vrml"}, + {".wsc", "text/scriptlet"}, + {".wsdl", "text/xml"}, + {".wvx", "video/x-ms-wvx"}, + {".x", "application/directx"}, + {".xaf", "x-world/x-vrml"}, + {".xaml", "application/xaml+xml"}, + {".xap", "application/x-silverlight-app"}, + {".xbap", "application/x-ms-xbap"}, + {".xbm", "image/x-xbitmap"}, + {".xdr", "text/plain"}, + {".xht", "application/xhtml+xml"}, + {".xhtml", "application/xhtml+xml"}, + {".xla", "application/vnd.ms-excel"}, + {".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"}, + {".xlc", "application/vnd.ms-excel"}, + {".xld", "application/vnd.ms-excel"}, + {".xlk", "application/vnd.ms-excel"}, + {".xll", "application/vnd.ms-excel"}, + {".xlm", "application/vnd.ms-excel"}, + {".xls", "application/vnd.ms-excel"}, + {".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"}, + {".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"}, + {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {".xlt", "application/vnd.ms-excel"}, + {".xltm", "application/vnd.ms-excel.template.macroEnabled.12"}, + {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, + {".xlw", "application/vnd.ms-excel"}, + {".xml", "text/xml"}, + {".xmta", "application/xml"}, + {".xof", "x-world/x-vrml"}, + {".XOML", "text/plain"}, + {".xpm", "image/x-xpixmap"}, + {".xps", "application/vnd.ms-xpsdocument"}, + {".xrm-ms", "text/xml"}, + {".xsc", "application/xml"}, + {".xsd", "text/xml"}, + {".xsf", "text/xml"}, + {".xsl", "text/xml"}, + {".xslt", "text/xml"}, + {".xsn", "application/octet-stream"}, + {".xss", "application/xml"}, + {".xtp", "application/octet-stream"}, + {".xwd", "image/x-xwindowdump"}, + {".z", "application/x-compress"}, + {".zip", "application/x-zip-compressed"}, + #endregion + + }; + + public static string GetMimeTypeFromExtension(string extension) + { + if (extension == null) + { + throw new ArgumentNullException("extension"); + } + + if (!extension.StartsWith(".")) + { + extension = "." + extension; + } + + string mime; + + return _mappings.TryGetValue(extension, out mime) ? mime : "application/octet-stream"; + } + + public static string GetMimeType(this string path) + { + return GetMimeTypeFromExtension(System.IO.Path.GetExtension(path)); + } } } \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Models/InputFile.cs.twig b/templates/dotnet/src/Appwrite/Models/InputFile.cs.twig new file mode 100644 index 000000000..5cc5c854f --- /dev/null +++ b/templates/dotnet/src/Appwrite/Models/InputFile.cs.twig @@ -0,0 +1,42 @@ +using System; +using System.IO; +using System.Web; + +namespace {{ spec.title | caseUcfirst }}.Models +{ + public class InputFile + { + public string Path { get; set; } + public string Filename { get; set; } + public string MimeType { get; set; } + public string SourceType { get; set; } + public object Data { get; set; } + + public static InputFile FromPath(string path) => new InputFile + { + Path = path, + Filename = System.IO.Path.GetFileName(path), + MimeType = path.GetMimeType(), + SourceType = "path" + }; + + public static InputFile FromFileInfo(FileInfo fileInfo) => + InputFile.FromPath(fileInfo.FullName); + + public static InputFile FromStream(Stream stream, string filename, string mimeType) => new InputFile + { + Data = stream, + Filename = filename, + MimeType = mimeType, + SourceType = "stream" + }; + + public static InputFile FromBytes(byte[] bytes, string filename, string mimeType) => new InputFile + { + Data = bytes, + Filename = filename, + MimeType = mimeType, + SourceType = "bytes" + }; + } +} \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 989198d9b..488ab536a 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -3,11 +3,10 @@ {{ parameter.type | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} {% endmacro %} {% macro method_parameters(parameters, consumes) %} -{% if parameters.all|length > 0 %}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} +{% if parameters.all|length > 0 %}{% for parameter in parameters.all | filter((param) => not param.isGlobal) %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} {% endmacro %} {% macro map_parameter(parameter) %} -{% if parameter.name == 'orderType' %}{{ parameter.name | caseCamel ~ '.ToString()'}}{% else %} -{{ parameter.name | caseCamel | escapeKeyword }}{% endif %} +{% if parameter.name == 'orderType' %}{{ parameter.name | caseCamel ~ '.ToString()'}}{% elseif parameter.isGlobal %}{{ parameter.name | caseUcfirst | escapeKeyword }}{% else %}{{ parameter.name | caseCamel | escapeKeyword }}{% endif %} {% endmacro %} {% macro methodNeedsSecurityParameters(method) %} {% if (method.type == "webAuth" or method.type == "location") and method.security|length > 0 %}{{ true }}{% else %}{{false}}{% endif %} @@ -22,12 +21,28 @@ using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using {{ spec.title | caseUcfirst }}.Models; namespace {{ spec.title | caseUcfirst }} { public class {{ service.name | caseUcfirst }} : Service { + +{% if service.globalParams | length %} +{% for parameter in service.globalParams %} + public {{ parameter.type | typeName | overrideIdentifier }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseUcfirst | escapeKeyword }} { get; set; } + +{% endfor %} + + public {{ service.name | caseUcfirst }}(Client client,{% for parameter in service.globalParams %} {{ parameter.type | typeName | overrideIdentifier }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}) : base(client) + { +{% for parameter in service.globalParams %} + {{ parameter.name | caseUcfirst | escapeKeyword }} = {{ parameter.name | caseCamel | escapeKeyword }}; +{% endfor %} + } +{% else %} public {{ service.name | caseUcfirst }}(Client client) : base(client) { } +{% endif %} {% for method in service.methods %} {% if method.title %} diff --git a/tests/DotNet31Test.php b/tests/DotNet31Test.php index e3306737a..963c65c78 100644 --- a/tests/DotNet31Test.php +++ b/tests/DotNet31Test.php @@ -19,6 +19,8 @@ class DotNet31Test extends Base ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, ]; } diff --git a/tests/DotNet50Test.php b/tests/DotNet50Test.php index 1be527c59..9c59359fd 100644 --- a/tests/DotNet50Test.php +++ b/tests/DotNet50Test.php @@ -19,6 +19,8 @@ class DotNet50Test extends Base ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, ]; } diff --git a/tests/DotNet60Test.php b/tests/DotNet60Test.php index 29c944c4e..a6d464534 100644 --- a/tests/DotNet60Test.php +++ b/tests/DotNet60Test.php @@ -19,6 +19,8 @@ class DotNet60Test extends Base ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, + ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, ]; } diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index ad5ad580d..e8d7a416b 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -21,25 +21,25 @@ public void Setup() public async Task Test1() { var client = new Client(); - var foo = new Foo(client); + var foo = new Foo(client, "string"); var bar = new Bar(client); var general = new General(client); Mock mock; // Foo Tests - mock = await foo.Get("string", 123, new List() { "string in array" }); + mock = await foo.Get(123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Post("string", 123, new List() { "string in array" }); + mock = await foo.Post(123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Put("string", 123, new List() { "string in array" }); + mock = await foo.Put(123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Patch("string", 123, new List() { "string in array" }); + mock = await foo.Patch(123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Delete("string", 123, new List() { "string in array" }); + mock = await foo.Delete(123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); // Bar Tests @@ -62,10 +62,18 @@ public async Task Test1() var result = await general.Redirect(); TestContext.WriteLine((result as Dictionary)["result"]); - mock = await general.Upload("string", 123, new List() { "string in array" }, new FileInfo("../../../../../../../resources/file.png")); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromPath("../../../../../../../resources/file.png")); TestContext.WriteLine(mock.Result); - mock = await general.Upload("string", 123, new List() { "string in array" }, new FileInfo("../../../../../../../resources/large_file.mp4")); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromPath("../../../../../../../resources/large_file.mp4")); + TestContext.WriteLine(mock.Result); + + var info = new FileInfo("../../../../../../../resources/file.png"); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromStream(info.OpenRead(), "file.png", "image/png")); + TestContext.WriteLine(mock.Result); + + info = new FileInfo("../../../../../../../resources/large_file.mp4"); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromStream(info.OpenRead(), "large_file.mp4", "video/mp4")); TestContext.WriteLine(mock.Result); try From 44b75f64e72f68afc4b1a55f00452299c46541ed Mon Sep 17 00:00:00 2001 From: Everly Precia Suresh Date: Fri, 5 Aug 2022 16:13:36 +0000 Subject: [PATCH 46/62] refactor .NET templates --- templates/dotnet/base/params.twig | 26 ++++++ templates/dotnet/base/requests/api.twig | 11 +++ templates/dotnet/base/requests/file.twig | 18 ++++ templates/dotnet/base/requests/location.twig | 5 ++ templates/dotnet/base/utils.twig | 16 ++++ .../Appwrite/Services/ServiceTemplate.cs.twig | 85 ++----------------- 6 files changed, 84 insertions(+), 77 deletions(-) create mode 100644 templates/dotnet/base/params.twig create mode 100644 templates/dotnet/base/requests/api.twig create mode 100644 templates/dotnet/base/requests/file.twig create mode 100644 templates/dotnet/base/requests/location.twig create mode 100644 templates/dotnet/base/utils.twig diff --git a/templates/dotnet/base/params.twig b/templates/dotnet/base/params.twig new file mode 100644 index 000000000..9ac7575a3 --- /dev/null +++ b/templates/dotnet/base/params.twig @@ -0,0 +1,26 @@ +{% import 'dotnet/base/utils.twig' as utils %} +{% for parameter in method.parameters.path %} + .Replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel | escapeKeyword }}){% if loop.last %};{% endif %} + +{% endfor %} + + var parameters = new Dictionary() + { +{% for parameter in method.parameters.query | merge(method.parameters.body) %} + { "{{ parameter.name }}", {{ utils.map_parameter(parameter) }} }{% if not loop.last or utils.methodNeedsSecurityParameters(method) %},{% endif %} + +{% endfor %} +{% if utils.methodNeedsSecurityParameters(method) %} +{% for node in method.security %} +{% for key,header in node|keys %} + { "{{header|caseLower}}", _client.Config["{{header|caseLower}}"] }{% if not loop.last or (loop.last and method.headers | length > 0) %},{% endif %} + +{% endfor %} +{% endfor %} +{% endif %} + }; + + var headers = new Dictionary() + { +{{ method.headers|map((header, key) => " { \"#{key}\", \"#{header}\" }")|join(',\n')|raw }} + }; diff --git a/templates/dotnet/base/requests/api.twig b/templates/dotnet/base/requests/api.twig new file mode 100644 index 000000000..3a4e103d3 --- /dev/null +++ b/templates/dotnet/base/requests/api.twig @@ -0,0 +1,11 @@ +{% import 'dotnet/base/utils.twig' as utils %} + return _client.Call{% if method.type != 'webAuth' %}<{{ utils.resultType(spec.title, method) }}>{% endif %}( + method: "{{ method.method | caseUpper }}", + path: path, + headers: headers, +{% if not method.responseModel %} + parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); +{% else %} + parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, + convert: Convert); +{% endif %} \ No newline at end of file diff --git a/templates/dotnet/base/requests/file.twig b/templates/dotnet/base/requests/file.twig new file mode 100644 index 000000000..de1f961ff --- /dev/null +++ b/templates/dotnet/base/requests/file.twig @@ -0,0 +1,18 @@ + string? idParamName = {% if method.parameters.all | filter(p => p.isUploadID) | length > 0 %}{% for parameter in method.parameters.all | filter(parameter => parameter.isUploadID) %}"{{ parameter.name }}"{% endfor %}{% else %}null{% endif %}; + +{% for parameter in method.parameters.all %} +{% if parameter.type == 'file' %} + var paramName = "{{ parameter.name }}"; +{% endif %} +{% endfor %} + + return _client.ChunkedUpload( + path, + headers, + parameters, +{% if method.responseModel %} + Convert, +{% endif %} + paramName, + idParamName, + onProgress); \ No newline at end of file diff --git a/templates/dotnet/base/requests/location.twig b/templates/dotnet/base/requests/location.twig new file mode 100644 index 000000000..6b6b1301e --- /dev/null +++ b/templates/dotnet/base/requests/location.twig @@ -0,0 +1,5 @@ + return _client.Call( + method: "{{ method.method | caseUpper }}", + path: path, + headers: headers, + parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); \ No newline at end of file diff --git a/templates/dotnet/base/utils.twig b/templates/dotnet/base/utils.twig new file mode 100644 index 000000000..04c029feb --- /dev/null +++ b/templates/dotnet/base/utils.twig @@ -0,0 +1,16 @@ +{% macro parameter(parameter) %} +{% if parameter.name == 'orderType' %}{{ 'OrderType orderType = OrderType.ASC' }}{% else %} +{{ parameter.type | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} +{% endmacro %} +{% macro method_parameters(parameters, consumes) %} +{% if parameters.all|length > 0 %}{% for parameter in parameters.all | filter((param) => not param.isGlobal) %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} +{% endmacro %} +{% macro map_parameter(parameter) %} +{% if parameter.name == 'orderType' %}{{ parameter.name | caseCamel ~ '.ToString()'}}{% elseif parameter.isGlobal %}{{ parameter.name | caseUcfirst | escapeKeyword }}{% else %}{{ parameter.name | caseCamel | escapeKeyword }}{% endif %} +{% endmacro %} +{% macro methodNeedsSecurityParameters(method) %} +{% if (method.type == "webAuth" or method.type == "location") and method.security|length > 0 %}{{ true }}{% else %}{{false}}{% endif %} +{% endmacro %} +{% macro resultType(namespace, method) %} +{% if method.type == "webAuth" %}bool{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}Models.{{method.responseModel | caseUcfirst | overrideIdentifier }}{% endif %} +{% endmacro %} \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 488ab536a..b76a0d8b2 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -1,19 +1,4 @@ -{% macro parameter(parameter) %} -{% if parameter.name == 'orderType' %}{{ 'OrderType orderType = OrderType.ASC' }}{% else %} -{{ parameter.type | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} -{% endmacro %} -{% macro method_parameters(parameters, consumes) %} -{% if parameters.all|length > 0 %}{% for parameter in parameters.all | filter((param) => not param.isGlobal) %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} -{% endmacro %} -{% macro map_parameter(parameter) %} -{% if parameter.name == 'orderType' %}{{ parameter.name | caseCamel ~ '.ToString()'}}{% elseif parameter.isGlobal %}{{ parameter.name | caseUcfirst | escapeKeyword }}{% else %}{{ parameter.name | caseCamel | escapeKeyword }}{% endif %} -{% endmacro %} -{% macro methodNeedsSecurityParameters(method) %} -{% if (method.type == "webAuth" or method.type == "location") and method.security|length > 0 %}{{ true }}{% else %}{{false}}{% endif %} -{% endmacro %} -{% macro resultType(namespace, method) %} -{% if method.type == "webAuth" %}bool{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}Models.{{method.responseModel | caseUcfirst | overrideIdentifier }}{% endif %} -{% endmacro %} +{% import 'dotnet/base/utils.twig' as utils %} using System; using System.Collections.Generic; @@ -55,84 +40,30 @@ namespace {{ spec.title | caseUcfirst }} /// {% endif %} /// - public Task{% if method.type != "webAuth" %}<{{ _self.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters, method.consumes) }}) + public Task{% if method.type != "webAuth" %}<{{ utils.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ utils.method_parameters(method.parameters, method.consumes) }}) { var path = "{{ method.path }}"{% if method.parameters.path | length == 0 %};{% endif %} -{% for parameter in method.parameters.path %} - .Replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel | escapeKeyword }}){% if loop.last %};{% endif %} - -{% endfor %} - - var parameters = new Dictionary() - { -{% for parameter in method.parameters.query | merge(method.parameters.body) %} - { "{{ parameter.name }}", {{ _self.map_parameter(parameter) }} }{% if not loop.last or _self.methodNeedsSecurityParameters(method) %},{% endif %} - -{% endfor %} -{% if _self.methodNeedsSecurityParameters(method) %} -{% for node in method.security %} -{% for key,header in node|keys %} - { "{{header|caseLower}}", _client.Config["{{header|caseLower}}"] }{% if not loop.last or (loop.last and method.headers | length > 0) %},{% endif %} - -{% endfor %} -{% endfor %} -{% endif %} - }; - - var headers = new Dictionary() - { -{{ method.headers|map((header, key) => " { \"#{key}\", \"#{header}\" }")|join(',\n')|raw }} - }; +{{include('dotnet/base/params.twig')}} {% if method.type == 'location' %} - return _client.Call( - method: "{{ method.method | caseUpper }}", - path: path, - headers: headers, - parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); +{{include('dotnet/base/requests/location.twig')}} {% else %} {% if method.responseModel %} - static {{ _self.resultType(spec.title, method) }} Convert(Dictionary it) => + static {{ utils.resultType(spec.title, method) }} Convert(Dictionary it) => {% if method.responseModel == 'any' %} it; {% else %} - {{ _self.resultType(spec.title, method) }}.From(map: it); + {{ utils.resultType(spec.title, method) }}.From(map: it); {% endif %} {% endif %} {% if 'multipart/form-data' in method.consumes %} - string? idParamName = {% if method.parameters.all | filter(p => p.isUploadID) | length > 0 %}{% for parameter in method.parameters.all | filter(parameter => parameter.isUploadID) %}"{{ parameter.name }}"{% endfor %}{% else %}null{% endif %}; - -{% for parameter in method.parameters.all %} -{% if parameter.type == 'file' %} - var paramName = "{{ parameter.name }}"; -{% endif %} -{% endfor %} - - return _client.ChunkedUpload( - path, - headers, - parameters, -{% if method.responseModel %} - Convert, -{% endif %} - paramName, - idParamName, - onProgress); +{{include('dotnet/base/requests/file.twig')}} {% else %} - return _client.Call{% if method.type != 'webAuth' %}<{{ _self.resultType(spec.title, method) }}>{% endif %}( - method: "{{ method.method | caseUpper }}", - path: path, - headers: headers, -{% if not method.responseModel %} - parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); -{% else %} - parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, - convert: Convert); -{% endif %} +{{include('dotnet/base/requests/api.twig')}} {% endif %} {% endif %} } From 58068ee3b4790c7ed62d240e1c5389cb5960a06f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 30 Sep 2022 00:32:44 +1300 Subject: [PATCH 47/62] Update for Appwrite 1.0 --- src/SDK/Language/DotNet.php | 54 ++++++--- templates/dotnet/src/Appwrite/Client.cs.twig | 15 +-- .../Appwrite/{Models => }/Exception.cs.twig | 2 +- .../Extensions.cs.twig} | 40 +++---- templates/dotnet/src/Appwrite/ID.cs.twig | 15 +++ .../src/Appwrite/Models/InputFile.cs.twig | 3 +- .../dotnet/src/Appwrite/Models/Model.cs.twig | 68 +++++------ .../Appwrite/Models/UploadProgress.cs.twig | 19 ++- .../dotnet/src/Appwrite/Permission.cs.twig | 30 +++++ templates/dotnet/src/Appwrite/Query.cs.twig | 87 ++++++++++++++ templates/dotnet/src/Appwrite/Role.cs.twig | 39 +++++++ .../Appwrite/Services/ServiceTemplate.cs.twig | 110 +++++++----------- 12 files changed, 323 insertions(+), 159 deletions(-) rename templates/dotnet/src/Appwrite/{Models => }/Exception.cs.twig (93%) rename templates/dotnet/src/Appwrite/{Helpers/ExtensionMethods.cs => Extensions/Extensions.cs.twig} (97%) create mode 100644 templates/dotnet/src/Appwrite/ID.cs.twig create mode 100644 templates/dotnet/src/Appwrite/Permission.cs.twig create mode 100644 templates/dotnet/src/Appwrite/Query.cs.twig create mode 100644 templates/dotnet/src/Appwrite/Role.cs.twig diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 90c4d1651..039e757a9 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -145,7 +145,7 @@ public function getIdentifierOverrides() } /** - * @param $type + * @param array $parameter * @return string */ public function getTypeName(array $parameter): string @@ -288,14 +288,19 @@ public function getFiles() return [ [ 'scope' => 'default', - 'destination' => 'README.md', - 'template' => 'dotnet/README.md.twig', + 'destination' => '.travis.yml', + 'template' => 'dotnet/.travis.yml.twig', ], [ 'scope' => 'default', 'destination' => 'CHANGELOG.md', 'template' => 'dotnet/CHANGELOG.md.twig', ], + [ + 'scope' => 'copy', + 'destination' => '/icon.png', + 'template' => 'dotnet/icon.png', + ], [ 'scope' => 'default', 'destination' => 'LICENSE', @@ -303,8 +308,8 @@ public function getFiles() ], [ 'scope' => 'default', - 'destination' => '.travis.yml', - 'template' => 'dotnet/.travis.yml.twig', + 'destination' => 'README.md', + 'template' => 'dotnet/README.md.twig', ], [ 'scope' => 'method', @@ -316,11 +321,6 @@ public function getFiles() 'destination' => '/src/{{ spec.title | caseUcfirst }}.sln', 'template' => 'dotnet/src/Appwrite.sln', ], - [ - 'scope' => 'copy', - 'destination' => '/icon.png', - 'template' => 'dotnet/icon.png', - ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/{{ spec.title | caseUcfirst }}.csproj', @@ -333,8 +333,33 @@ public function getFiles() ], [ 'scope' => 'default', - 'destination' => '/src/{{ spec.title | caseUcfirst }}/Helpers/ExtensionMethods.cs', - 'template' => 'dotnet/src/Appwrite/Helpers/ExtensionMethods.cs', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/{{ spec.title | caseUcfirst }}Exception.cs', + 'template' => 'dotnet/src/Appwrite/Exception.cs.twig', + ], + [ + 'scope' => 'default', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/ID.cs', + 'template' => 'dotnet/src/Appwrite/ID.cs.twig', + ], + [ + 'scope' => 'default', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Permission.cs', + 'template' => 'dotnet/src/Appwrite/Permission.cs.twig', + ], + [ + 'scope' => 'default', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Query.cs', + 'template' => 'dotnet/src/Appwrite/Query.cs.twig', + ], + [ + 'scope' => 'default', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Role.cs', + 'template' => 'dotnet/src/Appwrite/Role.cs.twig', + ], + [ + 'scope' => 'default', + 'destination' => '/src/{{ spec.title | caseUcfirst }}/Extensions/Extensions.cs', + 'template' => 'dotnet/src/Appwrite/Extensions/Extensions.cs.twig', ], [ 'scope' => 'default', @@ -346,11 +371,6 @@ public function getFiles() 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/UploadProgress.cs', 'template' => 'dotnet/src/Appwrite/Models/UploadProgress.cs.twig', ], - [ - 'scope' => 'default', - 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/Exception.cs', - 'template' => 'dotnet/src/Appwrite/Models/Exception.cs.twig', - ], [ 'scope' => 'default', 'destination' => '/src/{{ spec.title | caseUcfirst }}/Models/InputFile.cs', diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index 9ed8d7899..d362fefec 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -6,11 +6,11 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; +using {{ spec.title | caseUcfirst }}.Extensions; using {{ spec.title | caseUcfirst }}.Models; namespace {{ spec.title | caseUcfirst }} @@ -60,11 +60,12 @@ namespace {{ spec.title | caseUcfirst }} { "x-sdk-language", "{{ language.name | caseLower }}" }, { "x-sdk-version", "{{ sdk.version }}"}{% if spec.global.defaultHeaders | length > 0 %},{% endif %} -{% for key,header in spec.global.defaultHeaders %} + {%~ for key,header in spec.global.defaultHeaders %} { "{{key}}", "{{header}}" }{% if not loop.last %},{% endif %} -{% endfor %} + {%~ endfor %} }; + _config = new Dictionary(); if (selfSigned) @@ -96,10 +97,10 @@ namespace {{ spec.title | caseUcfirst }} return this; } -{% for header in spec.global.headers %} -{% if header.description %} + {%~ for header in spec.global.headers %} + {%~ if header.description %} /// {{header.description}} -{% endif %} + {%~ endif %} public Client Set{{header.key | caseUcfirst}}(string value) { _config.Add("{{ header.key | caseCamel }}", value); AddHeader("{{header.name}}", value); @@ -107,7 +108,7 @@ namespace {{ spec.title | caseUcfirst }} return this; } -{% endfor %} + {%~ endfor %} public Client AddHeader(string key, string value) { _headers.Add(key, value); diff --git a/templates/dotnet/src/Appwrite/Models/Exception.cs.twig b/templates/dotnet/src/Appwrite/Exception.cs.twig similarity index 93% rename from templates/dotnet/src/Appwrite/Models/Exception.cs.twig rename to templates/dotnet/src/Appwrite/Exception.cs.twig index 86567f0c9..eb967f973 100644 --- a/templates/dotnet/src/Appwrite/Models/Exception.cs.twig +++ b/templates/dotnet/src/Appwrite/Exception.cs.twig @@ -1,6 +1,6 @@ using System; -namespace Appwrite +namespace {{spec.title | caseUcfirst}} { public class {{spec.title | caseUcfirst}}Exception : Exception { diff --git a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs b/templates/dotnet/src/Appwrite/Extensions/Extensions.cs.twig similarity index 97% rename from templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs rename to templates/dotnet/src/Appwrite/Extensions/Extensions.cs.twig index 5ead38429..ff19ae70c 100644 --- a/templates/dotnet/src/Appwrite/Helpers/ExtensionMethods.cs +++ b/templates/dotnet/src/Appwrite/Extensions/Extensions.cs.twig @@ -1,13 +1,11 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; using System; -using System.IO; using System.Collections.Generic; +using System.Linq; -namespace {{ spec.title | caseUcfirst }} +namespace {{ spec.title | caseUcfirst }}.Extensions { - public static class ExtensionMethods + public static class Extensions { public static string ToJson(this Dictionary dict) { @@ -20,20 +18,18 @@ public static string ToQueryString(this Dictionary parameters) foreach (var kvp in parameters) { - if (kvp.Value == null) + switch (kvp.Value) { - continue; - } - if (kvp.Value is List list) - { - foreach(object entry in list) - { - query.Add($"{kvp.Key}[]={Uri.EscapeUriString(entry.ToString()!)}"); - } - } - else - { - query.Add($"{kvp.Key}={Uri.EscapeUriString(kvp.Value.ToString()!)}"); + case null: + continue; + case List list: + query.AddRange(list.Select(entry => + $"{kvp.Key}[]={Uri.EscapeUriString(entry.ToString())}" + )); + break; + default: + query.Add($"{kvp.Key}={Uri.EscapeUriString(kvp.Value.ToString())}"); + break; } } return string.Join("&", query); @@ -41,7 +37,7 @@ public static string ToQueryString(this Dictionary parameters) private static IDictionary _mappings = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { - #region Big freaking list of mime type + #region Mime Types {".323", "text/h323"}, {".3g2", "video/3gpp2"}, {".3gp", "video/3gpp"}, @@ -604,7 +600,7 @@ public static string ToQueryString(this Dictionary parameters) {".zip", "application/x-zip-compressed"}, #endregion - }; + }; public static string GetMimeTypeFromExtension(string extension) { @@ -618,9 +614,7 @@ public static string GetMimeTypeFromExtension(string extension) extension = "." + extension; } - string mime; - - return _mappings.TryGetValue(extension, out mime) ? mime : "application/octet-stream"; + return _mappings.TryGetValue(extension, out var mime) ? mime : "application/octet-stream"; } public static string GetMimeType(this string path) diff --git a/templates/dotnet/src/Appwrite/ID.cs.twig b/templates/dotnet/src/Appwrite/ID.cs.twig new file mode 100644 index 000000000..c58310e42 --- /dev/null +++ b/templates/dotnet/src/Appwrite/ID.cs.twig @@ -0,0 +1,15 @@ +namespace {{ spec.title | caseUcfirst }} +{ + public static class ID + { + public static string Unique() + { + return "unique()"; + } + + public static string Custom(string id) + { + return id; + } + } +} \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Models/InputFile.cs.twig b/templates/dotnet/src/Appwrite/Models/InputFile.cs.twig index 5cc5c854f..5d98b2167 100644 --- a/templates/dotnet/src/Appwrite/Models/InputFile.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/InputFile.cs.twig @@ -1,6 +1,5 @@ -using System; using System.IO; -using System.Web; +using Appwrite.Extensions; namespace {{ spec.title | caseUcfirst }}.Models { diff --git a/templates/dotnet/src/Appwrite/Models/Model.cs.twig b/templates/dotnet/src/Appwrite/Models/Model.cs.twig index f19f4aeff..d30396036 100644 --- a/templates/dotnet/src/Appwrite/Models/Model.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/Model.cs.twig @@ -1,4 +1,4 @@ -{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst | overrideIdentifier}}>{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %} +{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst | overrideIdentifier}}>{% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}{% endif %}{% else %}{{property | typeName}}{% endif %}{% if not property.required %}?{% endif %}{% endmacro %} {% macro property_name(definition, property) %}{{ property.name | caseUcfirst | removeDollarSign | escapeKeyword}}{% endmacro %} using System; @@ -12,68 +12,68 @@ namespace {{ spec.title | caseUcfirst }}.Models { public class {{ definition.name | caseUcfirst | overrideIdentifier }} { -{% for property in definition.properties %} + {%~ for property in definition.properties %} [JsonProperty("{{ property.name }}")] - public {{ _self.sub_schema(property) }} {{ _self.property_name(definition, property) }} { get; set; } + public {{ _self.sub_schema(property) }} {{ _self.property_name(definition, property) }} { get; private set; } -{% endfor %} -{% if definition.additionalProperties %} - public Dictionary Data { get; set; } -{% endif %} + {%~ endfor %} + {%~ if definition.additionalProperties %} + public Dictionary Data { get; private set; } + {%~ endif %} public {{ definition.name | caseUcfirst | overrideIdentifier }}( -{% for property in definition.properties %} + {%~ for property in definition.properties %} {{ _self.sub_schema(property) }} {{ property.name | caseCamel | escapeKeyword }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} -{% endfor %} -{% if definition.additionalProperties %} + {%~ endfor %} + {%~ if definition.additionalProperties %} Dictionary data -{% endif %} + {%~ endif %} ) { -{% for property in definition.properties %} + {%~ for property in definition.properties %} {{ _self.property_name(definition, property) }} = {{ property.name | caseCamel | escapeKeyword }}; -{% endfor %} -{% if definition.additionalProperties %} + {%~ endfor %} + {%~ if definition.additionalProperties %} Data = data; -{% endif %} + {%~ endif %} } public static {{ definition.name | caseUcfirst | overrideIdentifier}} From(Dictionary map) => new {{ definition.name | caseUcfirst | overrideIdentifier}}( -{% for property in definition.properties %} - {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property.type | typeName }}>(){% else %}{% if property.type == "integer" or property.type == "number" %}{% if not property.required %}map["{{ property.name }}"] == null ? null : {% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]){% else %}({{ property.type | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"]{% endif %}{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} + {%~ for property in definition.properties %} + {{ property.name | caseCamel | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject>>().Select(it => {{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: it)).ToList(){% else %}{{property.sub_schema | caseUcfirst | overrideIdentifier}}.From(map: ((JObject)map["{{ property.name }}"]).ToObject>()!){% endif %}{% else %}{% if property.type == 'array' %}((JArray)map["{{ property.name }}"]).ToObject<{{ property | typeName }}>(){% else %}{% if property.type == "integer" or property.type == "number" %}{% if not property.required %}map["{{ property.name }}"] == null ? null : {% endif %}Convert.To{% if property.type == "integer" %}Int64{% else %}Double{% endif %}(map["{{ property.name }}"]){% else %}({{ property | typeName }}{% if not property.required %}?{% endif %})map["{{ property.name }}"]{% endif %}{% endif %}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} -{% endfor %} -{% if definition.additionalProperties %} + {%~ endfor %} + {%~ if definition.additionalProperties %} data: map -{% endif %} + {%~ endif %} ); public Dictionary ToMap() => new Dictionary() { -{% for property in definition.properties %} + {%~ for property in definition.properties %} { "{{ property.name }}", {% if property.sub_schema %}{% if property.type == 'array' %}{{ _self.property_name(definition, property) }}.Select(it => it.ToMap()){% else %}{{ _self.property_name(definition, property) }}.ToMap(){% endif %}{% else %}{{ _self.property_name(definition, property) }}{% endif %}{{ ' }' }}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %} -{% endfor %} -{% if definition.additionalProperties %} + {%~ endfor %} + {%~ if definition.additionalProperties %} { "data", Data } -{% endif %} + {%~ endif %} }; -{% if definition.additionalProperties %} + {%~ if definition.additionalProperties %} public T ConvertTo(Func, T> fromJson) => fromJson.Invoke(Data); -{% endif %} -{% for property in definition.properties %} -{% if property.sub_schema %} -{% for def in spec.definitions %} -{% if def.name == property.sub_schema and def.additionalProperties and property.type == 'array' %} + {%~ endif %} + {%~ for property in definition.properties %} + {%~ if property.sub_schema %} + {%~ for def in spec.definitions %} + {%~ if def.name == property.sub_schema and def.additionalProperties and property.type == 'array' %} public T ConvertTo(Func, T> fromJson) => (T){{ property.name | caseUcfirst | escapeKeyword }}.Select(it => it.ConvertTo(fromJson)); -{% endif %} -{% endfor %} -{% endif %} -{% endfor %} + {%~ endif %} + {%~ endfor %} + {%~ endif %} + {%~ endfor %} } } \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig b/templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig index bbb502ce8..47c78391c 100644 --- a/templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig +++ b/templates/dotnet/src/Appwrite/Models/UploadProgress.cs.twig @@ -2,7 +2,19 @@ namespace {{ spec.title | caseUcfirst }} { public class UploadProgress { - public UploadProgress(string id, double progress, long sizeUploaded, long chunksTotal, long chunksUploaded) + public string Id { get; private set; } + public double Progress { get; private set; } + public long SizeUploaded { get; private set; } + public long ChunksTotal { get; private set; } + public long ChunksUploaded { get; private set; } + + public UploadProgress( + string id, + double progress, + long sizeUploaded, + long chunksTotal, + long chunksUploaded + ) { Id = id; Progress = progress; @@ -10,10 +22,5 @@ namespace {{ spec.title | caseUcfirst }} ChunksTotal = chunksTotal; ChunksUploaded = chunksUploaded; } - public string Id { get; private set; } - public double Progress { get; private set; } - public long SizeUploaded { get; private set; } - public long ChunksTotal { get; private set; } - public long ChunksUploaded { get; private set; } } } \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Permission.cs.twig b/templates/dotnet/src/Appwrite/Permission.cs.twig new file mode 100644 index 000000000..5bde420f1 --- /dev/null +++ b/templates/dotnet/src/Appwrite/Permission.cs.twig @@ -0,0 +1,30 @@ +namespace {{ spec.title | caseUcfirst }} +{ + public static class Permission + { + public static string Read(string role) + { + return $"read(\"{role}\")"; + } + + public static string Write(string role) + { + return $"write(\"{role}\")"; + } + + public static string Create(string role) + { + return $"create(\"{role}\")"; + } + + public static string Update(string role) + { + return $"update(\"{role}\")"; + } + + public static string Delete(string role) + { + return $"delete(\"{role}\")"; + } + } +} diff --git a/templates/dotnet/src/Appwrite/Query.cs.twig b/templates/dotnet/src/Appwrite/Query.cs.twig new file mode 100644 index 000000000..91e59e74f --- /dev/null +++ b/templates/dotnet/src/Appwrite/Query.cs.twig @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Appwrite +{ + public static class Query + { + public static string Equal(string attribute, object value) + { + return AddQuery(attribute, "equal", value); + } + + public static string NotEqual(string attribute, object value) + { + return AddQuery(attribute, "notEqual", value); + } + + public static string LessThan(string attribute, object value) + { + return AddQuery(attribute, "lessThan", value); + } + + public static string LessThanEqual(string attribute, object value) + { + return AddQuery(attribute, "lessThanEqual", value); + } + + public static string GreaterThan(string attribute, object value) + { + return AddQuery(attribute, "greaterThan", value); + } + + public static string GreaterThanEqual(string attribute, object value) + { + return AddQuery(attribute, "greaterThanEqual", value); + } + + public static string Search(string attribute, string value) + { + return AddQuery(attribute, "search", value); + } + + public static string CursorAfter(string documentId) + { + return $"cursorAfter(\"{documentId}\")"; + } + + public static string CursorBefore(string documentId) { + return $"cursorBefore(\"{documentId}\")"; + } + + public static string OrderAsc(string attribute) { + return $"orderAsc(\"{attribute}\")"; + } + + public static string OrderDesc(string attribute) { + return $"orderDesc(\"{attribute}\")"; + } + + public static string Limit(int limit) { + return $"limit({limit})"; + } + + public static string Offset(int offset) { + return $"offset({offset})"; + } + + private static string AddQuery(string attribute, string method, object value) + { + if (value is List list) + { + return $"{method}(\"{attribute}\", [{string.Join(",", list.Select(ParseValues))}])"; + } + else + { + return $"{method}(\"{attribute}\", [{ParseValues(value)}])"; + } + } + + private static string ParseValues(object value) + { + return value is string + ? $"\"{value}\"" + : value.ToString(); + } + } +} \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Role.cs.twig b/templates/dotnet/src/Appwrite/Role.cs.twig new file mode 100644 index 000000000..b4bcb9ba7 --- /dev/null +++ b/templates/dotnet/src/Appwrite/Role.cs.twig @@ -0,0 +1,39 @@ +namespace Appwrite; + +public static class Role +{ + public static string Any() + { + return "any"; + } + + public static string User(string id, string status = "") + { + return status == string.Empty + ? $"user:{id}" + : $"user:{id}/{status}"; + } + + public static string Users(string status = "") + { + return status == string.Empty + ? "users" : + $"users/{status}"; + } + public static string Guests() + { + return "guests"; + } + + public static string Team(string id, string role = "") + { + return role == string.Empty + ? $"team:{id}" + : $"team:{id}/{role}"; + } + + public static string Member(string id) + { + return $"member:{id}"; + } +} \ No newline at end of file diff --git a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig index 5f57bf139..72b2658e3 100644 --- a/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig +++ b/templates/dotnet/src/Appwrite/Services/ServiceTemplate.cs.twig @@ -3,142 +3,114 @@ {{ parameter | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} {% endmacro %} {% macro method_parameters(parameters, consumes) %} -{% if parameters.all|length > 0 %}{% for parameter in parameters.all | filter((param) => not param.isGlobal) %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} +{% if parameters.all|length > 0 %}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}, {% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} {% endmacro %} {% macro map_parameter(parameter) %} -{% if parameter.name == 'orderType' %}{{ parameter.name | caseCamel ~ '.ToString()'}}{% elseif parameter.isGlobal %}{{ parameter.name | caseUcfirst | escapeKeyword }}{% else %}{{ parameter.name | caseCamel | escapeKeyword }}{% endif %} -{% endmacro %} -{% macro methodNeedsSecurityParameters(method) %} -{% if (method.type == "webAuth" or method.type == "location") and method.security|length > 0 %}{{ true }}{% else %}{{false}}{% endif %} +{% if parameter.name == 'orderType' %}{{ parameter.name | caseCamel ~ '.ToString()'}}{% else %}{{ parameter.name | caseCamel | escapeKeyword }}{% endif %} {% endmacro %} {% macro resultType(namespace, method) %} {% if method.type == "webAuth" %}bool{% elseif method.type == "location" %}byte[]{% elseif not method.responseModel or method.responseModel == 'any' %}object{% else %}Models.{{method.responseModel | caseUcfirst | overrideIdentifier }}{% endif %} {% endmacro %} - using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; using {{ spec.title | caseUcfirst }}.Models; -namespace {{ spec.title | caseUcfirst }} +namespace {{ spec.title | caseUcfirst }}.Services { public class {{ service.name | caseUcfirst }} : Service { - -{% if service.globalParams | length %} -{% for parameter in service.globalParams %} - public {{ parameter.type | typeName | overrideIdentifier }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseUcfirst | escapeKeyword }} { get; set; } - -{% endfor %} - - public {{ service.name | caseUcfirst }}(Client client,{% for parameter in service.globalParams %} {{ parameter.type | typeName | overrideIdentifier }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}) : base(client) + public {{ service.name | caseUcfirst }}(Client client) : base(client) { -{% for parameter in service.globalParams %} - {{ parameter.name | caseUcfirst | escapeKeyword }} = {{ parameter.name | caseCamel | escapeKeyword }}; -{% endfor %} } -{% else %} - public {{ service.name | caseUcfirst }}(Client client) : base(client) { } -{% endif %} -{% for method in service.methods %} -{% if method.title %} + {%~ for method in service.methods %} + {%~ if method.title %} /// /// {{ method.title }} -{% endif %} -{% if method.description %} + {%~ endif %} + {%~ if method.description %} /// -{{ method.description | dotnetComment }} + {{~ method.description | dotnetComment }} /// -{% endif %} + {%~ endif %} /// public Task{% if method.type != "webAuth" %}<{{ _self.resultType(spec.title, method) }}>{% endif %} {{ method.name | caseUcfirst }}({{ _self.method_parameters(method.parameters, method.consumes) }}) { var path = "{{ method.path }}"{% if method.parameters.path | length == 0 %};{% endif %} -{% for parameter in method.parameters.path %} + {%~ for parameter in method.parameters.path %} .Replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel | escapeKeyword }}){% if loop.last %};{% endif %} -{% endfor %} + {%~ endfor %} var parameters = new Dictionary() { -{% for parameter in method.parameters.query | merge(method.parameters.body) %} - { "{{ parameter.name }}", {{ _self.map_parameter(parameter) }} }{% if not loop.last or _self.methodNeedsSecurityParameters(method) %},{% endif %} + {%~ for parameter in method.parameters.query | merge(method.parameters.body) %} + { "{{ parameter.name }}", {{ _self.map_parameter(parameter) }} }{% if not loop.last %},{% endif %} -{% endfor %} -{% if _self.methodNeedsSecurityParameters(method) %} -{% for node in method.security %} -{% for key,header in node|keys %} - { "{{header|caseLower}}", _client.Config["{{header|caseLower}}"] }{% if not loop.last or (loop.last and method.headers | length > 0) %},{% endif %} - -{% endfor %} -{% endfor %} -{% endif %} + {%~ endfor %} }; var headers = new Dictionary() { -{{ method.headers|map((header, key) => " { \"#{key}\", \"#{header}\" }")|join(',\n')|raw }} - }; + {%~ for key, header in method.headers %} + { "{{ key }}", "{{ header }}" }{% if not loop.last %},{% endif %} -{% if method.type == 'location' %} + {%~ endfor %} + }; + {%~ if method.type == 'location' %} return _client.Call( method: "{{ method.method | caseUpper }}", path: path, headers: headers, - parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); -{% else %} + parameters: parameters.Where(p => p.Value != null).ToDictionary(p => p.Key, p => p.Value)!); + {%~ else %} -{% if method.responseModel %} + {%~ if method.responseModel %} static {{ _self.resultType(spec.title, method) }} Convert(Dictionary it) => -{% if method.responseModel == 'any' %} + {%~ if method.responseModel == 'any' %} it; -{% else %} + {%~ else %} {{ _self.resultType(spec.title, method) }}.From(map: it); -{% endif %} -{% endif %} - -{% if 'multipart/form-data' in method.consumes %} + {%~ endif %} + {%~ endif %} + {%~ if 'multipart/form-data' in method.consumes %} string? idParamName = {% if method.parameters.all | filter(p => p.isUploadID) | length > 0 %}{% for parameter in method.parameters.all | filter(parameter => parameter.isUploadID) %}"{{ parameter.name }}"{% endfor %}{% else %}null{% endif %}; -{% for parameter in method.parameters.all %} -{% if parameter.type == 'file' %} + {%~ for parameter in method.parameters.all %} + {%~ if parameter.type == 'file' %} var paramName = "{{ parameter.name }}"; -{% endif %} -{% endfor %} + {%~ endif %} + {%~ endfor %} return _client.ChunkedUpload( path, headers, parameters, -{% if method.responseModel %} + {%~ if method.responseModel %} Convert, -{% endif %} + {%~ endif %} paramName, idParamName, onProgress); -{% else %} + {%~ else %} return _client.Call{% if method.type != 'webAuth' %}<{{ _self.resultType(spec.title, method) }}>{% endif %}( method: "{{ method.method | caseUpper }}", path: path, headers: headers, -{% if not method.responseModel %} + {%~ if not method.responseModel %} parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!); -{% else %} + {%~ else %} parameters: parameters.Where(it => it.Value != null).ToDictionary(it => it.Key, it => it.Value)!, convert: Convert); -{% endif %} -{% endif %} -{% endif %} + {%~ endif %} + {%~ endif %} + {%~ endif %} } -{% if not loop.last %} -{% endif %} -{% endfor %} + {%~ endfor %} } } From 0b2a8468bef1df9acbbe182268c8c4475727add8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 30 Sep 2022 01:19:33 +1300 Subject: [PATCH 48/62] Update tests --- src/SDK/Language/DotNet.php | 8 ++- templates/dotnet/src/Appwrite/Client.cs.twig | 10 +-- tests/DotNet31Test.php | 8 +++ tests/DotNet50Test.php | 8 +++ tests/DotNet60Test.php | 8 +++ tests/languages/dotnet/Tests.cs | 66 ++++++++++++++------ 6 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 039e757a9..d67c19982 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -253,7 +253,13 @@ public function getParamExample(array $param) $output .= '[object]'; break; case self::TYPE_ARRAY: - $output .= '[List]'; + if (\str_starts_with($example, '[')) { + $example = \substr($example, 1); + } + if (\str_ends_with($example, ']')) { + $example = \substr($example, 0, -1); + } + $output .= 'new List<'. $this->getTypeName($param['array']) . '> {' . $example . '}'; break; } } diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index d362fefec..f7eb18ee5 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -27,11 +27,13 @@ namespace {{ spec.title | caseUcfirst }} private static readonly int ChunkSize = 5 * 1024 * 1024; - public static JsonSerializerSettings DeserializerSettings { get; set; } = new JsonSerializerSettings { + public static JsonSerializerSettings DeserializerSettings { get; set; } = new JsonSerializerSettings + { MetadataPropertyHandling = MetadataPropertyHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesContractResolver(), - Converters = new List { + Converters = new List + { new StringEnumConverter() } }; @@ -40,7 +42,8 @@ namespace {{ spec.title | caseUcfirst }} { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new CamelCasePropertyNamesContractResolver(), - Converters = new List { + Converters = new List + { new StringEnumConverter() } }; @@ -59,7 +62,6 @@ namespace {{ spec.title | caseUcfirst }} { "x-sdk-platform", "{{ sdk.platform }}" }, { "x-sdk-language", "{{ language.name | caseLower }}" }, { "x-sdk-version", "{{ sdk.version }}"}{% if spec.global.defaultHeaders | length > 0 %},{% endif %} - {%~ for key,header in spec.global.defaultHeaders %} { "{{key}}", "{{header}}" }{% if not loop.last %},{% endif %} {%~ endfor %} diff --git a/tests/DotNet31Test.php b/tests/DotNet31Test.php index 963c65c78..9d0b6733a 100644 --- a/tests/DotNet31Test.php +++ b/tests/DotNet31Test.php @@ -4,6 +4,11 @@ class DotNet31Test extends Base { + protected string $sdkName = 'dotnet'; + protected string $sdkPlatform = 'server'; + protected string $sdkLanguage = 'dotnet'; + protected string $version = '0.0.1'; + protected string $language = 'dotnet'; protected string $class = 'Appwrite\SDK\Language\DotNet'; protected array $build = [ @@ -22,5 +27,8 @@ class DotNet31Test extends Base ...Base::LARGE_FILE_RESPONSES, ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, + ...Base::QUERY_HELPER_RESPONSES, + ...Base::PERMISSION_HELPER_RESPONSES, + ...Base::ID_HELPER_RESPONSES ]; } diff --git a/tests/DotNet50Test.php b/tests/DotNet50Test.php index 9c59359fd..8877f1ed8 100644 --- a/tests/DotNet50Test.php +++ b/tests/DotNet50Test.php @@ -4,6 +4,11 @@ class DotNet50Test extends Base { + protected string $sdkName = 'dotnet'; + protected string $sdkPlatform = 'server'; + protected string $sdkLanguage = 'dotnet'; + protected string $version = '0.0.1'; + protected string $language = 'dotnet'; protected string $class = 'Appwrite\SDK\Language\DotNet'; protected array $build = [ @@ -22,5 +27,8 @@ class DotNet50Test extends Base ...Base::LARGE_FILE_RESPONSES, ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, + ...Base::QUERY_HELPER_RESPONSES, + ...Base::PERMISSION_HELPER_RESPONSES, + ...Base::ID_HELPER_RESPONSES ]; } diff --git a/tests/DotNet60Test.php b/tests/DotNet60Test.php index a6d464534..c6d117504 100644 --- a/tests/DotNet60Test.php +++ b/tests/DotNet60Test.php @@ -4,6 +4,11 @@ class DotNet60Test extends Base { + protected string $sdkName = 'dotnet'; + protected string $sdkPlatform = 'server'; + protected string $sdkLanguage = 'dotnet'; + protected string $version = '0.0.1'; + protected string $language = 'dotnet'; protected string $class = 'Appwrite\SDK\Language\DotNet'; protected array $build = [ @@ -22,5 +27,8 @@ class DotNet60Test extends Base ...Base::LARGE_FILE_RESPONSES, ...Base::LARGE_FILE_RESPONSES, ...Base::EXCEPTION_RESPONSES, + ...Base::QUERY_HELPER_RESPONSES, + ...Base::PERMISSION_HELPER_RESPONSES, + ...Base::ID_HELPER_RESPONSES ]; } diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index e8d7a416b..dd446bd08 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -5,6 +5,7 @@ using Appwrite; using Appwrite.Models; +using Appwrite.Services; using NUnit.Framework; namespace AppwriteTests @@ -21,59 +22,59 @@ public void Setup() public async Task Test1() { var client = new Client(); - var foo = new Foo(client, "string"); + var foo = new Foo(client); var bar = new Bar(client); var general = new General(client); Mock mock; // Foo Tests - mock = await foo.Get(123, new List() { "string in array" }); + mock = await foo.Get("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Post(123, new List() { "string in array" }); + mock = await foo.Post("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Put(123, new List() { "string in array" }); + mock = await foo.Put("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Patch(123, new List() { "string in array" }); + mock = await foo.Patch("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await foo.Delete(123, new List() { "string in array" }); + mock = await foo.Delete("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); // Bar Tests - mock = await bar.Get("string", 123, new List() { "string in array" }); + mock = await bar.Get("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Post("string", 123, new List() { "string in array" }); + mock = await bar.Post("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Put("string", 123, new List() { "string in array" }); + mock = await bar.Put("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Patch("string", 123, new List() { "string in array" }); + mock = await bar.Patch("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); - mock = await bar.Delete("string", 123, new List() { "string in array" }); + mock = await bar.Delete("string", 123, new List() { "string in array" }); TestContext.WriteLine(mock.Result); // General Tests var result = await general.Redirect(); TestContext.WriteLine((result as Dictionary)["result"]); - mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromPath("../../../../../../../resources/file.png")); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromPath("../../../../../../../resources/file.png")); TestContext.WriteLine(mock.Result); - mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromPath("../../../../../../../resources/large_file.mp4")); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromPath("../../../../../../../resources/large_file.mp4")); TestContext.WriteLine(mock.Result); var info = new FileInfo("../../../../../../../resources/file.png"); - mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromStream(info.OpenRead(), "file.png", "image/png")); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromStream(info.OpenRead(), "file.png", "image/png")); TestContext.WriteLine(mock.Result); info = new FileInfo("../../../../../../../resources/large_file.mp4"); - mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromStream(info.OpenRead(), "large_file.mp4", "video/mp4")); + mock = await general.Upload("string", 123, new List() { "string in array" }, InputFile.FromStream(info.OpenRead(), "large_file.mp4", "video/mp4")); TestContext.WriteLine(mock.Result); try @@ -103,11 +104,36 @@ public async Task Test1() TestContext.WriteLine(e.Message); } -// mock = await general.SetCookie(); -// TestContext.WriteLine(mock.Result); -// -// mock = await general.GetCookie(); -// TestContext.WriteLine(mock.Result); + await general.Empty(); + + // Query helper tests + TestContext.WriteLine(Query.Equal("released", new List { true })); + TestContext.WriteLine(Query.Equal("title", new List { "Spiderman", "Dr. Strange" })); + TestContext.WriteLine(Query.NotEqual("title", "Spiderman")); + TestContext.WriteLine(Query.LessThan("releasedYear", 1990)); + TestContext.WriteLine(Query.GreaterThan("releasedYear", 1990)); + TestContext.WriteLine(Query.Search("name", "john")); + TestContext.WriteLine(Query.OrderAsc("title")); + TestContext.WriteLine(Query.OrderDesc("title")); + TestContext.WriteLine(Query.CursorAfter("my_movie_id")); + TestContext.WriteLine(Query.CursorBefore("my_movie_id")); + TestContext.WriteLine(Query.Limit(50)); + TestContext.WriteLine(Query.Offset(20)); + + // Permission & Roles helper tests + TestContext.WriteLine(Permission.Read(Role.Any())); + TestContext.WriteLine(Permission.Write(Role.User(ID.Custom("userid")))); + TestContext.WriteLine(Permission.Create(Role.Users())); + TestContext.WriteLine(Permission.Update(Role.Guests())); + TestContext.WriteLine(Permission.Delete(Role.Team("teamId", "owner"))); + TestContext.WriteLine(Permission.Delete(Role.Team("teamId"))); + TestContext.WriteLine(Permission.Create(Role.Member("memberId"))); + TestContext.WriteLine(Permission.Update(Role.Users("verified")));; + TestContext.WriteLine(Permission.Update(Role.User(ID.Custom("userid"), "unverified")));; + + // ID helper tests + TestContext.WriteLine(ID.Unique()); + TestContext.WriteLine(ID.Custom("custom_id")); } } } \ No newline at end of file From 993a13bae2b195084e35ff2f6174fe2823ee6dbf Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 13 Oct 2022 21:50:33 +1300 Subject: [PATCH 49/62] Add headers test --- tests/languages/dotnet/Tests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index dd446bd08..ca3c9684b 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -134,6 +134,9 @@ public async Task Test1() // ID helper tests TestContext.WriteLine(ID.Unique()); TestContext.WriteLine(ID.Custom("custom_id")); + + mock = await general.Headers(); + TestContext.WriteLine(mock.Result); } } } \ No newline at end of file From b0b5afba833ac412666e8c815b984a42594df61d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 13 Oct 2022 21:52:40 +1300 Subject: [PATCH 50/62] Merge fixes --- templates/dotnet/base/requests/api.twig | 2 +- templates/dotnet/base/utils.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/dotnet/base/requests/api.twig b/templates/dotnet/base/requests/api.twig index 016a85be3..bf030e99c 100644 --- a/templates/dotnet/base/requests/api.twig +++ b/templates/dotnet/base/requests/api.twig @@ -1,5 +1,5 @@ {% import 'dotnet/base/utils.twig' as utils %} - return _client.Call{% if method.type != 'webAuth' %}<{{ _self.resultType(spec.title, method) }}>{% endif %}( + return _client.Call{% if method.type != 'webAuth' %}<{{ utils.resultType(spec.title, method) }}>{% endif %}( method: "{{ method.method | caseUpper }}", path: path, headers: headers, diff --git a/templates/dotnet/base/utils.twig b/templates/dotnet/base/utils.twig index 04c029feb..602134741 100644 --- a/templates/dotnet/base/utils.twig +++ b/templates/dotnet/base/utils.twig @@ -1,6 +1,6 @@ {% macro parameter(parameter) %} {% if parameter.name == 'orderType' %}{{ 'OrderType orderType = OrderType.ASC' }}{% else %} -{{ parameter.type | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} +{{ parameter | typeName }}{% if not parameter.required %}?{% endif %} {{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required %} = null{% endif %}{% endif %} {% endmacro %} {% macro method_parameters(parameters, consumes) %} {% if parameters.all|length > 0 %}{% for parameter in parameters.all | filter((param) => not param.isGlobal) %}{{ _self.parameter(parameter) }}{% if not loop.last %}{{ ', ' }}{% endif %}{% endfor %}{% if 'multipart/form-data' in consumes %},{% endif %}{% endif %}{% if 'multipart/form-data' in consumes %} Action? onProgress = null{% endif %} From d168697bcd94d070903cfa491837c114ec1b4236 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 13 Oct 2022 21:59:26 +1300 Subject: [PATCH 51/62] Fix list query params --- .../src/Appwrite/Extensions/Extensions.cs.twig | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Extensions/Extensions.cs.twig b/templates/dotnet/src/Appwrite/Extensions/Extensions.cs.twig index ff19ae70c..10b2b5035 100644 --- a/templates/dotnet/src/Appwrite/Extensions/Extensions.cs.twig +++ b/templates/dotnet/src/Appwrite/Extensions/Extensions.cs.twig @@ -1,7 +1,7 @@ using Newtonsoft.Json; using System; +using System.Collections; using System.Collections.Generic; -using System.Linq; namespace {{ spec.title | caseUcfirst }}.Extensions { @@ -22,17 +22,19 @@ namespace {{ spec.title | caseUcfirst }}.Extensions { case null: continue; - case List list: - query.AddRange(list.Select(entry => - $"{kvp.Key}[]={Uri.EscapeUriString(entry.ToString())}" - )); + case IList list: + foreach (var item in list) + { + query.Add($"{kvp.Key}[]={item}"); + } break; default: - query.Add($"{kvp.Key}={Uri.EscapeUriString(kvp.Value.ToString())}"); + query.Add($"{kvp.Key}={kvp.Value.ToString()}"); break; } } - return string.Join("&", query); + + return Uri.EscapeUriString(string.Join("&", query)); } private static IDictionary _mappings = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { From 44616470d4a6e2333feaa415ac9d8e9d4f8d41c8 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 13 Oct 2022 22:04:19 +1300 Subject: [PATCH 52/62] Fix list query values --- templates/dotnet/src/Appwrite/Query.cs.twig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Query.cs.twig b/templates/dotnet/src/Appwrite/Query.cs.twig index 91e59e74f..cf13f7fb4 100644 --- a/templates/dotnet/src/Appwrite/Query.cs.twig +++ b/templates/dotnet/src/Appwrite/Query.cs.twig @@ -1,5 +1,5 @@ +using System.Collections; using System.Collections.Generic; -using System.Linq; namespace Appwrite { @@ -67,9 +67,14 @@ namespace Appwrite private static string AddQuery(string attribute, string method, object value) { - if (value is List list) + if (value is IList list) { - return $"{method}(\"{attribute}\", [{string.Join(",", list.Select(ParseValues))}])"; + var parsed = new List(); + foreach (var item in list) + { + parsed.Add(ParseValues(item)); + } + return $"{method}(\"{attribute}\", [{string.Join(",", parsed)}])"; } else { From 96605249b9873dcdba610b2e232ab3a83cf1343f Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 13 Oct 2022 22:04:44 +1300 Subject: [PATCH 53/62] Fix bool query values --- templates/dotnet/src/Appwrite/Query.cs.twig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Query.cs.twig b/templates/dotnet/src/Appwrite/Query.cs.twig index cf13f7fb4..e94017ffd 100644 --- a/templates/dotnet/src/Appwrite/Query.cs.twig +++ b/templates/dotnet/src/Appwrite/Query.cs.twig @@ -84,9 +84,15 @@ namespace Appwrite private static string ParseValues(object value) { - return value is string - ? $"\"{value}\"" - : value.ToString(); + switch (value) + { + case string str: + return $"\"{str}\""; + case bool boolean: + return boolean.ToString().ToLower(); + default: + return value.ToString(); + } } } } \ No newline at end of file From bf1e0f75009813b7ed63d88893f5637a73cdd204 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 13 Oct 2022 22:13:24 +1300 Subject: [PATCH 54/62] Fix endpoint --- templates/dotnet/src/Appwrite/Client.cs.twig | 78 +++++++++----------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Client.cs.twig b/templates/dotnet/src/Appwrite/Client.cs.twig index f7eb18ee5..134c5a796 100644 --- a/templates/dotnet/src/Appwrite/Client.cs.twig +++ b/templates/dotnet/src/Appwrite/Client.cs.twig @@ -17,13 +17,13 @@ namespace {{ spec.title | caseUcfirst }} { public class Client { - public string EndPoint => _endPoint; + public string Endpoint => _endpoint; public Dictionary Config => _config; private HttpClient _http; private readonly Dictionary _headers; private readonly Dictionary _config; - private string _endPoint; + private string _endpoint; private static readonly int ChunkSize = 5 * 1024 * 1024; @@ -49,11 +49,11 @@ namespace {{ spec.title | caseUcfirst }} }; public Client( - string endPoint = "https://appwrite.io/v1", + string endpoint = "{{spec.endpoint}}", bool selfSigned = false, HttpClient? http = null) { - _endPoint = endPoint; + _endpoint = endpoint; _http = http ?? new HttpClient(); _headers = new Dictionary() { @@ -92,9 +92,9 @@ namespace {{ spec.title | caseUcfirst }} return this; } - public Client SetEndPoint(string endPoint) + public Client SetEndpoint(string endpoint) { - _endPoint = endPoint; + _endpoint = endpoint; return this; } @@ -142,7 +142,7 @@ namespace {{ spec.title | caseUcfirst }} var request = new HttpRequestMessage( new HttpMethod(method), - _endPoint + path + queryString); + _endpoint + path + queryString); if ("multipart/form-data".Equals( headers["content-type"], @@ -212,54 +212,48 @@ namespace {{ spec.title | caseUcfirst }} request.Headers.Add(header.Key, header.Value); } } - try - { - var response = await _http.SendAsync(request); - var code = (int)response.StatusCode; - var contentType = response.Content.Headers - .GetValues("Content-Type") - .FirstOrDefault() ?? string.Empty; - var isJson = contentType.Contains("application/json"); - var isBytes = contentType.Contains("application/octet-stream"); + var response = await _http.SendAsync(request); + var code = (int)response.StatusCode; + var contentType = response.Content.Headers + .GetValues("Content-Type") + .FirstOrDefault() ?? string.Empty; - if (code >= 400) { - var message = await response.Content.ReadAsStringAsync(); + var isJson = contentType.Contains("application/json"); + var isBytes = contentType.Contains("application/octet-stream"); - if (isJson) { - message = JObject.Parse(message)["message"]!.ToString(); - } + if (code >= 400) { + var message = await response.Content.ReadAsStringAsync(); - throw new {{spec.title | caseUcfirst}}Exception(message, code); + if (isJson) { + message = JObject.Parse(message)["message"]!.ToString(); } - if (isJson) - { - var responseString = await response.Content.ReadAsStringAsync(); + throw new {{spec.title | caseUcfirst}}Exception(message, code); + } - var dict = JsonConvert.DeserializeObject>( - responseString, - DeserializerSettings); + if (isJson) + { + var responseString = await response.Content.ReadAsStringAsync(); - if (convert != null) - { - return convert(dict!); - } + var dict = JsonConvert.DeserializeObject>( + responseString, + DeserializerSettings); - return (dict as T)!; - } - else if (isBytes) - { - return ((await response.Content.ReadAsByteArrayAsync()) as T)!; - } - else + if (convert != null) { - return default!; + return convert(dict!); } + + return (dict as T)!; + } + else if (isBytes) + { + return ((await response.Content.ReadAsByteArrayAsync()) as T)!; } - catch (System.Exception e) + else { - throw new {{spec.title | caseUcfirst}}Exception(e.Message, e); + return default!; } } From a8fb7654e9508a0b1dee390e673d8141ae6cddab Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 21 Oct 2022 23:27:05 +1300 Subject: [PATCH 55/62] Fix compilation for .NET < 6 --- templates/dotnet/src/Appwrite/Role.cs.twig | 64 +++++++++++----------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/templates/dotnet/src/Appwrite/Role.cs.twig b/templates/dotnet/src/Appwrite/Role.cs.twig index b4bcb9ba7..7fc1fdbcd 100644 --- a/templates/dotnet/src/Appwrite/Role.cs.twig +++ b/templates/dotnet/src/Appwrite/Role.cs.twig @@ -1,39 +1,41 @@ -namespace Appwrite; - -public static class Role +namespace Appwrite { - public static string Any() + public static class Role { - return "any"; - } + public static string Any() + { + return "any"; + } - public static string User(string id, string status = "") - { - return status == string.Empty - ? $"user:{id}" - : $"user:{id}/{status}"; - } + public static string User(string id, string status = "") + { + return status == string.Empty + ? $"user:{id}" + : $"user:{id}/{status}"; + } - public static string Users(string status = "") - { - return status == string.Empty - ? "users" : - $"users/{status}"; - } - public static string Guests() - { - return "guests"; - } + public static string Users(string status = "") + { + return status == string.Empty + ? "users" : + $"users/{status}"; + } - public static string Team(string id, string role = "") - { - return role == string.Empty - ? $"team:{id}" - : $"team:{id}/{role}"; - } + public static string Guests() + { + return "guests"; + } - public static string Member(string id) - { - return $"member:{id}"; + public static string Team(string id, string role = "") + { + return role == string.Empty + ? $"team:{id}" + : $"team:{id}/{role}"; + } + + public static string Member(string id) + { + return $"member:{id}"; + } } } \ No newline at end of file From 17fed89f8da7e28c06b917f0e4f1816cf0276dea Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 21 Oct 2022 23:28:56 +1300 Subject: [PATCH 56/62] Disable flutter beta test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5de004b0b..f20b80fda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ env: - SDK=FlutterStable - SDK=Go112 - SDK=Go118 - - SDK=FlutterBeta +# - SDK=FlutterBeta - SDK=KotlinJava8 - SDK=KotlinJava11 - SDK=KotlinJava17 From 6935c149b92aef0fa4a64f1c8012a0361e2a2acc Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 25 Oct 2022 11:53:45 +1300 Subject: [PATCH 57/62] Lint --- src/SDK/Language/DotNet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index b57c986ea..5c83ed503 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -258,7 +258,7 @@ public function getParamExample(array $param): string if (\str_ends_with($example, ']')) { $example = \substr($example, 0, -1); } - $output .= 'new List<'. $this->getTypeName($param['array']) . '> {' . $example . '}'; + $output .= 'new List<' . $this->getTypeName($param['array']) . '> {' . $example . '}'; break; } } else { From cc46c3d57c282051f3d1cc602b968826e2c3bd9c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 4 Nov 2022 21:18:05 +1300 Subject: [PATCH 58/62] Remove irrelevant change --- templates/android/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/android/gradle/wrapper/gradle-wrapper.properties b/templates/android/gradle/wrapper/gradle-wrapper.properties index 710f6bd0b..2dadb9d6e 100644 --- a/templates/android/gradle/wrapper/gradle-wrapper.properties +++ b/templates/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jun 01 15:55:54 IST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 9e7a321c81ac608d3577d3233b41153b4c893bb7 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 May 2023 13:25:44 +1200 Subject: [PATCH 59/62] Add new 1.3 queries --- templates/dotnet/src/Appwrite/Query.cs.twig | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/templates/dotnet/src/Appwrite/Query.cs.twig b/templates/dotnet/src/Appwrite/Query.cs.twig index e94017ffd..c940f4d6b 100644 --- a/templates/dotnet/src/Appwrite/Query.cs.twig +++ b/templates/dotnet/src/Appwrite/Query.cs.twig @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; namespace Appwrite { @@ -40,6 +41,46 @@ namespace Appwrite return AddQuery(attribute, "search", value); } + public static string IsNull(string attribute) + { + return $"isNull(\"{attribute}\")"; + } + + public static string IsNotNull(string attribute) + { + return $"isNotNull(\"{attribute}\")"; + } + + public static string StartsWith(string attribute, string value) + { + return AddQuery(attribute, "startsWith", value); + } + + public static string EndsWith(string attribute, string value) + { + return AddQuery(attribute, "endsWith", value); + } + + public static string Between(string attribute, string start, string end) + { + return AddQuery(attribute, "between", new List { start, end }); + } + + public static string Between(string attribute, int start, int end) + { + return AddQuery(attribute, "between", new List { start, end }); + } + + public static string Between(string attribute, double start, double end) + { + return AddQuery(attribute, "between", new List { start, end }); + } + + public static string Select(List attributes) + { + return $"select([{string.Join(",", attributes.Select(attribute => $"\"{attribute}\""))}])"; + } + public static string CursorAfter(string documentId) { return $"cursorAfter(\"{documentId}\")"; From a9a28c03bf93a9896022a0db6017f4cd4d819eb5 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 May 2023 13:25:59 +1200 Subject: [PATCH 60/62] Add new 1.3 query tests --- tests/languages/dotnet/Tests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/languages/dotnet/Tests.cs b/tests/languages/dotnet/Tests.cs index ca3c9684b..cb6b765d6 100644 --- a/tests/languages/dotnet/Tests.cs +++ b/tests/languages/dotnet/Tests.cs @@ -113,6 +113,14 @@ public async Task Test1() TestContext.WriteLine(Query.LessThan("releasedYear", 1990)); TestContext.WriteLine(Query.GreaterThan("releasedYear", 1990)); TestContext.WriteLine(Query.Search("name", "john")); + TestContext.WriteLine(Query.IsNull("name")); + TestContext.WriteLine(Query.IsNotNull("name")); + TestContext.WriteLine(Query.Between("age", 50, 100)); + TestContext.WriteLine(Query.Between("age", 50.5, 100.5)); + TestContext.WriteLine(Query.Between("name", "Anna", "Brad")); + TestContext.WriteLine(Query.StartsWith("name", "Ann")); + TestContext.WriteLine(Query.EndsWith("name", "nne")); + TestContext.WriteLine(Query.Select(new List { "name", "age" })); TestContext.WriteLine(Query.OrderAsc("title")); TestContext.WriteLine(Query.OrderDesc("title")); TestContext.WriteLine(Query.CursorAfter("my_movie_id")); From 715c47b9bdaea15aead4abbad19a8025fb32ce44 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 May 2023 13:27:02 +1200 Subject: [PATCH 61/62] Update tests to .NET 6 + 7 with alpine images --- .travis.yml | 3 +- tests/DotNet60Test.php | 2 +- tests/{DotNet50Test.php => DotNet70Test.php} | 6 +- tests/languages/dotnet/Tests31.csproj | 14 --- .../dotnet/{Tests50.csproj => Tests70.csproj} | 2 +- tests/languages/dotnet/tests.ps1 | 96 ------------------- 6 files changed, 6 insertions(+), 117 deletions(-) rename tests/{DotNet50Test.php => DotNet70Test.php} (83%) delete mode 100644 tests/languages/dotnet/Tests31.csproj rename tests/languages/dotnet/{Tests50.csproj => Tests70.csproj} (91%) delete mode 100644 tests/languages/dotnet/tests.ps1 diff --git a/.travis.yml b/.travis.yml index 8694f24d8..663053727 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,9 +29,8 @@ env: - SDK=DartStable - SDK=Deno1193 - SDK=Deno1303 - - SDK=DotNet31 - - SDK=DotNet50 - SDK=DotNet60 + - SDK=DotNet70 - SDK=FlutterStable - SDK=FlutterBeta - SDK=Go112 diff --git a/tests/DotNet60Test.php b/tests/DotNet60Test.php index c6d117504..ecadcd9d6 100644 --- a/tests/DotNet60Test.php +++ b/tests/DotNet60Test.php @@ -17,7 +17,7 @@ class DotNet60Test extends Base 'cp tests/languages/dotnet/Tests60.csproj tests/sdks/dotnet/src/test/Tests.csproj', ]; protected string $command = - 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:6.0-alpine dotnet test --verbosity normal --framework net6.0'; + 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:6.0-alpine3.17 dotnet test --verbosity normal --framework net6.0'; protected array $expectedOutput = [ ...Base::FOO_RESPONSES, diff --git a/tests/DotNet50Test.php b/tests/DotNet70Test.php similarity index 83% rename from tests/DotNet50Test.php rename to tests/DotNet70Test.php index 8877f1ed8..582213e81 100644 --- a/tests/DotNet50Test.php +++ b/tests/DotNet70Test.php @@ -2,7 +2,7 @@ namespace Tests; -class DotNet50Test extends Base +class DotNet70Test extends Base { protected string $sdkName = 'dotnet'; protected string $sdkPlatform = 'server'; @@ -14,10 +14,10 @@ class DotNet50Test extends Base protected array $build = [ 'mkdir -p tests/sdks/dotnet/src/test', 'cp tests/languages/dotnet/Tests.cs tests/sdks/dotnet/src/test/Tests.cs', - 'cp tests/languages/dotnet/Tests50.csproj tests/sdks/dotnet/src/test/Tests.csproj', + 'cp tests/languages/dotnet/Tests70.csproj tests/sdks/dotnet/src/test/Tests.csproj', ]; protected string $command = - 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim dotnet test --verbosity normal --framework net5.0'; + 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/dotnet/src/test/ mcr.microsoft.com/dotnet/sdk:7.0-alpine3.17 dotnet test --verbosity normal --framework net7.0'; protected array $expectedOutput = [ ...Base::FOO_RESPONSES, diff --git a/tests/languages/dotnet/Tests31.csproj b/tests/languages/dotnet/Tests31.csproj deleted file mode 100644 index 06f9420ba..000000000 --- a/tests/languages/dotnet/Tests31.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - netcoreapp3.1 - latest - false - - - - - - - - - diff --git a/tests/languages/dotnet/Tests50.csproj b/tests/languages/dotnet/Tests70.csproj similarity index 91% rename from tests/languages/dotnet/Tests50.csproj rename to tests/languages/dotnet/Tests70.csproj index 8f77f7f7c..392e78b36 100644 --- a/tests/languages/dotnet/Tests50.csproj +++ b/tests/languages/dotnet/Tests70.csproj @@ -1,6 +1,6 @@ - net5.0 + net7.0 latest false diff --git a/tests/languages/dotnet/tests.ps1 b/tests/languages/dotnet/tests.ps1 deleted file mode 100644 index bf8b5c808..000000000 --- a/tests/languages/dotnet/tests.ps1 +++ /dev/null @@ -1,96 +0,0 @@ -function Await-Task { - param ( - [Parameter(ValueFromPipeline=$true, Mandatory=$true)] - $task - ) - - process { - while (-not $task.AsyncWaitHandle.WaitOne(200)) { } - $task.GetAwaiter() - } -} - -function Print-Response { - param ( - $response - ) - Write-Host ($response.Content.ReadAsStringAsync().Result | ConvertFrom-Json).result -} - -Add-Type -Path "/app/tests/sdks/dotnet/src/test/Appwrite.dll" | Out-Null - -$client = New-Object Appwrite.Client -$foo = New-Object Appwrite.Foo -ArgumentList $client -$bar = New-Object Appwrite.Bar -ArgumentList $client -$general = New-Object Appwrite.General -ArgumentList $client - -Write-Host -Write-Host "Test Started" - -$list = $("string in array") -$response = $foo.Get("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $foo.Post("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $foo.Put("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $foo.Patch("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $foo.Delete("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $bar.Get("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $bar.Post("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $bar.Put("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $bar.Patch("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $bar.Delete("string", 123, $list) | Await-Task -Print-Response $response.GetResult() - -$response = $general.Redirect() | Await-Task -Print-Response $response.GetResult() - -$response = $general.Upload("string", 123, $list, (Get-Item "../../../../resources/file.png")) | Await-Task -Print-Response $response.GetResult() - -try { - $response = $general.Empty() | Await-Task - $response.GetResult() | Out-Null -} catch [Appwrite.AppwriteException] { - Write-Host $_.Exception.Message -} - -try { - $response = $general.Error400() | Await-Task - $response.GetResult() -} catch [Appwrite.AppwriteException] { - Write-Host $_.Exception.Message -} - -try { - $response = $general.Error500() | Await-Task - $response.GetResult() -} catch [Appwrite.AppwriteException] { - Write-Host $_.Exception.Message -} - -try { - $response = $general.Error502() | Await-Task - $response.GetResult() -} catch [Appwrite.AppwriteException] { - Write-Host $_.Exception.Message -} - -$response = $general.headers() | Await-Task -Print-Response $response.GetResult() \ No newline at end of file From 75b45e81681ba6202d82ccc77c40831f00c3ae92 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 3 May 2023 13:34:08 +1200 Subject: [PATCH 62/62] Update target frameworks to netstandard2.0 + net461 --- templates/dotnet/src/Appwrite/Appwrite.csproj.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig index f4da089f5..fbb3d1b75 100644 --- a/templates/dotnet/src/Appwrite/Appwrite.csproj.twig +++ b/templates/dotnet/src/Appwrite/Appwrite.csproj.twig @@ -1,6 +1,6 @@ - net47;net48;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1 + netstandard2.0;net461 {{spec.title}} {{sdk.version}} {{spec.contactName}}