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

feature/code reduction #2406

Merged
merged 19 commits into from
Mar 23, 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
4 changes: 2 additions & 2 deletions docs/support.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Major releases include new features, new public API surface area, and bug fixes.

### Minor releases

Minor releases also include new features, public API surface area, and bug fixes. The difference between these and major releases is that minor releases will not contain breaking changes to the tooling experience or the generated API surface area of GA maturity languages. Minor releases can install side by side with previous minor releases. Minor releases are targeted to publish on the first Tuesday of every month.
Minor releases also include new features, public API surface area, and bug fixes. The difference between these and major releases is that minor releases will not contain breaking changes to the tooling experience or the generated API surface area of stable maturity languages. Minor releases can install side by side with previous minor releases. Minor releases are targeted to publish on the first Tuesday of every month.

### Patches

Expand All @@ -33,7 +33,7 @@ The following criteria is used to determine the maturity levels.
- **Preview**: Kiota is at or near the level of table but hasn't been used to generate production API clients.
- **Experimental**: Kiota provides some functionality for the language but is still in early stages. Some features may not work correctly or at all.

Breaking changes to languages that are stable will result in major version change for Kiota tooling.
Breaking changes to languages that are stable will result in major version change for Kiota tooling. Code generation changes to a stable maturity language are not considered breaking when they rely on additions in the corresponding abstractions library as these changes will only require to update the abstractions library the the latest minor/patch version under the same major version.

The current status of language support can be queried by using the following command.

Expand Down
4 changes: 4 additions & 0 deletions src/Kiota.Builder/CodeDOM/CodeProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public class CodeProperty : CodeTerminalWithKind<CodePropertyKind>, IDocumentedE
public bool ReadOnly { get; set; } = false;
public AccessModifier Access { get; set; } = AccessModifier.Public;
public bool ExistsInBaseType => OriginalPropertyFromBaseType != null;
public bool ExistsInExternalBaseType
{
get; set;
}
public CodeMethod? Getter
{
get; set;
Expand Down
5 changes: 4 additions & 1 deletion src/Kiota.Builder/CodeDOM/CodeType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

namespace Kiota.Builder.CodeDOM;
public class CodeType : CodeTypeBase, ICloneable
Expand All @@ -18,7 +19,9 @@ public override object Clone()
return new CodeType
{
TypeDefinition = TypeDefinition, // not cloning the type definition as it's a code element that lives in the tree and we don't want to fork the tree
IsExternal = IsExternal
IsExternal = IsExternal,
GenericTypeParameterValues = new(GenericTypeParameterValues),
}.BaseClone<CodeType>(this);
}
public List<CodeType> GenericTypeParameterValues { get; set; } = new();
}
8 changes: 8 additions & 0 deletions src/Kiota.Builder/CodeDOM/ProprietableBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ public virtual IEnumerable<CodeProperty> AddProperty(params CodeProperty[] prope
throw new ArgumentOutOfRangeException(nameof(properties));
return AddRange(properties);
}
public void RemovePropertiesOfKind(params CodePropertyKind[] kind)
{
if (kind == null || !kind.Any())
throw new ArgumentNullException(nameof(kind));
var propertiesToRemove = Properties.Where(x => x.IsOfKind(kind)).ToList();
foreach (var property in propertiesToRemove)
RemoveChildElement(property);
}
#nullable disable
public T Kind
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;

using Kiota.Builder.CodeDOM;

namespace Kiota.Builder.Extensions;

public static class CodePropertiesEnumerableExtensions
{
public static CodeProperty? OfKind(this IEnumerable<CodeProperty> properties, CodePropertyKind kind)
{
ArgumentNullException.ThrowIfNull(properties);
return properties.FirstOrDefault(x => x != null && x.IsOfKind(kind));
}
}
31 changes: 31 additions & 0 deletions src/Kiota.Builder/Refiners/CSharpRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,32 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
return Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
MoveRequestBuilderPropertiesToBaseType(generatedCode,
new CodeUsing
{
Name = "BaseRequestBuilder",
Declaration = new CodeType
{
Name = "Microsoft.Kiota.Abstractions",
IsExternal = true
}
});
//TODO uncomment on the next major version
// RemoveRequestConfigurationClasses(generatedCode,
// new CodeUsing
// {
// Name = "RequestConfiguration",
// Declaration = new CodeType
// {
// Name = "Microsoft.Kiota.Abstractions",
// IsExternal = true
// }
// },
// new CodeType
// {
// Name = "DefaultQueryParameters",
// IsExternal = true,
// });
AddDefaultImports(generatedCode, defaultUsingEvaluators);
CorrectCoreType(generatedCode, CorrectMethodType, CorrectPropertyType);
MoveClassesWithNamespaceNamesUnderNamespace(generatedCode);
Expand All @@ -40,6 +66,11 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
new CSharpReservedNamesProvider(), x => $"@{x.ToFirstCharacterUpperCase()}",
new HashSet<Type> { typeof(CodeClass), typeof(ClassDeclaration), typeof(CodeProperty), typeof(CodeUsing), typeof(CodeNamespace), typeof(CodeMethod), typeof(CodeEnum) }
);
ReplaceReservedNames(
generatedCode,
new CSharpReservedClassNamesProvider(),
x => $"{x.ToFirstCharacterUpperCase()}Escaped"
);
ReplaceReservedExceptionPropertyNames(
generatedCode,
new CSharpExceptionsReservedNamesProvider(),
Expand Down
13 changes: 13 additions & 0 deletions src/Kiota.Builder/Refiners/CSharpReservedClassNamesProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;

namespace Kiota.Builder.Refiners;

public class CSharpReservedClassNamesProvider : IReservedNamesProvider
{
private readonly Lazy<HashSet<string>> _reservedNames = new(static () => new(StringComparer.OrdinalIgnoreCase)
{
"BaseRequestBuilder",
});
public HashSet<string> ReservedNames => _reservedNames.Value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public class CSharpReservedNamesProvider : IReservedNamesProvider
"when",
"while",
"with",
"yield"
"yield",
});
public HashSet<string> ReservedNames => _reservedNames.Value;
}
78 changes: 76 additions & 2 deletions src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ protected static void ReplaceReservedNames(CodeElement current, IReservedNamesPr
// in the CodeNamespace if-block so we also need to update the using references
if (!codeElementExceptions?.Contains(typeof(CodeNamespace)) ?? true)
ReplaceReservedCodeUsingNamespaceSegmentNames(currentDeclaration, provider, replacement);
if (currentDeclaration.Inherits?.Name is string inheritName && provider.ReservedNames.Contains(inheritName))
if (currentDeclaration.Inherits?.Name is string inheritName && provider.ReservedNames.Contains(inheritName) && (currentDeclaration.Inherits is not CodeType inheritType || !inheritType.IsExternal))
currentDeclaration.Inherits.Name = replacement(currentDeclaration.Inherits.Name);
if (currentClass.DiscriminatorInformation.DiscriminatorMappings.Select(static x => x.Value.Name).Any(provider.ReservedNames.Contains))
ReplaceMappingNames(currentClass.DiscriminatorInformation.DiscriminatorMappings, provider, replacement);
Expand Down Expand Up @@ -468,7 +468,7 @@ private static IEnumerable<CodeUsing> usingSelector(AdditionalUsingEvaluator x)
protected static void AddDefaultImports(CodeElement current, IEnumerable<AdditionalUsingEvaluator> evaluators)
{
var usingsToAdd = evaluators.Where(x => x.CodeElementEvaluator.Invoke(current))
.SelectMany(x => usingSelector(x))
.SelectMany(usingSelector)
.ToArray();
if (usingsToAdd.Any())
{
Expand Down Expand Up @@ -1407,4 +1407,78 @@ protected void RemoveHandlerFromRequestBuilder(CodeElement currentElement)

CrawlTree(currentElement, RemoveHandlerFromRequestBuilder);
}
protected static void MoveRequestBuilderPropertiesToBaseType(CodeElement currentElement, CodeUsing baseTypeUsing, AccessModifier? accessModifier = null)
{
if (currentElement is CodeClass currentClass && currentClass.IsOfKind(CodeClassKind.RequestBuilder))
{
if (currentClass.StartBlock.Inherits == null)
{
currentClass.StartBlock.Inherits = new CodeType
{
Name = baseTypeUsing.Name,
IsExternal = true,
};
currentClass.AddUsing(baseTypeUsing);
}

var properties = currentClass.Properties.Where(static x => x.IsOfKind(CodePropertyKind.PathParameters, CodePropertyKind.UrlTemplate, CodePropertyKind.RequestAdapter));
foreach (var property in properties)
{
property.ExistsInExternalBaseType = true;
if (accessModifier.HasValue)
property.Access = accessModifier.Value;
}
}

CrawlTree(currentElement, x => MoveRequestBuilderPropertiesToBaseType(x, baseTypeUsing, accessModifier));
}
protected static void RemoveRequestConfigurationClassesCommonProperties(CodeElement currentElement, CodeUsing baseTypeUsing)
{
if (currentElement is CodeClass currentClass && currentClass.IsOfKind(CodeClassKind.RequestConfiguration))
{
if (currentClass.StartBlock.Inherits == null)
{
currentClass.StartBlock.Inherits = new CodeType
{
Name = baseTypeUsing.Name,
IsExternal = true,
};
currentClass.AddUsing(baseTypeUsing);
}
currentClass.RemovePropertiesOfKind(CodePropertyKind.Headers, CodePropertyKind.Options);
}

CrawlTree(currentElement, x => RemoveRequestConfigurationClassesCommonProperties(x, baseTypeUsing));
}
protected static void RemoveRequestConfigurationClasses(CodeElement currentElement, CodeUsing? configurationParameterTypeUsing = null, CodeType? defaultValueForGenericTypeParam = null)
{
if (currentElement is CodeClass currentClass && currentClass.IsOfKind(CodeClassKind.RequestConfiguration) &&
currentClass.Parent is CodeClass parentClass)
{
parentClass.RemoveChildElement(currentClass);
var configurationParameters = parentClass.Methods
.SelectMany(static x => x.Parameters)
.Where(x => x.IsOfKind(CodeParameterKind.RequestConfiguration) && x.Type is CodeType type && type.TypeDefinition == currentClass)
.ToArray();
var genericTypeParamValue = currentClass.Properties.OfKind(CodePropertyKind.QueryParameters)?.Type as CodeType ?? defaultValueForGenericTypeParam;
if (configurationParameterTypeUsing != null && genericTypeParamValue != null && configurationParameters.Any())
{
parentClass.AddUsing(configurationParameterTypeUsing);
var configurationParameterType = new CodeType
{
Name = configurationParameterTypeUsing.Name,
IsExternal = true,
};
foreach (var configurationParameter in configurationParameters)
{
var newType = (CodeType)configurationParameterType.Clone();
newType.ActionOf = configurationParameter.Type.ActionOf;
newType.GenericTypeParameterValues.Add(genericTypeParamValue);
configurationParameter.Type = newType;
}
}
}

CrawlTree(currentElement, x => RemoveRequestConfigurationClasses(x, configurationParameterTypeUsing, defaultValueForGenericTypeParam));
}
}
15 changes: 13 additions & 2 deletions src/Kiota.Builder/Refiners/GoRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
return Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
MoveRequestBuilderPropertiesToBaseType(generatedCode,
new CodeUsing
{
Name = "BaseRequestBuilder",
Declaration = new CodeType
{
Name = "github.com/microsoft/kiota-abstractions-go",
IsExternal = true
}
},
accessModifier: AccessModifier.Public);
ReplaceIndexersByMethodsWithParameter(
generatedCode,
false,
Expand All @@ -29,8 +40,8 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
generatedCode,
true,
string.Empty,
false,
MergeOverLappedStrings);
false,
MergeOverLappedStrings);
RenameCancellationParameter(generatedCode);
RemoveDiscriminatorMappingsTargetingSubNamespaces(generatedCode);
cancellationToken.ThrowIfCancellationRequested();
Expand Down
3 changes: 2 additions & 1 deletion src/Kiota.Builder/Refiners/GoReservedNamesProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public class GoReservedNamesProvider : IReservedNamesProvider
"switch",
"type",
"var",
"vendor" // cannot be used as a package name
"vendor", // cannot be used as a package name
"BaseRequestBuilder"
});
public HashSet<string> ReservedNames => _reservedNames.Value;
}
33 changes: 20 additions & 13 deletions src/Kiota.Builder/Refiners/JavaRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
{
cancellationToken.ThrowIfCancellationRequested();
LowerCaseNamespaceNames(generatedCode);
MoveRequestBuilderPropertiesToBaseType(generatedCode,
new CodeUsing
{
Name = "BaseRequestBuilder",
Declaration = new CodeType
{
Name = "com.microsoft.kiota",
IsExternal = true
}
});
RemoveRequestConfigurationClassesCommonProperties(generatedCode,
new CodeUsing
{
Name = "BaseRequestConfiguration",
Declaration = new CodeType
{
Name = "com.microsoft.kiota",
IsExternal = true
}
});
var reservedNamesProvider = new JavaReservedNamesProvider();
CorrectNames(generatedCode, s =>
{
Expand Down Expand Up @@ -206,16 +226,12 @@ private static void AddEnumSetImport(CodeElement currentElement)
"com.microsoft.kiota.store", "BackingStoreFactory", "BackingStoreFactorySingleton"),
new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.BackingStore),
"com.microsoft.kiota.store", "BackingStore", "BackedModel", "BackingStoreFactorySingleton"),
new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.Options),
"java.util", "Collections"),
new (static x => x is CodeProperty prop && "decimal".Equals(prop.Type.Name, StringComparison.OrdinalIgnoreCase) ||
x is CodeMethod method && "decimal".Equals(method.ReturnType.Name, StringComparison.OrdinalIgnoreCase) ||
x is CodeParameter para && "decimal".Equals(para.Type.Name, StringComparison.OrdinalIgnoreCase),
"java.math", "BigDecimal"),
new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.QueryParameter) && !string.IsNullOrEmpty(prop.SerializationName),
"com.microsoft.kiota", "QueryParameter"),
new (static x => x is CodeProperty prop && prop.IsOfKind(CodePropertyKind.Headers),
"com.microsoft.kiota", "RequestHeaders"),
new (static x => x is CodeClass @class && @class.OriginalComposedType is CodeIntersectionType intersectionType && intersectionType.Types.Any(static y => !y.IsExternal),
"com.microsoft.kiota.serialization", "ParseNodeHelper"),
};
Expand All @@ -228,15 +244,6 @@ private static void CorrectPropertyType(CodeProperty currentProperty)
}
else if (currentProperty.IsOfKind(CodePropertyKind.BackingStore))
currentProperty.Type.Name = currentProperty.Type.Name[1..]; // removing the "I"
else if (currentProperty.IsOfKind(CodePropertyKind.Options))
{
currentProperty.Type.Name = "java.util.List<RequestOption>"; //fully qualified name to avoid conflict with generated types
currentProperty.DefaultValue = "Collections.emptyList()";
}
else if (currentProperty.IsOfKind(CodePropertyKind.Headers))
{
currentProperty.DefaultValue = $"new {currentProperty.Type.Name.ToFirstCharacterUpperCase()}()";
}
else if (currentProperty.IsOfKind(CodePropertyKind.QueryParameter))
currentProperty.DefaultValue = $"new {currentProperty.Type.Name.ToFirstCharacterUpperCase()}()";
else if (currentProperty.IsOfKind(CodePropertyKind.AdditionalData))
Expand Down
1 change: 1 addition & 0 deletions src/Kiota.Builder/Refiners/JavaReservedNamesProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public class JavaReservedNamesProvider : IReservedNamesProvider
"volatile",
"wait",
"while",
"BaseRequestBuilder",
});
public HashSet<string> ReservedNames => _reservedNames.Value;
}
11 changes: 11 additions & 0 deletions src/Kiota.Builder/Refiners/RubyRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance
{
cancellationToken.ThrowIfCancellationRequested();
ReplaceIndexersByMethodsWithParameter(generatedCode, false, "_by_id");
MoveRequestBuilderPropertiesToBaseType(generatedCode,
new CodeUsing
{
Name = "MicrosoftKiotaAbstractions::BaseRequestBuilder",
Declaration = new CodeType
{
Name = "MicrosoftKiotaAbstractions",
IsExternal = true
}
});
RemoveRequestConfigurationClasses(generatedCode);
var classesToDisambiguate = new HashSet<CodeClass>();
var suffix = "Model";
DisambiguateClassesWithNamespaceNames(generatedCode, classesToDisambiguate, suffix);
Expand Down
1 change: 1 addition & 0 deletions src/Kiota.Builder/Refiners/RubyReservedNamesProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class RubyReservedNamesProvider : IReservedNamesProvider
"while",
"defined?",
"self",
"BaseRequestBuilder",
});
public HashSet<string> ReservedNames => _reservedNames.Value;
}
Loading