Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript Adding model interface, serializer and deserializer functions and remove model classes #1559

Merged
merged 145 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
145 commits
Select commit Hold shift + click to select a range
0de0fe7
Creating interfaces with all properties, renaming model classes and …
nikithauc Apr 26, 2022
df8c7ef
codemethodwriter edits
nikithauc Apr 30, 2022
e6d7220
set type name final interface name, usings non class prop types model
nikithauc May 1, 2022
e606ba1
conditions to append impl
nikithauc May 2, 2022
81c8abc
add using if not of same type, sorting inheritance order
nikithauc May 2, 2022
e2614fc
arrvalue for enum,
nikithauc May 4, 2022
f097e2d
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc May 4, 2022
267e036
working graph; add usings from class usings into interface;
nikithauc May 6, 2022
0465e1b
converting request config to interfaces
nikithauc May 10, 2022
bf7d7e1
adding declaration tests and clean up
nikithauc May 10, 2022
b1db79f
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc May 10, 2022
c38ed3d
unit test typescript refiner
nikithauc May 12, 2022
56114bd
resolve code smell issues
nikithauc May 12, 2022
3553d1a
remove unused variable
nikithauc May 12, 2022
dd8e76c
toarray condition, split method
nikithauc May 12, 2022
a7de27a
codemethod param condition; processmodelprops
nikithauc May 17, 2022
af1d19f
making additional data optional, breaking smaller functions
nikithauc May 18, 2022
6a3b45b
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc May 19, 2022
16c5fa3
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc May 19, 2022
bb64711
function mappings update
nikithauc May 20, 2022
6d2f750
create new property for interface
nikithauc May 22, 2022
9f29620
remove code smells
nikithauc May 23, 2022
9ae1c26
bodyparsable construction
nikithauc May 23, 2022
c9c6bd8
make method static
nikithauc May 23, 2022
c0dd9a6
super constructor, indentation
nikithauc May 25, 2022
f8fe56c
partial interface implementation
nikithauc May 27, 2022
942e71b
switch parent type; clone
nikithauc Jun 2, 2022
b15f1b7
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jun 2, 2022
acf8fcd
remove inherits from inheritance
nikithauc Jun 3, 2022
3caf07f
Update src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs
nikithauc Jun 7, 2022
1b65d46
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jun 7, 2022
f9b34f6
Merge branch 'nikithauc/model-as-interfaces' of https://github.com/mi…
nikithauc Jun 7, 2022
a9e1d35
remove unnecessary cast
nikithauc Jun 7, 2022
6637909
Update CHANGELOG.md
nikithauc Jun 21, 2022
536bc30
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jun 21, 2022
2b0498a
method writer property instance
nikithauc Jun 24, 2022
fcdf53e
Merge branch 'nikithauc/model-as-interfaces' of https://github.com/mi…
nikithauc Jun 24, 2022
f079492
var name change
nikithauc Jun 27, 2022
38ede80
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jun 27, 2022
f034729
reset constructor initialization
nikithauc Jun 27, 2022
6ec98c7
setter addn
nikithauc Jul 1, 2022
e443ce8
add null checks
nikithauc Jul 1, 2022
0ec9918
code cleanup
nikithauc Jul 1, 2022
6fa5ed6
remove code smell
nikithauc Jul 1, 2022
17728f7
Merge branch 'main' into nikithauc/model-as-interfaces
baywet Jul 8, 2022
8e43ca1
Merge branch 'main' into nikithauc/model-as-interfaces
baywet Aug 8, 2022
f6c7935
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Nov 17, 2022
754b0f7
Enum array value ; main merge test works
nikithauc Nov 17, 2022
f152b4b
null checks
nikithauc Nov 17, 2022
329f059
removing classes; serialization functions
nikithauc Jan 5, 2023
70766d7
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jan 10, 2023
7f9b912
parsable factory temp with graph full working
nikithauc Jan 21, 2023
6d9a2cb
comments
nikithauc Jan 23, 2023
e2ef046
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jan 23, 2023
9482494
updating unit tests
nikithauc Jan 24, 2023
e0c039a
additional data switch
nikithauc Jan 27, 2023
1bae27c
update newline in test assertion; code cleanup
nikithauc Jan 27, 2023
3d1aab1
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jan 27, 2023
f2df9af
reset codemethod writer code; cleanup
nikithauc Jan 27, 2023
21fd3cb
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jan 27, 2023
808167e
rename method and remove unused code
nikithauc Jan 27, 2023
1156cac
Merge branch 'nikithauc/model-as-interfaces' of https://github.com/mi…
nikithauc Jan 27, 2023
c374c7d
factory switch property name
nikithauc Jan 28, 2023
eb2c547
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Jan 31, 2023
30b51ee
linting TS refiner
nikithauc Feb 1, 2023
b15414b
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Feb 1, 2023
9e8079a
more linting
nikithauc Feb 2, 2023
3d8d0f6
sync changes test for full graph
nikithauc Feb 2, 2023
fb80027
updating tests
nikithauc Feb 2, 2023
107e54a
updating tests after the refactor
nikithauc Feb 3, 2023
13e57d6
update child name when replacing reserved name
nikithauc Feb 5, 2023
3d0d2f1
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Feb 5, 2023
ee87717
removing switch for addn data
nikithauc Feb 6, 2023
e962c6f
format and test change for switch
nikithauc Feb 6, 2023
d6fe80a
Apply suggestions from code review
nikithauc Feb 13, 2023
f29c86a
revert launchSettings.json
Feb 15, 2023
754a258
addressing comments
nikithauc Feb 16, 2023
667f4ae
Merge branch 'nikithauc/model-as-interfaces' of https://github.com/mi…
nikithauc Feb 16, 2023
8ed9526
Merge branch 'main' into nikithauc/model-as-interfaces
nikithauc Feb 16, 2023
93b566f
format
nikithauc Feb 16, 2023
1e408eb
Merge branch 'main' into nikithauc/model-as-interfaces
Feb 27, 2023
783429a
fix a failing test case
Feb 27, 2023
c20b391
resolve pr comment - unnecessary string interpolation
Feb 27, 2023
4c4a23c
address pr comments
Feb 27, 2023
228a6e6
address pr comments
Feb 27, 2023
e7d51c3
address pr comments - optional chaining and asserting a non-null value
Feb 28, 2023
fd9213b
format code
Feb 28, 2023
1877aa0
Merge branch 'main' into nikithauc/model-as-interfaces
Feb 28, 2023
a5f26ee
address pr comments
Mar 1, 2023
48f0c81
address pr comments
Mar 1, 2023
0f0bc8e
format code
Mar 1, 2023
2a9076c
address pr comments and minor refactoring
Mar 2, 2023
853944d
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 2, 2023
bfc6440
address pr comments
Mar 2, 2023
979766e
address pr comments
Mar 2, 2023
e08d017
address pr comments
Mar 2, 2023
da0cc63
address pr comments
Mar 2, 2023
3514729
address pr comments
Mar 2, 2023
952bc05
address pr comments
Mar 3, 2023
e33969d
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 3, 2023
231f341
address pr comments
Mar 3, 2023
75c1cf3
address pr comments
Mar 3, 2023
e5f8596
address pr comments
Mar 3, 2023
e4180f7
address pr comments
Mar 6, 2023
eddff21
format code
Mar 6, 2023
d9f4c8a
address pr comment: refactor CodeNameSpaceWriter.cs to use a single loop
Mar 7, 2023
3cf82d2
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 7, 2023
36fa8d7
remove Parallel.ForEach in the writer to ensure test cases consistency
Mar 7, 2023
9245ca6
address pr comments
Mar 8, 2023
2e51d17
address pr comments
Mar 8, 2023
083dcf2
remove duplicate comments on properties
Mar 9, 2023
028addf
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 9, 2023
f17406d
Apply suggestions from code review
baywet Mar 16, 2023
9415aa6
Update src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs
baywet Mar 16, 2023
1403c63
Update tests/Kiota.Builder.Tests/Refiners/TypeScriptLanguageRefinerTe…
baywet Mar 16, 2023
cb2a41d
Update src/Kiota.Builder/Refiners/TypeScriptRefiner.cs
baywet Mar 16, 2023
94651a1
Update src/Kiota.Builder/Refiners/TypeScriptRefiner.cs
baywet Mar 16, 2023
b4b0f9f
Update src/Kiota.Builder/Refiners/TypeScriptRefiner.cs
baywet Mar 16, 2023
60d1246
Merge branch 'main' into nikithauc/model-as-interfaces
baywet Mar 16, 2023
e2d3ed1
- fixes name conflict
baywet Mar 16, 2023
1530f88
- moves changelog entry to the right place
baywet Mar 16, 2023
bd78558
- reverts extraneous parameter
baywet Mar 16, 2023
8abce95
- code method writer final touches
baywet Mar 16, 2023
b882bfb
- code linting
baywet Mar 16, 2023
1d22ddf
- code linting
baywet Mar 16, 2023
0f8991e
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 20, 2023
067901e
refactor TypeScriptLanguageRefinerTests.cs to remove redundant parame…
Mar 20, 2023
97686d5
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 22, 2023
23727cb
remove an extra 'object' parameter from the writeObjectValue and writ…
Mar 22, 2023
a6e0de0
resolve missing import statement on the request builder class
Mar 27, 2023
706b474
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 27, 2023
2ec8e8e
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 28, 2023
ed98839
Merge branch 'main' into nikithauc/model-as-interfaces
Mar 28, 2023
8ddc23a
fix Sonar warnings
Mar 29, 2023
7aa74bb
Merge branch 'main' into nikithauc/model-as-interfaces
Apr 6, 2023
299873d
add serializerMethod property
Apr 11, 2023
0752ccc
retrieve the serialization function name correctly
Apr 12, 2023
a9c282a
Merge branch 'main' into nikithauc/model-as-interfaces
Apr 12, 2023
6b7cce5
Merge branch 'main' into nikithauc/model-as-interfaces
Apr 13, 2023
5d6d6a0
set type for the default value
Apr 13, 2023
11fc839
Merge branch 'main' into nikithauc/model-as-interfaces
Apr 14, 2023
6dbf21d
remove unused method UpdateReservedNameReplacementInParent
Apr 14, 2023
af03dce
Merge branch 'main' into nikithauc/model-as-interfaces
baywet Apr 24, 2023
407c811
address PR comments
Apr 25, 2023
2c9aa6f
Merge branch 'main' into nikithauc/model-as-interfaces
Apr 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
baywet marked this conversation as resolved.
Show resolved Hide resolved

- Added typescript interfaces for models and request config params. [#1013](https://github.com/microsoft/kiota/issues/1013) and [#1521](https://github.com/microsoft/kiota/issues/1521)
- Added automatic loading of the lock file for the extension so quick edits of clients are supported.
- Added trimming of derived types that are not being used by the client. [#2543](https://github.com/microsoft/kiota/issues/2543)
- Added support for merging schemas of AllOf > 2. [#2438](https://github.com/microsoft/kiota/issues/2438)
Expand Down
2 changes: 2 additions & 0 deletions src/Kiota.Builder/CodeDOM/CodeInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ public enum CodeInterfaceKind
{
Custom,
Model,
QueryParameters,
RequestConfiguration
}

public class CodeInterface : ProprietableBlock<CodeInterfaceKind, InterfaceDeclaration>, ITypeDefinition
Expand Down
2 changes: 2 additions & 0 deletions src/Kiota.Builder/CodeDOM/CodeNamespace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public CodeNamespace GetRootNamespace()
public IEnumerable<CodeNamespace> Namespaces => InnerChildElements.Values.OfType<CodeNamespace>();
public IEnumerable<CodeClass> Classes => InnerChildElements.Values.OfType<CodeClass>();
public IEnumerable<CodeEnum> Enums => InnerChildElements.Values.OfType<CodeEnum>();
public IEnumerable<CodeFunction> Functions => InnerChildElements.Values.OfType<CodeFunction>();
public IEnumerable<CodeInterface> Interfaces => InnerChildElements.Values.OfType<CodeInterface>();
public CodeNamespace? FindNamespaceByName(string nsName)
{
ArgumentException.ThrowIfNullOrEmpty(nsName);
Expand Down
3 changes: 2 additions & 1 deletion src/Kiota.Builder/CodeDOM/CodeProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public enum CodePropertyKind
SerializationHint,
}

public class CodeProperty : CodeTerminalWithKind<CodePropertyKind>, IDocumentedElement, IAlternativeName
public class CodeProperty : CodeTerminalWithKind<CodePropertyKind>, IDocumentedElement, IAlternativeName, ICloneable
{
public bool ReadOnly { get; set; } = false;
public AccessModifier Access { get; set; } = AccessModifier.Public;
Expand Down Expand Up @@ -135,3 +135,4 @@ public object Clone()
return property;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public class TypeScriptCodeRenderer : CodeRenderer
public TypeScriptCodeRenderer(GenerationConfiguration configuration) : base(configuration) { }
public override bool ShouldRenderNamespaceFile(CodeNamespace codeNamespace)
{
return codeNamespace.Classes.Any(static c => c.IsOfKind(CodeClassKind.Model));
return codeNamespace.Interfaces.Any();
}
}
nikithauc marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions src/Kiota.Builder/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,13 @@ private static string NormalizeSymbolsAfterCleanup(string original)
/// <returns></returns>
public static string CleanupXMLString(this string? original)
=> SecurityElement.Escape(original) ?? string.Empty;

/// <summary>
/// Checks if 2 strings are equal, case insensitive
/// </summary>
/// <param name="a">The first or current string</param>
/// <param name="b">The second string</param>
/// <returns></returns>
public static bool EqualsIgnoreCase(this string? a, string? b)
=> String.Equals(a, b, StringComparison.OrdinalIgnoreCase);
}
1 change: 1 addition & 0 deletions src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ currentProperty.Type is CodeType propertyType &&

CrawlTree(current, x => ReplaceReservedNames(x, provider, replacement, codeElementExceptions, shouldReplaceCallback));
}

private static void ReplaceReservedEnumNames(CodeEnum currentEnum, IReservedNamesProvider provider, Func<string, string> replacement)
{
currentEnum.Options
Expand Down
641 changes: 634 additions & 7 deletions src/Kiota.Builder/Refiners/TypeScriptRefiner.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class TypeScriptReservedNamesProvider : IReservedNamesProvider
"instanceOf",
"new",
"null",
"package",
"return",
"super",
"switch",
Expand Down
8 changes: 8 additions & 0 deletions src/Kiota.Builder/Writers/LanguageWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,15 @@ internal void WriteLine(string line, bool includeIndent = true)
{
writer?.WriteLine(includeIndent ? GetIndent() + line : line);
}
internal void WriteLines(IEnumerable<string> lines)
{
WriteLinesInternal(lines);
}
internal void WriteLines(params string[] lines)
{
WriteLinesInternal(lines);
}
private void WriteLinesInternal(IEnumerable<string> lines)
{
foreach (var line in lines)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Writers/Php/PhpConventionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ internal void AddParametersAssignment(LanguageWriter writer, CodeTypeBase pathPa
if (parameters.Any())
writer.WriteLines(parameters.Select(p =>
$"${TempDictionaryVarName}['{p.Item2}'] = {p.Item3};"
).ToArray());
));
}

private static bool IsSymbolDuplicated(string symbol, CodeElement targetElement)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal void AddParametersAssignment(LanguageWriter writer, CodeTypeBase? pathP
if (parameters.Any())
writer.WriteLines(parameters.Select(p =>
$"{TempDictionaryVarName}[\"{p.Item2}\"] = {p.Item3}"
).ToArray());
));
}

public override string GetAccessModifier(AccessModifier access)
Expand Down
3 changes: 1 addition & 2 deletions src/Kiota.Builder/Writers/Swift/CodeEnumWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ public override void WriteCodeElement(CodeEnum codeElement, LanguageWriter write
writer.StartBlock($"public enum {codeElement.Name.ToFirstCharacterUpperCase()} : String {{"); //TODO docs
writer.WriteLines(codeElement.Options
.Select(static x => x.Name.ToFirstCharacterUpperCase())
.Select(static (x, idx) => $"case {x}")
.ToArray());
.Select(static (x, idx) => $"case {x}"));
//TODO static parse function?
//enum and ns are closed by the code block end writer
}
Expand Down
200 changes: 193 additions & 7 deletions src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@


using System;

using System.Collections.Generic;
using System.Linq;
using Kiota.Builder.CodeDOM;
using Kiota.Builder.Extensions;

namespace Kiota.Builder.Writers.TypeScript;

public class CodeFunctionWriter : BaseElementWriter<CodeFunction, TypeScriptConventionService>
{
private readonly CodeUsingWriter _codeUsingWriter;
public CodeFunctionWriter(TypeScriptConventionService conventionService, string clientNamespaceName) : base(conventionService)
{
_codeUsingWriter = new(clientNamespaceName);
}
private readonly CodeUsingWriter _codeUsingWriter;
private static readonly HashSet<string> customSerializationWriters = new(StringComparer.OrdinalIgnoreCase) { "writeObjectValue", "writeCollectionOfObjectValues" };

public override void WriteCodeElement(CodeFunction codeElement, LanguageWriter writer)
{
ArgumentNullException.ThrowIfNull(codeElement);
if (codeElement.OriginalLocalMethod == null) throw new InvalidOperationException($"{nameof(codeElement.OriginalLocalMethod)} should not be null");
ArgumentNullException.ThrowIfNull(writer);
if (codeElement.Parent is not CodeNamespace) throw new InvalidOperationException("the parent of a function should be a namespace");

var returnType = conventions.GetTypeString(codeElement.OriginalLocalMethod.ReturnType, codeElement);
_codeUsingWriter.WriteCodeElement(codeElement.StartBlock.Usings, codeElement.GetImmediateParentOfType<CodeNamespace>(), writer);
var codeMethod = codeElement.OriginalLocalMethod;

var returnType = codeMethod.Kind != CodeMethodKind.Factory ? conventions.GetTypeString(codeMethod.ReturnType, codeElement) : string.Empty;
CodeMethodWriter.WriteMethodPrototypeInternal(codeElement.OriginalLocalMethod, writer, returnType, false, conventions, true);

writer.IncreaseIndent();

switch (codeMethod.Kind)
{
case CodeMethodKind.Deserializer:
WriteDeserializerFunction(codeElement, writer);
break;
case CodeMethodKind.Serializer:
WriteSerializerFunction(codeElement, writer);
break;
case CodeMethodKind.Factory:
WriteDiscriminatorFunction(codeElement, writer);
break;
default: throw new InvalidOperationException("Invalid code method kind");
}
}

private void WriteDiscriminatorFunction(CodeFunction codeElement, LanguageWriter writer)
{
var returnType = conventions.GetTypeString(codeElement.OriginalLocalMethod.ReturnType, codeElement);

CodeMethodWriter.WriteDefensiveStatements(codeElement.OriginalLocalMethod, writer);
WriteFactoryMethodBody(codeElement, returnType, writer);
}
Expand All @@ -46,17 +70,179 @@ private void WriteFactoryMethodBody(CodeFunction codeElement, string returnType,
writer.IncreaseIndent();
foreach (var mappedType in codeElement.OriginalMethodParentClass.DiscriminatorInformation.DiscriminatorMappings)
{
var typeName = conventions.GetTypeString(mappedType.Value, codeElement, false, writer);
writer.WriteLine($"case \"{mappedType.Key}\":");
writer.IncreaseIndent();
writer.WriteLine($"return new {typeName.ToFirstCharacterUpperCase()}();");
writer.WriteLine($"return {getDeserializationFunction(codeElement, mappedType.Value.Name.ToFirstCharacterUpperCase())};");
writer.DecreaseIndent();
}
writer.CloseBlock();
writer.CloseBlock();
writer.CloseBlock();
}
var s = getDeserializationFunction(codeElement, returnType);
writer.WriteLine($"return {s.ToFirstCharacterLowerCase()};");
}

private string getDeserializationFunction(CodeElement codeElement, string returnType)
{
if (codeElement.Parent is not CodeNamespace codeNamespace)
{
throw new InvalidOperationException($"{codeElement.Name} does not have a parent namespace");
}
var parent = codeNamespace.FindChildByName<CodeFunction>($"deserializeInto{returnType}");

return conventions.GetTypeString(new CodeType { TypeDefinition = parent }, codeElement, false);
}

private void WriteSerializerFunction(CodeFunction codeElement, LanguageWriter writer)
{
var param = codeElement.OriginalLocalMethod.Parameters.FirstOrDefault(static x => x.Type is CodeType type && type.TypeDefinition is CodeInterface);
if (param == null || param.Type is not CodeType codeType || codeType.TypeDefinition is not CodeInterface codeInterface)
throw new InvalidOperationException("Interface parameter not found for code interface");

writer.IncreaseIndent();

if (codeInterface.StartBlock.Implements.FirstOrDefault(static x => x.TypeDefinition is CodeInterface) is CodeType inherits)
{
writer.WriteLine($"serialize{inherits.TypeDefinition!.Name.ToFirstCharacterUpperCase()}(writer, {param.Name.ToFirstCharacterLowerCase()})");
}

foreach (var otherProp in codeInterface.Properties.Where(static x => x.IsOfKind(CodePropertyKind.Custom) && !x.ExistsInBaseType && !x.ReadOnly))
{
WritePropertySerializer(codeInterface.Name.ToFirstCharacterLowerCase(), otherProp, writer, codeElement);
}

if (codeInterface.GetPropertyOfKind(CodePropertyKind.AdditionalData) is CodeProperty additionalDataProperty)
writer.WriteLine($"writer.writeAdditionalData({codeInterface.Name.ToFirstCharacterLowerCase()}.{additionalDataProperty.Name.ToFirstCharacterLowerCase()});");
writer.DecreaseIndent();
}

private static bool IsCodePropertyCollectionOfEnum(CodeProperty property)
{
return property.Type is CodeType cType && cType.IsCollection && cType.TypeDefinition is CodeEnum;
}

private void WritePropertySerializer(string modelParamName, CodeProperty codeProperty, LanguageWriter writer, CodeFunction codeFunction)
{
var isCollectionOfEnum = IsCodePropertyCollectionOfEnum(codeProperty);
var spreadOperator = isCollectionOfEnum ? "..." : string.Empty;
var codePropertyName = codeProperty.Name.ToFirstCharacterLowerCase();
var propTypeName = conventions.GetTypeString(codeProperty.Type, codeProperty.Parent!, false);

var serializationName = GetSerializationMethodName(codeProperty.Type);

if (customSerializationWriters.Contains(serializationName) && codeProperty.Type is CodeType propType && propType.TypeDefinition is not null)
{
var serializeName = getSerializerAlias(propType, codeFunction, $"serialize{propType.TypeDefinition.Name}");
writer.WriteLine($"writer.{serializationName}<{propTypeName}>(\"{codeProperty.WireName}\", {modelParamName}.{codePropertyName}, {serializeName});");
}
else
{
if (!string.IsNullOrWhiteSpace(spreadOperator))
{
writer.WriteLine($"if({modelParamName}.{codePropertyName})");
}
writer.WriteLine($"writer.{serializationName}(\"{codeProperty.WireName}\", {spreadOperator}{modelParamName}.{codePropertyName});");
}
}

private string GetSerializationMethodName(CodeTypeBase propType)
{
var propertyType = conventions.TranslateType(propType);
if (!string.IsNullOrEmpty(propertyType) && propType is CodeType currentType && GetSerializationMethodNameForCodeType(currentType, propertyType) is string result && !String.IsNullOrWhiteSpace(result))
{
return result;
}
return propertyType switch
{
"string" or "boolean" or "number" or "Guid" or "Date" or "DateOnly" or "TimeOnly" or "Duration" => $"write{propertyType.ToFirstCharacterUpperCase()}Value",
_ => $"writeObjectValue",
};
}

private static string? GetSerializationMethodNameForCodeType(CodeType propType, string propertyType)
{
if (propType.TypeDefinition is CodeEnum currentEnum)
return $"writeEnumValue<{currentEnum.Name.ToFirstCharacterUpperCase()}>";
else if (propType.CollectionKind != CodeTypeBase.CodeTypeCollectionKind.None)
{
if (propType.TypeDefinition == null)
return $"writeCollectionOfPrimitiveValues<{propertyType.ToFirstCharacterLowerCase()}>";
else
return "writeCollectionOfObjectValues";
}
return null;
}

private void WriteDeserializerFunction(CodeFunction codeFunction, LanguageWriter writer)
{
if (codeFunction.OriginalLocalMethod.Parameters.FirstOrDefault() is CodeParameter param && param.Type is CodeType codeType && codeType.TypeDefinition is CodeInterface codeInterface)
{
var properties = codeInterface.Properties.Where(static x => x.Kind == CodePropertyKind.Custom && !x.ExistsInBaseType);

writer.StartBlock("return {");
if (codeInterface.StartBlock.Implements.FirstOrDefault(x => x.TypeDefinition is CodeInterface) is CodeType type && type.TypeDefinition is CodeInterface inherits)
{
writer.WriteLine($"...deserializeInto{inherits?.Name.ToFirstCharacterUpperCase()}({param.Name.ToFirstCharacterLowerCase()}),");
}

foreach (var otherProp in properties)
{
var keyName = !string.IsNullOrWhiteSpace(otherProp.SerializationName) ? otherProp.SerializationName.ToFirstCharacterLowerCase() : otherProp.Name.ToFirstCharacterLowerCase();
writer.WriteLine($"\"{keyName}\": n => {{ {param.Name.ToFirstCharacterLowerCase()}.{otherProp.Name.ToFirstCharacterLowerCase()} = n.{GetDeserializationMethodName(otherProp.Type, codeFunction)}; }},");
}

writer.CloseBlock();
}
else
throw new InvalidOperationException($"Model interface for deserializer function {codeFunction.Name} is not available");
}

writer.WriteLine($"return new {returnType.ToFirstCharacterUpperCase()}();");
private string GetDeserializationMethodName(CodeTypeBase propType, CodeFunction codeFunction)
{
var isCollection = propType.CollectionKind != CodeTypeBase.CodeTypeCollectionKind.None;
var propertyType = conventions.GetTypeString(propType, codeFunction, false);
if (!string.IsNullOrEmpty(propertyType) && propType is CodeType currentType)
{
if (currentType.TypeDefinition is CodeEnum currentEnum)
return $"getEnumValue{(currentEnum.Flags || isCollection ? "s" : string.Empty)}<{currentEnum.Name.ToFirstCharacterUpperCase()}>({propertyType.ToFirstCharacterUpperCase()})";
baywet marked this conversation as resolved.
Show resolved Hide resolved
else if (isCollection)
if (currentType.TypeDefinition == null)
return $"getCollectionOfPrimitiveValues<{propertyType.ToFirstCharacterLowerCase()}>()";
else
{
return $"getCollectionOfObjectValues<{propertyType.ToFirstCharacterUpperCase()}>({GetFactoryMethodName(propType, codeFunction.OriginalLocalMethod)})";
}
}
return propertyType switch
{
"string" or "boolean" or "number" or "Guid" or "Date" or "DateOnly" or "TimeOnly" or "Duration" => $"get{propertyType.ToFirstCharacterUpperCase()}Value()",
_ => $"getObjectValue<{propertyType.ToFirstCharacterUpperCase()}>({GetFactoryMethodName(propType, codeFunction.OriginalLocalMethod)})"
};
}

private string GetFactoryMethodName(CodeTypeBase targetClassType, CodeMethod currentElement)
{
if (conventions.TranslateType(targetClassType) is string targetClassName)
{
var resultName = $"create{targetClassName.ToFirstCharacterUpperCase()}FromDiscriminatorValue";
if (conventions.GetTypeString(targetClassType, currentElement, false) is string returnType && targetClassName.EqualsIgnoreCase(returnType)) return resultName;
if (targetClassType is CodeType currentType &&
currentType.TypeDefinition is CodeInterface definitionClass &&
definitionClass.GetImmediateParentOfType<CodeNamespace>() is CodeNamespace parentNamespace &&
parentNamespace.FindChildByName<CodeFunction>(resultName) is CodeFunction factoryMethod)
{
var methodName = conventions.GetTypeString(new CodeType { Name = resultName, TypeDefinition = factoryMethod }, currentElement, false);
return methodName.ToFirstCharacterUpperCase();// static function is aliased
}
}
throw new InvalidOperationException($"Unable to find factory method for {targetClassType}");
}

private string? getSerializerAlias(CodeType propType, CodeFunction codeFunction, string propertySerializerName)
{
if (propType.TypeDefinition?.Parent is not CodeNamespace parentNameSpace) return string.Empty;
var serializationFunction = parentNameSpace.FindChildByName<CodeFunction>(propertySerializerName);
return conventions.GetTypeString(new CodeType { TypeDefinition = serializationFunction }, codeFunction, false);
}
}
Loading