Skip to content

Commit

Permalink
Merge pull request #2406 from microsoft/feature/code-reduction
Browse files Browse the repository at this point in the history
feature/code reduction
  • Loading branch information
baywet authored Mar 23, 2023
2 parents 56d6f26 + 925f333 commit 3dfa284
Show file tree
Hide file tree
Showing 40 changed files with 507 additions and 276 deletions.
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
16 changes: 16 additions & 0 deletions src/Kiota.Builder/Extensions/CodePropertiesEnumerableExtensions.cs
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;
}
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Refiners/CSharpReservedNamesProvider.cs
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 @@ -346,7 +346,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 @@ -478,7 +478,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 @@ -1431,4 +1431,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

0 comments on commit 3dfa284

Please sign in to comment.