Description
What are you generating using Kiota, clients or plugins?
API Client/SDK
In what context or format are you using Kiota?
Nuget tool
Client library/SDK language
Csharp
Describe the bug
I am trying to use openapi.json from our supplier. The issue is that they are using paths with a similar structure (/path/{param}, /path/{param}/suffix) but with different types (string/uuid). This causes the resulting client to be wrong.
Expected behavior
Two indexers/With methods with different parameter types to be generated.
How to reproduce
generate --log-level trace --openapi /opt/asp.net/path-openapi.json --exclude-backward-compatible --language csharp --disable-validation-rules KnownAndNotSupportedFormats --output /opt/asp.net/KiotaClient --class-name KiotaClient --namespace-name PathsClient
Open API description file
Kiota Version
1.27.0+278d4c17bfbc9fff8d8a1618bd95b43b1e3f7f4d
Latest Kiota version known to work for scenario above?(Not required)
No response
Known Workarounds
Manually modify the source openapi.json and unify parameter types (usually to string).
Configuration
No response
Other information
I was trying to create a PR that would solve this issue. But I was not able to find the place to fix Mapped type WithStringParamItemRequestBuilder for WithStringParam using the fallback approach.
and simillar.
What I think I succeeded in is modifying the MergeIndexNodesAtSameLevel
method:
private static IDictionary<string, IOpenApiPathItem> GetPathItems(OpenApiUrlTreeNode node)
{
if (node.PathItems.Count > 0)
{
return node.PathItems;
}
return GetPathItems(node.Children.First().Value);
}
private static (JsonSchemaType?, string?)? GetSegmentType(OpenApiUrlTreeNode node)
{
var parameterName = node.Segment.Trim('{', '}').CleanupSymbolName();
var pathItems = GetPathItems(node);
if (pathItems.TryGetValue(Constants.DefaultOpenApiLabel, out var pathItem) && pathItem.Operations?.First().Value is {} operation)
{
var parameter = operation.Parameters?.FirstOrDefault(x => x.In == ParameterLocation.Path && x.Name?.Equals(parameterName, StringComparison.Ordinal) == true);
if (parameter?.Schema is {} schema)
{
return (schema.Type, schema.Format);
}
}
return null;
}
internal static void MergeIndexNodesAtSameLevel(this OpenApiUrlTreeNode node, ILogger logger)
{
var groupedIndexNodes = node.Children
.Where(static x => x.Value.IsPathSegmentWithSingleSimpleParameter())
.GroupBy(static x => GetSegmentType(x.Value))
.Select(static x => KeyValuePair.Create(
x.Key, x.OrderBy(static x => x.Key, StringComparer.OrdinalIgnoreCase).ToArray()
));
foreach (var (_, indexNodes) in groupedIndexNodes)
{
if (indexNodes.Length > 1)
{
var indexNode = indexNodes[0];
node.Children.Remove(indexNode.Key);
var oldSegmentName = indexNode.Value.Segment.Trim('{', '}').CleanupSymbolName();
var segmentIndex = indexNode.Value.Path.Split('\\', StringSplitOptions.RemoveEmptyEntries).ToList().IndexOf(indexNode.Value.Segment);
var newSegmentParameterName = oldSegmentName.EndsWith("-id", StringComparison.OrdinalIgnoreCase) ? oldSegmentName : $"{{{oldSegmentName.TrimSuffix("id", StringComparison.OrdinalIgnoreCase)}-id}}";
indexNode.Value.Path = indexNode.Value.Path.Replace(indexNode.Key, newSegmentParameterName, StringComparison.OrdinalIgnoreCase);
indexNode.Value.AdditionalData.Add(Constants.KiotaSegmentNameTreeNodeExtensionKey, [newSegmentParameterName]);
indexNode.Value.AddDeduplicatedSegment(newSegmentParameterName);
node.Children.Add(newSegmentParameterName, indexNode.Value);
CopyNodeIntoOtherNode(indexNode.Value, indexNode.Value, indexNode.Key, newSegmentParameterName, logger);
foreach (var child in indexNodes.Except([indexNode]))
{
node.Children.Remove(child.Key);
CopyNodeIntoOtherNode(child.Value, indexNode.Value, child.Key, newSegmentParameterName, logger);
}
ReplaceParameterInPathForAllChildNodes(indexNode.Value, segmentIndex, newSegmentParameterName);
}
}
foreach (var child in node.Children.Values)
MergeIndexNodesAtSameLevel(child, logger);
}
Debug output
Click to expand log
``` dbug: Kiota.Builder.KiotaBuilder[0] kiota version 1.28.0 info: Kiota.Builder.KiotaBuilder[0] loaded description from local source dbug: Kiota.Builder.KiotaBuilder[0] step 1 - reading the stream - took 00:00:00.0492876 dbug: Kiota.Builder.KiotaBuilder[0] step 2 - parsing the document - took 00:00:00.3415032 dbug: Kiota.Builder.KiotaBuilder[0] step 3 - updating generation configuration from kiota extension - took 00:00:00.0007088 dbug: Kiota.Builder.KiotaBuilder[0] step 4 - filtering API paths with patterns - took 00:00:00.0254496 info: Kiota.Builder.KiotaBuilder[0] Client root URL set to https://api.example.com dbug: Kiota.Builder.KiotaBuilder[0] step 5 - checking whether the output should be updated - took 00:00:00.1115461 dbug: Kiota.Builder.KiotaBuilder[0] step 6 - create uri space - took 00:00:00.0503657 dbug: Kiota.Builder.KiotaBuilder[0] InitializeInheritanceIndex 00:00:00.0180228 dbug: Kiota.Builder.KiotaBuilder[0] CreateRequestBuilderClass 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] MapTypeDefinitions 00:00:00.0223389 dbug: Kiota.Builder.KiotaBuilder[0] TrimInheritedModels 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] CleanUpInternalState 00:00:00 dbug: Kiota.Builder.KiotaBuilder[0] step 7 - create source model - took 00:00:00.4091464 dbug: Kiota.Builder.KiotaBuilder[0] 116ms: Language refinement applied dbug: Kiota.Builder.KiotaBuilder[0] step 8 - refine by language - took 00:00:00.1248213 dbug: Kiota.Builder.KiotaBuilder[0] step 9 - writing files - took 00:00:00.1386270 info: Kiota.Builder.KiotaBuilder[0] loaded description from local source dbug: Kiota.Builder.KiotaBuilder[0] step 10 - writing lock file - took 00:00:00.0570530 Generation completed successfully Client base url set to https://api.example.com dbug: Kiota.Builder.KiotaBuilder[0] Api manifest path: /opt/asp.net/kiota/src/kiota/bin/Debug/net9.0/apimanifest.jsonHint: use the info command to get the list of dependencies you need to add to your project.
Example: kiota info -d "/opt/asp.net/path-openapi.json" -l CSharp
Hint: use the --include-path and --exclude-path options with glob patterns to filter the paths generated.
Example: kiota generate --include-path "**/foo" -d "/opt/asp.net/path-openapi.json"
</details>
Metadata
Metadata
Assignees
Type
Projects
Status