|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.Extensions.ApiDescription.Client;
/// <summary>
/// Adds or corrects ClassName, FirstForGenerator, Namespace, and OutputPath metadata in OpenApiReference items.
/// Also stores final metadata as SerializedMetadata.
/// </summary>
public class GetOpenApiReferenceMetadata : Task
{
private const string TypeScriptLanguageName = "TypeScript";
/// <summary>
/// Extension to use in default OutputPath metadata value. Ignored when generating TypeScript.
/// </summary>
[Required]
public string Extension { get; set; }
/// <summary>
/// Default Namespace metadata value.
/// </summary>
[Required]
public string Namespace { get; set; }
/// <summary>
/// Default directory for OutputPath values.
/// </summary>
public string OutputDirectory { get; set; }
/// <summary>
/// The OpenApiReference items to update.
/// </summary>
[Required]
public ITaskItem[] Inputs { get; set; }
/// <summary>
/// The updated OpenApiReference items. Will include ClassName, Namespace and OutputPath metadata.
/// </summary>
[Output]
public ITaskItem[] Outputs { get; set; }
/// <inheritdoc />
public override bool Execute()
{
var outputs = new List<ITaskItem>(Inputs.Length);
var codeGenerators = new HashSet<string>();
var destinations = new HashSet<string>();
foreach (var item in Inputs)
{
var codeGenerator = item.GetMetadata("CodeGenerator");
if (string.IsNullOrEmpty(codeGenerator))
{
// This case occurs when user overrides the required metadata with an empty string.
var type = string.IsNullOrEmpty(item.GetMetadata("SourceProject")) ?
"OpenApiReference" :
"OpenApiProjectReference";
Log.LogError(Resources.FormatInvalidEmptyMetadataValue("CodeGenerator", type, item.ItemSpec));
continue;
}
var newItem = new TaskItem(item);
outputs.Add(newItem);
if (codeGenerators.Add(codeGenerator))
{
newItem.SetMetadata("FirstForGenerator", "true");
}
else
{
newItem.SetMetadata("FirstForGenerator", "false");
}
var outputPath = item.GetMetadata("OutputPath");
if (string.IsNullOrEmpty(outputPath))
{
// No need to further sanitize this path because the file must exist.
var filename = item.GetMetadata("Filename");
var isTypeScript = codeGenerator.EndsWith(
TypeScriptLanguageName,
StringComparison.OrdinalIgnoreCase);
outputPath = $"{filename}Client{(isTypeScript ? ".ts" : Extension)}";
}
// Place output file in correct directory (relative to project directory).
if (!Path.IsPathRooted(outputPath) && !string.IsNullOrEmpty(OutputDirectory))
{
outputPath = Path.Combine(OutputDirectory, outputPath);
}
if (!destinations.Add(outputPath))
{
// This case may occur when user is experimenting e.g. with multiple code generators or options.
// May also occur when user accidentally duplicates OutputPath metadata.
Log.LogError(Resources.FormatDuplicateFileOutputPaths(outputPath));
continue;
}
MetadataSerializer.SetMetadata(newItem, "OutputPath", outputPath);
var className = item.GetMetadata("ClassName");
if (string.IsNullOrEmpty(className))
{
var outputFilename = Path.GetFileNameWithoutExtension(outputPath);
className = CSharpIdentifier.SanitizeIdentifier(outputFilename);
MetadataSerializer.SetMetadata(newItem, "ClassName", className);
}
var @namespace = item.GetMetadata("Namespace");
if (string.IsNullOrEmpty(@namespace))
{
MetadataSerializer.SetMetadata(newItem, "Namespace", Namespace);
}
// Add metadata which may be used as a property and passed to an inner build.
newItem.RemoveMetadata("SerializedMetadata");
newItem.SetMetadata("SerializedMetadata", MetadataSerializer.SerializeMetadata(newItem));
}
Outputs = outputs.ToArray();
return !Log.HasLoggedErrors;
}
}
|