Skip to content
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
62 changes: 58 additions & 4 deletions src/NSwag.CodeGeneration.Tests/CodeGenerationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ public void When_using_MultipleClientsFromFirstTagAndOperationName_then_ensure_t
}

[Theory(DisplayName = "Ensure expected operation name generation when using MultipleClientsFromFirstTagAndOperationName behavior")]
[InlineData("OperationId_SecondUnderscore_Test", "Test")]
[InlineData("OperationId_MultipleUnderscores_Client_Test", "Test")]
[InlineData("OperationId_SecondUnderscore_Test", "SecondUnderscore_Test")]
[InlineData("OperationId_MultipleUnderscores_Client_Test", "MultipleUnderscores_Client_Test")]
[InlineData("OperationId_Test", "Test")]
[InlineData("UnderscoreLast_", "UnderscoreLast_")]
[InlineData("_UnderscoreFirst", "UnderscoreFirst")]
Expand All @@ -111,8 +111,8 @@ public void When_using_MultipleClientsFromFirstTagAndOperationName_then_ensure_t
}

[Theory(DisplayName = "Ensure expected client name generation with different operationIds when using the MultipleClientsFromOperationId behavior")]
[InlineData("OperationId_SecondUnderscore_Test", "SecondUnderscore")]
[InlineData("OperationId_MultipleUnderscores_Client_Test", "Client")]
[InlineData("OperationId_SecondUnderscore_Test", "OperationId")]
[InlineData("OperationId_MultipleUnderscores_Client_Test", "OperationId")]
[InlineData("OperationId_Test", "OperationId")]
[InlineData("UnderscoreLast_", "UnderscoreLast")]
[InlineData("_UnderscoreFirst", "")]
Expand All @@ -139,5 +139,59 @@ public void When_using_MultipleClientsFromOperationId_then_ensure_that_underscor
// Assert
Assert.Equal(expectedClientName, clientName);
}

[Theory(DisplayName = "Ensure expected operation name generation with different operationIds when using the MultipleClientsFromOperationId behavior")]
[InlineData("OperationId_SecondUnderscore_Test", "SecondUnderscore_Test")]
[InlineData("OperationId_MultipleUnderscores_Client_Test", "MultipleUnderscores_Client_Test")]
[InlineData("OperationId_Test", "Test")]
[InlineData("UnderscoreLast_", "UnderscoreLast_")]
[InlineData("_UnderscoreFirst", "UnderscoreFirst")]
[InlineData("NoUnderscore", "NoUnderscore")]
public void When_using_MultipleClientsFromOperationId_then_ensure_that_operationname_is_correct(string operationId, string expectedOperationName)
{
// Arrange
var operation = new OpenApiOperation
{
OperationId = operationId
};
var generator = new MultipleClientsFromOperationIdOperationNameGenerator();

var document = new OpenApiDocument();
var path = string.Empty;
var httpMethod = string.Empty;

// Act
string operationName = generator.GetOperationName(document, path, httpMethod, operation);

// Assert
Assert.Equal(expectedOperationName, operationName);
}

[Theory(DisplayName = "Ensure unique (client, operation) pairs when using MultipleClientsFromOperationId to avoid duplicate method names")]
[InlineData("Orders_items_get", "Products_items_get")]
[InlineData("Resource1_getSomething", "Resource2_getSomething")]
[InlineData("A_B_C", "D_B_C")]
public void When_using_MultipleClientsFromOperationId_then_unique_operationIds_produce_unique_client_operation_pairs(string operationId1, string operationId2)
{
// Arrange
var operation1 = new OpenApiOperation { OperationId = operationId1 };
var operation2 = new OpenApiOperation { OperationId = operationId2 };
var generator = new MultipleClientsFromOperationIdOperationNameGenerator();

var document = new OpenApiDocument();
var path = string.Empty;
var httpMethod = string.Empty;

// Act
string clientName1 = generator.GetClientName(document, path, httpMethod, operation1);
string operationName1 = generator.GetOperationName(document, path, httpMethod, operation1);
string clientName2 = generator.GetClientName(document, path, httpMethod, operation2);
string operationName2 = generator.GetOperationName(document, path, httpMethod, operation2);

// Assert: unique operation IDs must not produce the same (client, operation) pair
Assert.False(
clientName1 == clientName2 && operationName1 == operationName2,
$"OperationIds '{operationId1}' and '{operationId2}' produced duplicate (client='{clientName1}', operation='{operationName1}') pair");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,42 +81,23 @@ public virtual string GetOperationName(OpenApiDocument document, string path, st
private static ReadOnlySpan<char> GetClientName(OpenApiOperation operation)
{
ReadOnlySpan<char> operationIdSpan = operation.OperationId.AsSpan();
const char underscoreSeparator = '_';
int idxFirst = operationIdSpan.IndexOf(underscoreSeparator);
int idx = operationIdSpan.IndexOf('_');

// no underscore, fast path
if (idxFirst == -1)
// no underscore or underscore is the first character
if (idx <= 0)
{
return [];
}

int idxLast = operationIdSpan.LastIndexOf(underscoreSeparator);

// only one underscore
if (idxFirst == idxLast)
{
// underscore is the first character
if (idxFirst == 0)
{
return [];
}

return operationIdSpan.Slice(0, idxFirst);
}

// backwards search for the second underscore
// e.g. OperationId_SecondUnderscore_Test => SecondUnderscore
operationIdSpan = operationIdSpan.Slice(0, idxLast);
int idxSecondLast = operationIdSpan.LastIndexOf(underscoreSeparator);

return operationIdSpan.Slice(idxSecondLast + 1);
return operationIdSpan.Slice(0, idx);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ReadOnlySpan<char> GetOperationName(OpenApiOperation operation)
{
var span = operation.OperationId.AsSpan();
var idx = span.LastIndexOf('_');
var idx = span.IndexOf('_');
// No underscore, or underscore is the last character: return the full operation ID
return idx != -1 && idx < span.Length - 1
? span.Slice(idx + 1)
: span;
Expand Down
Loading