Skip to content

Commit

Permalink
Add go backing store support. (#2185)
Browse files Browse the repository at this point in the history
* Add go backing store support
  • Loading branch information
rkodev authored Jan 27, 2023
1 parent 0ae2bd3 commit e40dc60
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 19 deletions.
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Added support for external documentation links on request execution methods (PHP Generation). [2038](https://github.com/microsoft/kiota/issues/2138)
- Adds support for nullable reference types in dotnet for projects running Netstandard 2.1/Net 6.0 and above []()
- Added support for nullable reference types in dotnet for projects running Netstandard 2.1/Net 6.0 and above [2073](https://github.com/microsoft/kiota/issues/2073)
- Added support for multi-value headers to CLI generation. (Shell)
- Add support for multi-value headers for PHP Generation. [#2052](https://github.com/microsoft/kiota/issues/2052)
- Add support for Composed types (De)Serialization for PHP Generation. [#1814](https://github.com/microsoft/kiota/issues/1814)
- Added support for multi-value headers for PHP Generation. [#2052](https://github.com/microsoft/kiota/issues/2052)
- Added support for Composed types (De)Serialization for PHP Generation. [#1814](https://github.com/microsoft/kiota/issues/1814)
- Added support for backing store in Go. [466](https://github.com/microsoft/kiota/issues/466)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/KiotaBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1351,7 +1351,7 @@ private void CreatePropertiesForModelClass(OpenApiUrlTreeNode currentNode, OpenA
private const string AdditionalDataPropName = "AdditionalData";
private const string BackingStorePropertyName = "BackingStore";
private const string BackingStoreInterface = "IBackingStore";
private const string BackedModelInterface = "IBackedModel";
internal const string BackedModelInterface = "IBackedModel";
private const string ParseNodeInterface = "IParseNode";
internal const string AdditionalHolderInterface = "IAdditionalDataHolder";
internal static void AddSerializationMembers(CodeClass model, bool includeAdditionalProperties, bool usesBackingStore) {
Expand Down
9 changes: 6 additions & 3 deletions src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,21 @@ private static bool ReplaceSerializationModules(CodeElement generatedCode, Func<

return false;
}
protected static void CorrectCoreTypesForBackingStore(CodeElement currentElement, string defaultPropertyValue) {
protected static void CorrectCoreTypesForBackingStore(CodeElement currentElement, string defaultPropertyValue, Boolean hasPrefix = true) {
if(currentElement is CodeClass currentClass && currentClass.IsOfKind(CodeClassKind.Model, CodeClassKind.RequestBuilder)
&& currentClass.StartBlock is ClassDeclaration currentDeclaration) {
var backedModelImplements = currentDeclaration.Implements.FirstOrDefault(x => "IBackedModel".Equals(x.Name, StringComparison.OrdinalIgnoreCase));
if(backedModelImplements != null)
backedModelImplements.Name = backedModelImplements.Name[1..]; //removing the "I"
var backingStoreProperty = currentClass.GetPropertyOfKind(CodePropertyKind.BackingStore);
if(backingStoreProperty != null)
if (backingStoreProperty != null)
{
backingStoreProperty.DefaultValue = defaultPropertyValue;
backingStoreProperty.NamePrefix = hasPrefix ? backingStoreProperty.NamePrefix : String.Empty;
}

}
CrawlTree(currentElement, x => CorrectCoreTypesForBackingStore(x, defaultPropertyValue));
CrawlTree(currentElement, x => CorrectCoreTypesForBackingStore(x, defaultPropertyValue, hasPrefix));
}
private static bool DoesAnyParentHaveAPropertyWithDefaultValue(CodeClass current)
{
Expand Down
66 changes: 64 additions & 2 deletions src/Kiota.Builder/Refiners/GoRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Kiota.Builder.CodeDOM;
using Kiota.Builder.Configuration;
using Kiota.Builder.Extensions;
Expand Down Expand Up @@ -139,6 +140,8 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
RemoveHandlerFromRequestBuilder(generatedCode);
AddContextParameterToGeneratorMethods(generatedCode);
CorrectTypes(generatedCode);
CorrectCoreTypesForBackingStore(generatedCode, $"{conventions.StoreHash}.BackingStoreFactoryInstance()", false);
CorrectBackingStoreTypes(generatedCode);
}, cancellationToken);
}

Expand All @@ -154,6 +157,59 @@ private string MergeOverLappedStrings(string start, string end)

return $"{start}{end}";
}

private void CorrectBackingStoreTypes(CodeElement currentElement, Dictionary<String, CodeInterface> result = null)
{
if (!_configuration.UsesBackingStore)
return;

var currentMethod = currentElement as CodeMethod;
var parameters = currentMethod?.Parameters;
var codeParameters = parameters as CodeParameter[] ?? parameters?.ToArray();
codeParameters?.Where(x => x.IsOfKind(CodeParameterKind.BackingStore)
&& currentMethod.IsOfKind(CodeMethodKind.ClientConstructor)).ToList().ForEach(x =>
{
var type = (x.Type as CodeType)!;
type.Name = "BackingStoreFactory";
type.IsNullable = false;
type.IsExternal = true;
});

if (currentElement is CodeClass codeClass && codeClass.IsOfKind(CodeClassKind.Model))
{
var propertiesToCorrect = codeClass.Properties
.Where(x => x.IsOfKind(CodePropertyKind.Custom))
.Union(codeClass.Methods
.Where(x => x.IsAccessor && (x.AccessedProperty?.IsOfKind(CodePropertyKind.Custom) ?? false))
.Select(static x => x.AccessedProperty))
.Distinct()
.OrderBy(static x => x.Name, StringComparer.OrdinalIgnoreCase);

var targetNameSpace = codeClass.GetImmediateParentOfType<CodeNamespace>();
var modelsNameSpace = findClientNameSpace(targetNameSpace)
.FindNamespaceByName($"{_configuration.ClientNamespaceName}.models");

foreach (var property in propertiesToCorrect)
{
if (property.Type is CodeType codeType && codeType.TypeDefinition is CodeClass)
{
var interfaceName = $"{codeType.Name}able";
var existing = targetNameSpace.FindChildByName<CodeInterface>(interfaceName, false) ??
modelsNameSpace.FindChildByName<CodeInterface>(interfaceName) ??
modelsNameSpace.FindChildByName<CodeInterface>(interfaceName.ToFirstCharacterUpperCase());

if (existing == null)
continue;

CodeType type = (codeType.Clone() as CodeType)!;
type.Name = interfaceName;
type.TypeDefinition = existing;
property.Type = type;
}
}
}
CrawlTree(currentElement, x => CorrectBackingStoreTypes(x, result));
}

private static void CorrectTypes(CodeElement currentElement)
{
Expand Down Expand Up @@ -414,9 +470,15 @@ x.Type is CodeType pType &&
"github.com/microsoft/kiota-abstractions-go/serialization", "MergeDeserializersForIntersectionWrapper"),
new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.Headers),
"github.com/microsoft/kiota-abstractions-go", "RequestHeaders"),
};//TODO add backing store types once we have them defined
private static void CorrectImplements(ProprietableBlockDeclaration block) {
new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.BackingStore), "github.com/microsoft/kiota-abstractions-go/store","BackingStore"),
new (static x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.ClientConstructor) &&
method.Parameters.Any(y => y.IsOfKind(CodeParameterKind.BackingStore)),
"github.com/microsoft/kiota-abstractions-go/store", "BackingStoreFactory"),
};

private void CorrectImplements(ProprietableBlockDeclaration block) {
block.ReplaceImplementByName(KiotaBuilder.AdditionalHolderInterface, "AdditionalDataHolder");
block.ReplaceImplementByName(KiotaBuilder.BackedModelInterface, "BackedModel");
}
private static void CorrectMethodType(CodeMethod currentMethod) {
var parentClass = currentMethod.Parent as CodeClass;
Expand Down
32 changes: 23 additions & 9 deletions src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,15 +381,26 @@ private void WriteGetterBody(CodeMethod codeElement, LanguageWriter writer, Code
if(!(codeElement.AccessedProperty?.Type?.IsNullable ?? true) &&
!(codeElement.AccessedProperty?.ReadOnly ?? true) &&
!string.IsNullOrEmpty(codeElement.AccessedProperty?.DefaultValue)) {
writer.WriteLines($"{conventions.GetTypeString(codeElement.AccessedProperty.Type, parentClass)} value = m.{backingStore.NamePrefix}{backingStore.Name.ToFirstCharacterLowerCase()}.Get(\"{codeElement.AccessedProperty.Name.ToFirstCharacterLowerCase()}\")",
"if value == nil {");
writer.IncreaseIndent();
writer.WriteLines($"value = {codeElement.AccessedProperty.DefaultValue};",
writer.WriteLines(
$"val , err := m.{backingStore.NamePrefix}{backingStore.Name.ToFirstCharacterLowerCase()}.Get(\"{codeElement.AccessedProperty.Name.ToFirstCharacterLowerCase()}\")");
writer.WriteBlock("if err != nil {", "}", "panic(err)");
writer.WriteBlock("if val == nil {" , "}",
$"var value = {codeElement.AccessedProperty.DefaultValue};",
$"m.Set{codeElement.AccessedProperty?.Name?.ToFirstCharacterUpperCase()}(value);");
writer.CloseBlock();
writer.WriteLine("return value;");
} else
writer.WriteLine($"return m.Get{backingStore.Name.ToFirstCharacterUpperCase()}().Get(\"{codeElement.AccessedProperty?.Name?.ToFirstCharacterLowerCase()}\");");

writer.WriteLine($"return val.({conventions.GetTypeString(codeElement.AccessedProperty.Type, parentClass)})");
}
else
{
var returnType = conventions.GetTypeString(codeElement.ReturnType, codeElement.Parent);

writer.WriteLine($"val , err := m.Get{backingStore.Name.ToFirstCharacterUpperCase()}().Get(\"{codeElement.AccessedProperty?.Name?.ToFirstCharacterLowerCase()}\")");

writer.WriteBlock("if val != nil {", "}", $"return val.({returnType})");
writer.WriteBlock("if err != nil {", "}", "panic(err)");

writer.WriteLine("return nil");
}
}
private void WriteApiConstructorBody(CodeClass parentClass, CodeMethod method, LanguageWriter writer) {
var requestAdapterProperty = parentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter);
Expand Down Expand Up @@ -483,7 +494,10 @@ private static void WriteSetterBody(CodeMethod codeElement, LanguageWriter write
if(backingStore == null)
writer.WriteLine($"m.{codeElement.AccessedProperty?.Name?.ToFirstCharacterLowerCase()} = value");
else
writer.WriteLine($"m.Get{backingStore.Name.ToFirstCharacterUpperCase()}().Set(\"{codeElement.AccessedProperty?.Name?.ToFirstCharacterLowerCase()}\", value)");
{
writer.WriteLine($"err := m.Get{backingStore.Name.ToFirstCharacterUpperCase()}().Set(\"{codeElement.AccessedProperty?.Name?.ToFirstCharacterLowerCase()}\", value)");
writer.WriteBlock("if err != nil {", "}", "panic(err)");
}
}
private void WriteIndexerBody(CodeMethod codeElement, CodeClass parentClass, LanguageWriter writer, string returnType) {
var pathParametersProperty = parentClass.GetPropertyOfKind(CodePropertyKind.PathParameters);
Expand Down
2 changes: 2 additions & 0 deletions src/Kiota.Builder/Writers/Go/GoConventionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class GoConventionService : CommonLanguageConventionService
#pragma warning disable CA1822 // Method should be static
public string AbstractionsHash => "i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f";
public string SerializationHash => "i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91";
public string StoreHash => "ie8677ce2c7e1b4c22e9c3827ecd078d41185424dd9eeb92b7d971ed2d49a392e";
public string StringsHash => "ie967d16dae74a49b5e0e051225c5dac0d76e5e38f13dd1628028cbce108c25b6";

public string ContextVarTypeName => "context.Context";
Expand Down Expand Up @@ -94,6 +95,7 @@ public string TranslateType(CodeTypeBase type, bool includeImportSymbol)
"string" or "float32" or "float64" or "int32" or "int64" => type.Name,
"String" or "Int64" or "Int32" or "Float32" or "Float64" => type.Name.ToFirstCharacterLowerCase(), //casing hack
"context.Context" => "context.Context",
"BackedModel" => $"{StoreHash}.BackedModel",
_ => type.Name.ToFirstCharacterUpperCase() ?? "Object",
};
}
Expand Down
7 changes: 7 additions & 0 deletions src/Kiota.Builder/Writers/LanguageWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ internal void CloseBlock(string symbol = "}", bool decreaseIndent = true)
WriteLine(symbol);
}

internal void WriteBlock(string startSymbol = "{", string closeSymbol = "}", params string[] lines)
{
StartBlock(startSymbol);
WriteLines(lines);
CloseBlock(closeSymbol);
}

internal void Write(string text, bool includeIndent = true)
{
writer.Write(includeIndent ? GetIndent() + text : text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ public void WritesGetterToBackingStoreWithNonnullProperty() {
method.Kind = CodeMethodKind.Getter;
writer.Write(method);
var result = tw.ToString();
Assert.Contains("if value == nil", result);
Assert.Contains("if val == nil", result);
Assert.Contains(defaultValue, result);
}
[Fact]
Expand Down

0 comments on commit e40dc60

Please sign in to comment.