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

Add go backing store support. #2185

Merged
merged 7 commits into from
Jan 27, 2023
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
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