Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for external documentation links within descriptions in Python. [#2041](https://github.com/microsoft/kiota/issues/2041)
- Added support for API manifests. [#3104](https://github.com/microsoft/kiota/issues/3104)
- Added support for reserved path parameters. [#2320](https://github.com/microsoft/kiota/issues/2320)
- Added support for csv values in enums using a mask.

### Changed

Expand Down
14 changes: 11 additions & 3 deletions src/Kiota.Builder/Refiners/GoRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
cancellationToken.ThrowIfCancellationRequested();
MakeModelPropertiesNullable(
generatedCode);
AddErrorImportForEnums(
AddErrorAndStringsImportForEnums(
generatedCode);
var defaultConfiguration = new GenerationConfiguration();
ReplaceDefaultSerializationModules(
Expand Down Expand Up @@ -469,16 +469,24 @@ private static void ReplaceRequestBuilderPropertiesByMethods(CodeElement current
}
CrawlTree(currentElement, ReplaceRequestBuilderPropertiesByMethods);
}
private static void AddErrorImportForEnums(CodeElement currentElement)
private static void AddErrorAndStringsImportForEnums(CodeElement currentElement)
{
if (currentElement is CodeEnum currentEnum)
{
currentEnum.AddUsing(new CodeUsing
{
Name = "errors",
});

if (currentEnum.Flags)
{
currentEnum.AddUsing(new CodeUsing
{
Name = "strings",
});
}
}
CrawlTree(currentElement, AddErrorImportForEnums);
CrawlTree(currentElement, AddErrorAndStringsImportForEnums);
}
private static readonly GoConventionService conventions = new();
private static readonly HashSet<string> typeToSkipStrConv = new(StringComparer.OrdinalIgnoreCase) {
Expand Down
122 changes: 92 additions & 30 deletions src/Kiota.Builder/Writers/Go/CodeEnumWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ public override void WriteCodeElement(CodeEnum codeElement, LanguageWriter write
if (codeElement.Parent is CodeNamespace ns)
writer.WriteLine($"package {ns.Name.GetLastNamespaceSegment().Replace("-", string.Empty, StringComparison.OrdinalIgnoreCase)}");

writer.WriteLine("import (");
writer.IncreaseIndent();
foreach (var cUsing in codeElement.Usings)
writer.StartBlock("import (");
foreach (var cUsing in codeElement.Usings.OrderBy(static x => x.Name, StringComparer.OrdinalIgnoreCase))
writer.WriteLine($"\"{cUsing.Name}\"");
writer.DecreaseIndent();
writer.WriteLine(")");
writer.CloseBlock(")");

var typeName = codeElement.Name.ToFirstCharacterUpperCase();
conventions.WriteShortDescription(codeElement.Documentation.Description, writer);
conventions.WriteDeprecation(codeElement, writer);
Expand All @@ -40,43 +39,106 @@ public override void WriteCodeElement(CodeEnum codeElement, LanguageWriter write
iotaSuffix = string.Empty;
}
writer.DecreaseIndent();
writer.WriteLines(")",
string.Empty,
$"func (i {typeName}) String() string {{");
writer.IncreaseIndent();
var literalOptions = enumOptions
.Select(x => $"\"{x.WireName}\"")
.Aggregate((x, y) => x + ", " + y);
writer.WriteLine($"return []string{{{literalOptions}}}[i]");
writer.DecreaseIndent();
writer.WriteLines("}",
$"func Parse{typeName}(v string) (any, error) {{");
writer.IncreaseIndent();
writer.WriteLine($"result := {enumOptions.First().Name.ToUpperInvariant()}_{typeName.ToUpperInvariant()}");
writer.WriteLine("switch v {");
writer.IncreaseIndent();
foreach (var item in enumOptions)
writer.WriteLines(")", string.Empty);

var isMultiValue = codeElement.Flags;
WriteStringFunction(codeElement, writer, isMultiValue);
WriteParsableEnum(codeElement, writer, isMultiValue);
WriteSerializeFunction(codeElement, writer, isMultiValue);
WriteMultiValueFunction(codeElement, writer, isMultiValue);
}

private void WriteStringFunction(CodeEnum codeElement, LanguageWriter writer, Boolean isMultiValue)
{
var typeName = codeElement.Name.ToFirstCharacterUpperCase();
var enumOptions = codeElement.Options;

if (isMultiValue)
{
writer.WriteLine($"case \"{item.WireName}\":");
writer.IncreaseIndent();
writer.WriteLine($"result = {item.Name.ToUpperInvariant()}_{typeName.ToUpperInvariant()}");
writer.DecreaseIndent();
writer.StartBlock($"func (i {typeName}) String() string {{");
writer.WriteLine("var values []string");
var literalOptions = enumOptions
.Select(x => $"\"{x.WireName}\"")
.Aggregate((x, y) => x + ", " + y);
writer.StartBlock($"for p := {typeName}(1); p <= {enumOptions.Last().Name.ToUpperInvariant()}_{typeName.ToUpperInvariant()}; p <<= 1 {{");
writer.StartBlock($"if i&p == p {{");
writer.WriteLine($"values = append(values, []string{{{literalOptions}}}[p])");
writer.CloseBlock();
writer.CloseBlock();
writer.WriteLine("return strings.Join(values, \",\")");
writer.CloseBlock();
}
writer.WriteLine("default:");
writer.IncreaseIndent();
else
{
writer.StartBlock($"func (i {typeName}) String() string {{");
var literalOptions = enumOptions
.Select(x => $"\"{x.WireName}\"")
.Aggregate((x, y) => x + ", " + y);
writer.WriteLine($"return []string{{{literalOptions}}}[i]");
writer.CloseBlock();
}
}

private void WriteParsableEnum(CodeEnum codeElement, LanguageWriter writer, Boolean isMultiValue)
{
var typeName = codeElement.Name.ToFirstCharacterUpperCase();
var enumOptions = codeElement.Options;

writer.StartBlock($"func Parse{typeName}(v string) (any, error) {{");

if (isMultiValue)
{
writer.WriteLine($"var result {typeName}");
writer.WriteLine("values := strings.Split(v, \",\")");
writer.StartBlock("for _, str := range values {");
writer.StartBlock("switch str {");
foreach (var item in enumOptions)
{
writer.StartBlock($"case \"{item.WireName}\":");
writer.WriteLine($"result |= {item.Name.ToUpperInvariant()}_{typeName.ToUpperInvariant()}");
writer.DecreaseIndent();
}
}
else
{
writer.WriteLine($"result := {enumOptions.First().Name.ToUpperInvariant()}_{typeName.ToUpperInvariant()}");
writer.StartBlock("switch v {");
foreach (var item in enumOptions)
{
writer.StartBlock($"case \"{item.WireName}\":");
writer.WriteLine($"result = {item.Name.ToUpperInvariant()}_{typeName.ToUpperInvariant()}");
writer.DecreaseIndent();
}
}


writer.StartBlock("default:");
writer.WriteLine($"return 0, errors.New(\"Unknown {typeName} value: \" + v)");
writer.DecreaseIndent();
writer.CloseBlock();
if (isMultiValue) writer.CloseBlock();
writer.WriteLine("return &result, nil");
writer.CloseBlock();
writer.WriteLine($"func Serialize{typeName}(values []{typeName}) []string {{");
writer.IncreaseIndent();
}

private void WriteSerializeFunction(CodeEnum codeElement, LanguageWriter writer, Boolean isMultiValue)
{
var typeName = codeElement.Name.ToFirstCharacterUpperCase();
writer.StartBlock($"func Serialize{typeName}(values []{typeName}) []string {{");
writer.WriteLines("result := make([]string, len(values))",
"for i, v := range values {");
"for i, v := range values {");
writer.IncreaseIndent();
writer.WriteLine("result[i] = v.String()");
writer.CloseBlock();
writer.WriteLine("return result");
writer.CloseBlock();
}

private void WriteMultiValueFunction(CodeEnum codeElement, LanguageWriter writer, Boolean isMultiValue)
{
var typeName = codeElement.Name.ToFirstCharacterUpperCase();
writer.StartBlock($"func (i {typeName}) isMultiValue() bool {{");
writer.WriteLine($"return {isMultiValue.ToString().ToLowerInvariant()}");
writer.CloseBlock();
}
}
4 changes: 2 additions & 2 deletions src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -901,9 +901,9 @@ private string GetDeserializationMethodName(CodeTypeBase propType, CodeClass par
return $"GetCollectionOfEnumValues({conventions.GetImportedStaticMethodName(propType, parentClass, "Parse")})";
else
return $"GetCollectionOfObjectValues({GetTypeFactory(propType, parentClass, propertyTypeNameWithoutImportSymbol)})";
if (currentType.TypeDefinition is CodeEnum currentEnum)
if (currentType.TypeDefinition is CodeEnum)
{
return $"GetEnum{(currentEnum.Flags ? "Set" : string.Empty)}Value({conventions.GetImportedStaticMethodName(propType, parentClass, "Parse")})";
return $"GetEnumValue({conventions.GetImportedStaticMethodName(propType, parentClass, "Parse")})";
}
}
return propertyTypeNameWithoutImportSymbol switch
Expand Down
19 changes: 16 additions & 3 deletions tests/Kiota.Builder.Tests/Refiners/GoLanguageRefinerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -817,15 +817,28 @@ public async Task ReplacesRequestBuilderPropertiesByMethods()
Assert.Single(model.Methods.Where(x => x.IsOfKind(CodeMethodKind.RequestBuilderBackwardCompatibility)));
}
[Fact]
public async Task AddsErrorImportForEnums()
public async Task AddsErrorImportForEnumsForMultiValueEnum()
{
var testEnum = root.AddEnum(new CodeEnum
{
Name = "TestEnum",

Flags = true
}).First();
await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.Go }, root);
Assert.Single(testEnum.Usings.Where(static x => "errors".Equals(x.Name, StringComparison.Ordinal)));
Assert.Single(testEnum.Usings.Where(static x => "strings".Equals(x.Name, StringComparison.Ordinal)));
}
[Fact]
public async Task AddsErrorImportForEnumsForSingleValueEnum()
{
var testEnum = root.AddEnum(new CodeEnum
{
Name = "TestEnum",
Flags = false
}).First();
await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.Go }, root);
Assert.Single(testEnum.Usings.Where(x => x.Name == "errors"));
Assert.Single(testEnum.Usings.Where(static x => "errors".Equals(x.Name, StringComparison.Ordinal)));
Assert.Empty(testEnum.Usings.Where(static x => "strings".Equals(x.Name, StringComparison.Ordinal)));
}
[Fact]
public async Task CorrectsCoreType()
Expand Down
45 changes: 43 additions & 2 deletions tests/Kiota.Builder.Tests/Writers/Go/CodeEnumWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void Dispose()
GC.SuppressFinalize(this);
}
[Fact]
public void WritesEnum()
public void WritesSingleValueEnum()
{
const string optionName = "option1";
currentEnum.AddOption(new CodeEnumOption { Name = optionName });
Expand All @@ -45,7 +45,8 @@ public void WritesEnum()
Assert.Contains($"{EnumName.ToFirstCharacterUpperCase()} = iota", result);
Assert.Contains("func (i", result);
Assert.Contains("String() string {", result);
Assert.Contains("return []string{", result);
Assert.Contains("return []string{\"option1\"}[i]", result);
Assert.Contains("result = OPTION1_SOMEENUM", result);
Assert.Contains("[i]", result);
Assert.Contains("func Parse", result);
Assert.Contains("(v string) (any, error)", result);
Expand All @@ -56,6 +57,46 @@ public void WritesEnum()
Assert.Contains("return 0, errors.New(\"Unknown ", result);
AssertExtensions.CurlyBracesAreClosed(result);
Assert.Contains(optionName.ToUpperInvariant(), result);
Assert.Contains("func (i SomeEnum) isMultiValue() bool {", result);
Assert.Contains("return false", result);
}
[Fact]
public void WritesMultiValueEnum()
{
var root = CodeNamespace.InitRootNamespace();
var myEnum = root.AddEnum(new CodeEnum
{
Name = "MultiValueEnum",
Flags = true
}).First();
const string optionName = "option1";
myEnum.AddOption(new CodeEnumOption { Name = optionName });
writer.Write(myEnum);

var result = tw.ToString();
Assert.Contains($"type MultiValueEnum int", result);
Assert.Contains("const (", result);
Assert.Contains("OPTION1_MULTIVALUEENUM MultiValueEnum = iota", result);
Assert.Contains("func (i", result);
Assert.Contains("String() string {", result);
Assert.Contains("for p := MultiValueEnum(1); p <= OPTION1_MULTIVALUEENUM; p <<= 1 {", result);
Assert.Contains("if i&p == p {", result);
Assert.Contains("values = append(values, []string{\"option1\"}[p])", result);
Assert.Contains("for _, str := range values {", result);
Assert.Contains("strings.Join(values", result);
Assert.Contains("result |= OPTION1_MULTIVALUEENUM", result);
Assert.Contains("[i]", result);
Assert.Contains("func Parse", result);
Assert.Contains("(v string) (any, error)", result);
Assert.Contains("switch str", result);
Assert.Contains("default", result);
Assert.Contains("result :=", result);
Assert.Contains("return &result, nil", result);
Assert.Contains("return 0, errors.New(\"Unknown ", result);
Assert.Contains("func (i MultiValueEnum) isMultiValue() bool {", result);
Assert.Contains("return true", result);
AssertExtensions.CurlyBracesAreClosed(result);
Assert.Contains(optionName.ToUpperInvariant(), result);
}
[Fact]
public void DoesntWriteAnythingOnNoOption()
Expand Down