private string GenerateEnumerableMethodForBatchApiEndpoint(ApiEndpoint apiEndpoint, string baseEndpointPath) { var indent = new Indent(); var builder = new StringBuilder(); var endpointPath = (baseEndpointPath + "/" + apiEndpoint.Path.Segments.ToPath()).TrimEnd('/'); var methodNameForEndpoint = apiEndpoint.ToCSharpMethodName(); var batchDataType = ((ApiFieldType.Object)apiEndpoint.ResponseBody !).GetBatchDataType() !; if (apiEndpoint.ResponseBody != null) { var methodParametersBuilder = new MethodParametersBuilder(_codeGenerationContext) .WithParametersForApiParameters(apiEndpoint.Parameters); if (apiEndpoint.RequestBody != null) { if (FeatureFlags.DoNotExposeRequestObjects) { methodParametersBuilder = methodParametersBuilder .WithParametersForApiFields(apiEndpoint.RequestBody.Fields); } else { methodParametersBuilder = methodParametersBuilder .WithParameter( apiEndpoint.ToCSharpRequestBodyClassName(endpointPath) !, "data"); } } var partialType = "Partial<" + batchDataType.GetBatchElementTypeOrType().ToCSharpType(_codeGenerationContext) + ">"; var funcType = $"Func<{partialType}, {partialType}>?"; methodParametersBuilder = methodParametersBuilder .WithParameter( funcType, "partial", CSharpExpression.NullLiteral); methodParametersBuilder = methodParametersBuilder .WithParameter(CSharpType.CancellationToken.Value, "cancellationToken", CSharpExpression.DefaultLiteral); builder.Append( indent.Wrap(GenerateMethodDocumentationForEndpoint(apiEndpoint))); builder.Append($"{indent}public IAsyncEnumerable<"); builder.Append(batchDataType.ElementType.ToCSharpType(_codeGenerationContext)); builder.Append(">"); builder.Append($" {methodNameForEndpoint}AsyncEnumerable("); builder.Append(methodParametersBuilder.BuildMethodParametersList()); builder.AppendLine(")"); indent.Increment(); builder.Append($"{indent}=> BatchEnumerator.AllItems((batchSkip, batchCancellationToken) => "); builder.Append($"{methodNameForEndpoint}Async("); var partialTypeForBatch = "Partial<Batch<" + batchDataType.GetBatchElementTypeOrType().ToCSharpType(_codeGenerationContext) + ">>"; builder.Append( methodParametersBuilder .WithDefaultValueForAllParameters(null) .WithDefaultValueForParameter("skip", "batchSkip") .WithDefaultValueForParameter("partial", $"builder => {partialTypeForBatch}.Default().WithNext().WithTotalCount().WithData(partial != null ? partial : _ => {partialType}.Default())") .WithDefaultValueForParameter(CSharpType.CancellationToken.Value, "batchCancellationToken") .BuildMethodCallParameters()); builder.Append("), skip, cancellationToken);"); indent.Decrement(); } else { builder.AppendLine($"{indent}#warning UNSUPPORTED CASE - " + apiEndpoint.ToCSharpMethodName() + " - " + apiEndpoint.Method.ToHttpMethod() + " " + endpointPath); } return(builder.ToString()); }
public string GenerateDtoDefinition(ApiDto apiDto) { var indent = new Indent(); var builder = new StringBuilder(); var typeNameForDto = apiDto.ToCSharpClassName(); if (apiDto.Deprecation != null) { builder.AppendLine(apiDto.Deprecation.ToCSharpDeprecation()); } if (apiDto.HierarchyRole != HierarchyRole.INTERFACE && apiDto.Extends == null && apiDto.Inheritors.Count > 0) { // When extending another DTO, make sure to apply a converter builder.AppendLine($"{indent}[JsonConverter(typeof(ClassNameDtoTypeConverter))]"); } var modifierForDto = apiDto.HierarchyRole == HierarchyRole.INTERFACE ? "interface" : apiDto.HierarchyRole == HierarchyRole.ABSTRACT ? "abstract class" : apiDto.HierarchyRole == HierarchyRole.FINAL ? "sealed class" : "class"; var dtoHierarchy = new List <string>(); var dtoHierarchyFieldNames = new List <string>(); if (apiDto.Extends != null && _codeGenerationContext.TryGetDto(apiDto.Extends.Id, out var apiDtoExtends)) { dtoHierarchy.Add(apiDtoExtends !.ToCSharpClassName()); dtoHierarchyFieldNames.AddRange(apiDtoExtends !.Fields.Select(it => it.Field.Name)); } if (apiDto.Implements != null) { foreach (var dtoImplements in apiDto.Implements) { if (_codeGenerationContext.TryGetDto(dtoImplements.Id, out var apiDtoImplements)) { dtoHierarchy.Add(apiDtoImplements !.ToCSharpClassName()); } } } if (dtoHierarchy.Count > 0 || apiDto.Inheritors.Count > 0) { dtoHierarchy.Add(nameof(IClassNameConvertible)); } dtoHierarchy.Add(nameof(IPropagatePropertyAccessPath)); builder.AppendLine($"{indent}public {modifierForDto} {typeNameForDto}"); indent.Increment(); builder.AppendLine($"{indent} : " + string.Join(", ", dtoHierarchy)); indent.Decrement(); builder.AppendLine($"{indent}{{"); indent.Increment(); // When in a hierarchy with IClassNameConvertible, make sure we can capture the class name. if (dtoHierarchy.Contains(nameof(IClassNameConvertible)) && apiDto.HierarchyRole != HierarchyRole.INTERFACE) { var modifierForClassNameProperty = apiDto.Extends == null ? apiDto.HierarchyRole != HierarchyRole.FINAL ? "virtual" // Parent : "" : "override"; // Inheritor builder.AppendLine($"{indent}[JsonPropertyName(\"className\")]"); builder.AppendLine($"{indent}public {modifierForClassNameProperty} string? ClassName => \"{apiDto.Name}\";"); builder.AppendLine($"{indent}"); } // Determine list of fields var apiDtoFields = DetermineFieldsToGenerateFor(apiDto); // Generate factories for inheritors foreach (var apiDtoInheritorReference in apiDto.Inheritors) { if (_codeGenerationContext.TryGetDto(apiDtoInheritorReference.Id, out var apiDtoInheritor) && apiDtoInheritor !.HierarchyRole != HierarchyRole.INTERFACE && apiDtoInheritor.HierarchyRole != HierarchyRole.ABSTRACT) { var inheritorTypeName = apiDtoInheritor.ToCSharpClassName(); var inheritorFactoryMethodName = apiDtoInheritor.ToCSharpFactoryMethodName(apiDto); var methodParametersBuilder = new MethodParametersBuilder(_codeGenerationContext) .WithParametersForApiDtoFields(DetermineFieldsToGenerateFor(apiDtoInheritor !)); builder.AppendLine($"{indent}public static {inheritorTypeName} {inheritorFactoryMethodName}({methodParametersBuilder.BuildMethodParametersList()})"); indent.Increment(); builder.AppendLine($"{indent}=> new {inheritorTypeName}({methodParametersBuilder.WithDefaultValueForAllParameters(null).BuildMethodCallParameters()});"); indent.Decrement(); builder.AppendLine($"{indent}"); } } // Generate constructor // ReSharper disable once RedundantLogicalConditionalExpressionOperand if (apiDto.HierarchyRole != HierarchyRole.INTERFACE && apiDto.HierarchyRole != HierarchyRole.ABSTRACT) { var methodParametersBuilder = new MethodParametersBuilder(_codeGenerationContext) .WithParametersForApiDtoFields(apiDtoFields); // Empty constructor builder.AppendLine($"{indent}public {typeNameForDto}() {{ }}"); builder.AppendLine($"{indent}"); // Parameterized constructor if (apiDtoFields.Count > 0) { builder.AppendLine($"{indent}public {typeNameForDto}({methodParametersBuilder.BuildMethodParametersList()})"); builder.AppendLine($"{indent}{{"); indent.Increment(); foreach (var apiDtoField in apiDtoFields) { if (FeatureFlags.GenerateAlternativeForOptionalParameterDefaultReferenceTypes && apiDtoField.Field.Type.IsCSharpReferenceType()) { builder.AppendLine($"{indent}{apiDtoField.Field.ToCSharpPropertyName(typeNameForDto)} = {apiDtoField.Field.ToCSharpVariableInstanceOrDefaultValue(_codeGenerationContext)};"); } else { builder.AppendLine($"{indent}{apiDtoField.Field.ToCSharpPropertyName(typeNameForDto)} = {apiDtoField.Field.ToCSharpVariableName()};"); } } indent.Decrement(); builder.AppendLine($"{indent}}}"); builder.AppendLine($"{indent}"); } } // Generate properties for fields foreach (var apiDtoField in apiDtoFields) { builder.AppendLine(indent.Wrap(GenerateDtoFieldDefinition(typeNameForDto, apiDtoField.Field))); } // Implement IPropagatePropertyAccessPath? if (dtoHierarchy.Contains(nameof(IPropagatePropertyAccessPath)) && apiDto.HierarchyRole != HierarchyRole.INTERFACE) { builder.AppendLine(indent.Wrap(GenerateDtoPropagatePropertyAccessPath(apiDto, apiDtoFields))); } indent.Decrement(); builder.AppendLine($"{indent}}}"); return(builder.ToString()); }
private string GenerateMethodForApiEndpoint(ApiEndpoint apiEndpoint, string baseEndpointPath) { var indent = new Indent(); var builder = new StringBuilder(); var endpointPath = (baseEndpointPath + "/" + apiEndpoint.Path.Segments.ToPath()).TrimEnd('/'); var apiCallMethod = apiEndpoint.Method.ToHttpMethod(); var methodNameForEndpoint = apiEndpoint.ToCSharpMethodName(); var isResponsePrimitiveOrArrayOfPrimitive = apiEndpoint.ResponseBody is ApiFieldType.Primitive || (apiEndpoint.ResponseBody is ApiFieldType.Array arrayField && arrayField.ElementType is ApiFieldType.Primitive); if (apiEndpoint.ResponseBody == null) { var methodParametersBuilder = new MethodParametersBuilder(_codeGenerationContext) .WithParametersForApiParameters(apiEndpoint.Parameters); if (apiEndpoint.RequestBody != null) { if (FeatureFlags.DoNotExposeRequestObjects) { methodParametersBuilder = methodParametersBuilder .WithParametersForApiFields(apiEndpoint.RequestBody.Fields); } else { methodParametersBuilder = methodParametersBuilder .WithParameter( apiEndpoint.ToCSharpRequestBodyClassName(endpointPath) !, "data"); } } methodParametersBuilder = methodParametersBuilder .WithParameter(CSharpType.CancellationToken.Value, "cancellationToken", CSharpExpression.DefaultLiteral); builder.Append( indent.Wrap(GenerateMethodDocumentationForEndpoint(apiEndpoint))); builder.Append($"{indent}public async Task {methodNameForEndpoint}Async("); builder.Append(methodParametersBuilder.BuildMethodParametersList()); builder.AppendLine(")"); builder.AppendLine($"{indent}{{"); indent.Increment(); // Generate query string var requestParametersBuilder = new QueryStringParameterConversionGenerator("queryParameters", _codeGenerationContext) .WithQueryStringParametersForEndpoint(apiEndpoint); if (apiEndpoint.ResponseBody != null && !isResponsePrimitiveOrArrayOfPrimitive) { var partialType = "Partial<" + apiEndpoint.ResponseBody.GetArrayElementTypeOrType().ToCSharpType(_codeGenerationContext) + ">"; requestParametersBuilder = requestParametersBuilder .WithQueryStringParameter("$fields", $"(partial != null ? partial(new {partialType}()) : {partialType}.Default()).ToString()"); } builder.AppendLine($"{indent}var {requestParametersBuilder.TargetNameValueCollectionName} = new NameValueCollection();"); requestParametersBuilder.WriteTo(builder, indent); builder.AppendLine($"{indent}"); // Generate HTTP request builder.Append($"{indent}await _connection.RequestResourceAsync"); builder.Append("(\"" + apiCallMethod + "\", "); builder.Append("$\"api/http/" + endpointPath + "{" + requestParametersBuilder.TargetNameValueCollectionName + ".ToQueryString()}"); builder.Append("\""); if (apiEndpoint.RequestBody != null) { if (FeatureFlags.DoNotExposeRequestObjects) { // TODO MAARTEN REFACTOR WITH PROPER NEWLINES builder.Append(", " + ConstructNewRequestObject(indent, apiEndpoint, endpointPath)); } else { builder.Append(", data"); } } builder.Append(", cancellationToken"); builder.AppendLine(");"); indent.Decrement(); builder.AppendLine($"{indent}}}"); } else if (apiEndpoint.ResponseBody != null) { var methodParametersBuilder = new MethodParametersBuilder(_codeGenerationContext) .WithParametersForApiParameters(apiEndpoint.Parameters); if (apiEndpoint.RequestBody != null) { if (FeatureFlags.DoNotExposeRequestObjects) { methodParametersBuilder = methodParametersBuilder .WithParametersForApiFields(apiEndpoint.RequestBody.Fields); } else { methodParametersBuilder = methodParametersBuilder .WithParameter( apiEndpoint.ToCSharpRequestBodyClassName(endpointPath) !, "data"); } } if (apiEndpoint.ResponseBody != null && !isResponsePrimitiveOrArrayOfPrimitive) { var partialType = "Partial<" + apiEndpoint.ResponseBody.GetArrayElementTypeOrType().ToCSharpType(_codeGenerationContext) + ">"; var funcType = $"Func<{partialType}, {partialType}>?"; methodParametersBuilder = methodParametersBuilder .WithParameter( funcType, "partial", CSharpExpression.NullLiteral); } methodParametersBuilder = methodParametersBuilder .WithParameter(CSharpType.CancellationToken.Value, "cancellationToken", CSharpExpression.DefaultLiteral); builder.Append( indent.Wrap(GenerateMethodDocumentationForEndpoint(apiEndpoint))); builder.Append($"{indent}public async Task<"); builder.Append(apiEndpoint.ResponseBody !.ToCSharpType(_codeGenerationContext)); builder.Append(">"); builder.Append($" {methodNameForEndpoint}Async("); builder.Append(methodParametersBuilder.BuildMethodParametersList()); builder.AppendLine(")"); builder.AppendLine($"{indent}{{"); indent.Increment(); // Generate query string var requestParametersBuilder = new QueryStringParameterConversionGenerator("queryParameters", _codeGenerationContext) .WithQueryStringParametersForEndpoint(apiEndpoint); if (apiEndpoint.ResponseBody != null && !isResponsePrimitiveOrArrayOfPrimitive) { var partialType = "Partial<" + apiEndpoint.ResponseBody.GetArrayElementTypeOrType().ToCSharpType(_codeGenerationContext) + ">"; requestParametersBuilder = requestParametersBuilder .WithQueryStringParameter("$fields", $"(partial != null ? partial(new {partialType}()) : {partialType}.Default()).ToString()"); } builder.AppendLine($"{indent}var {requestParametersBuilder.TargetNameValueCollectionName} = new NameValueCollection();"); requestParametersBuilder.WriteTo(builder, indent); builder.AppendLine($"{indent}"); // Generate HTTP request builder.Append($"{indent}return await _connection.RequestResourceAsync<"); if (apiEndpoint.RequestBody != null) { builder.Append(apiEndpoint.ToCSharpRequestBodyClassName(endpointPath) !); builder.Append(", "); } builder.Append(apiEndpoint.ResponseBody !.ToCSharpType(_codeGenerationContext)); builder.Append(">"); builder.Append("(\"" + apiCallMethod + "\", "); builder.Append("$\"api/http/" + endpointPath + "{" + requestParametersBuilder.TargetNameValueCollectionName + ".ToQueryString()}"); builder.Append("\""); if (apiEndpoint.RequestBody != null) { if (FeatureFlags.DoNotExposeRequestObjects) { builder.Append(", " + ConstructNewRequestObject(indent, apiEndpoint, endpointPath)); } else { builder.Append(", data"); } } builder.Append(", cancellationToken"); builder.AppendLine(");"); indent.Decrement(); builder.AppendLine($"{indent}}}"); } else { builder.AppendLine($"{indent}#warning UNSUPPORTED CASE - " + apiEndpoint.ToCSharpMethodName() + " - " + apiEndpoint.Method.ToHttpMethod() + " " + endpointPath); } return(builder.ToString()); }