Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
29 changes: 29 additions & 0 deletions tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/IResponseFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Azure.Sdk.Tools.Cli.Models;
using Azure.Sdk.Tools.Cli.Models.Responses.Package;

namespace Azure.Sdk.Tools.Cli.Helpers;

/// <summary>
/// Factory for creating standardized command responses.
/// </summary>
public interface IResponseFactory
{
/// <summary>
/// Creates a package failure response with the specified error message and package info.
/// </summary>
/// <param name="message">The error message to include in the response.</param>
/// <param name="packageInfo">Optional package information to include in the response.</param>
/// <param name="nextSteps">Optional next steps to include in the response.</param>
/// <returns>A PackageOperationResponse indicating failure.</returns>
PackageOperationResponse CreateFailureResponse(string message, PackageInfo? packageInfo = null, string[]? nextSteps = null);

/// <summary>
/// Creates a success response with the specified message.
/// Only works with PackageOperationResponse.
/// </summary>
/// <param name="message">The success message to include in the response.</param>
/// <param name="packageInfo">Optional package information to include in the response.</param>
/// <param name="nextSteps">Optional next steps to include in the response.</param>
/// <returns>A PackageOperationResponse indicating success.</returns>
PackageOperationResponse CreateSuccessResponse(string message, PackageInfo? packageInfo = null, string[]? nextSteps = null);
}
38 changes: 38 additions & 0 deletions tools/azsdk-cli/Azure.Sdk.Tools.Cli/Helpers/ResponseFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Azure.Sdk.Tools.Cli.Models;
using Azure.Sdk.Tools.Cli.Models.Responses.Package;

namespace Azure.Sdk.Tools.Cli.Helpers;

/// <summary>
/// Factory for creating standardized command responses.
/// </summary>
public class ResponseFactory : IResponseFactory
{
/// <inheritdoc />
public PackageOperationResponse CreateFailureResponse(string message, PackageInfo? packageInfo = null, string[]? nextSteps = null)
{
return new PackageOperationResponse
{
ResponseErrors = [message],
PackageName = packageInfo?.PackageName ?? string.Empty,
Language = packageInfo?.Language ?? SdkLanguage.Unknown,
PackageType = packageInfo?.SdkType ?? SdkType.Unknown,
Result = "failed",
NextSteps = nextSteps?.ToList() ?? []
};
}

/// <inheritdoc />
public PackageOperationResponse CreateSuccessResponse(string message, PackageInfo? packageInfo = null, string[]? nextSteps = null)
{
return new PackageOperationResponse
{
Result = "succeeded",
Message = message,
PackageName = packageInfo?.PackageName ?? string.Empty,
Language = packageInfo?.Language ?? SdkLanguage.Unknown,
PackageType = packageInfo?.SdkType ?? SdkType.Unknown,
NextSteps = nextSteps?.ToList() ?? []
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public interface ISpecGenSdkConfigHelper
{
// Config value retrieval methods
Task<T> GetConfigValueFromRepoAsync<T>(string repositoryRoot, string jsonPath);
Task<(BuildConfigType type, string value)> GetBuildConfigurationAsync(string repositoryRoot);
Task<(SpecGenSdkConfigContentType type, string value)> GetConfigurationAsync(string repositoryRoot, SpecGenSdkConfigType configType);

// Command processing methods
string SubstituteCommandVariables(string command, Dictionary<string, string> variables);
string[] ParseCommand(string command);
Expand All @@ -40,6 +40,14 @@ public class SpecGenSdkConfigHelper : ISpecGenSdkConfigHelper
// Constants
private const string BuildCommandJsonPath = "packageOptions/buildScript/command";
private const string BuildScriptPathJsonPath = "packageOptions/buildScript/path";
private const string UpdateChangelogCommandJsonPath = "packageOptions/updateChangelogScript/command";
private const string UpdateChangelogScriptPathJsonPath = "packageOptions/updateChangelogScript/path";
private const string UpdateVersionCommandJsonPath = "packageOptions/updateVersionScript/command";
private const string UpdateVersionScriptPathJsonPath = "packageOptions/updateVersionScript/path";
private const string UpdateMetadataCommandJsonPath = "packageOptions/updateMetadataScript/command";
private const string UpdateMetadataScriptPathJsonPath = "packageOptions/updateMetadataScript/path";
private const string UpdateCiCommandJsonPath = "packageOptions/updateCiScript/command";
private const string UpdateCiScriptPathJsonPath = "packageOptions/updateCiScript/path";
private const string SpecToSdkConfigPath = "eng/swagger_to_sdk_config.json";

private readonly ILogger<SpecGenSdkConfigHelper> _logger;
Expand All @@ -54,10 +62,7 @@ public async Task<T> GetConfigValueFromRepoAsync<T>(string repositoryRoot, strin
{
var specToSdkConfigFilePath = Path.Combine(repositoryRoot, SpecToSdkConfigPath);

_logger.LogInformation(
"Reading configuration from: {ConfigFilePath} at path: {JsonPath}",
specToSdkConfigFilePath,
jsonPath);
_logger.LogInformation("Reading configuration from: {SpecToSdkConfigFilePath} at path: {JsonPath}", specToSdkConfigFilePath, jsonPath);

if (!File.Exists(specToSdkConfigFilePath))
{
Expand Down Expand Up @@ -93,42 +98,45 @@ public async Task<T> GetConfigValueFromRepoAsync<T>(string repositoryRoot, strin
}
}

// Get build configuration (either command or script path)
public async Task<(BuildConfigType type, string value)> GetBuildConfigurationAsync(string repositoryRoot)
// Get configuration for a specific type (either command or script path)
public async Task<(SpecGenSdkConfigContentType type, string value)> GetConfigurationAsync(string repositoryRoot, SpecGenSdkConfigType configType)
{
var (commandPath, scriptPath) = GetConfigPaths(configType);

// Try command first
try
{
var command = await GetConfigValueFromRepoAsync<string>(repositoryRoot, BuildCommandJsonPath);
var command = await GetConfigValueFromRepoAsync<string>(repositoryRoot, commandPath);
if (!string.IsNullOrEmpty(command))
{
_logger.LogDebug("Found build command configuration");
return (BuildConfigType.Command, command);
_logger.LogDebug("Found {ConfigType} command configuration", configType);
return (SpecGenSdkConfigContentType.Command, command);
}
}
catch (InvalidOperationException)
{
// Command not found, continue to try path
_logger.LogDebug("No build command found, trying script path");
_logger.LogDebug("No {ConfigType} command found, trying script path", configType);
}

// Try path
try
{
var path = await GetConfigValueFromRepoAsync<string>(repositoryRoot, BuildScriptPathJsonPath);
var path = await GetConfigValueFromRepoAsync<string>(repositoryRoot, scriptPath);
if (!string.IsNullOrEmpty(path))
{
_logger.LogDebug("Found build script path configuration");
return (BuildConfigType.ScriptPath, path);
_logger.LogDebug("Found {ConfigType} script path configuration", configType);
return (SpecGenSdkConfigContentType.ScriptPath, path);
}
}
catch (InvalidOperationException ex)
catch (InvalidOperationException)
{
// Path not found either
_logger.LogError(ex, "No build configuration found");
_logger.LogError("No {ConfigType} configuration found", configType);
}

throw new InvalidOperationException($"Neither '{BuildCommandJsonPath}' nor '{BuildScriptPathJsonPath}' found in configuration.");
_logger.LogWarning("Neither '{CommandPath}' nor '{ScriptPath}' found in configuration for {ConfigType}", commandPath, scriptPath, configType);
return (SpecGenSdkConfigContentType.Unknown, string.Empty);
}

// Substitute template variables in command strings
Expand Down Expand Up @@ -172,6 +180,20 @@ public string[] ParseCommand(string command)
return tokens;
}

// Get the configuration paths for a specific config type
private (string commandPath, string scriptPath) GetConfigPaths(SpecGenSdkConfigType configType)
{
return configType switch
{
SpecGenSdkConfigType.Build => (BuildCommandJsonPath, BuildScriptPathJsonPath),
SpecGenSdkConfigType.UpdateChangelog => (UpdateChangelogCommandJsonPath, UpdateChangelogScriptPathJsonPath),
SpecGenSdkConfigType.UpdateVersion => (UpdateVersionCommandJsonPath, UpdateVersionScriptPathJsonPath),
SpecGenSdkConfigType.UpdateMetadata => (UpdateMetadataCommandJsonPath, UpdateMetadataScriptPathJsonPath),
SpecGenSdkConfigType.UpdateCi => (UpdateCiCommandJsonPath, UpdateCiScriptPathJsonPath),
_ => throw new ArgumentException($"Unsupported config type: {configType}")
};
}

// Try to get a JSON element by its path
private (bool found, JsonElement element) TryGetJsonElementByPath(JsonElement root, string path)
{
Expand All @@ -195,9 +217,25 @@ public string[] ParseCommand(string command)
}
}

public enum BuildConfigType
/// <summary>
/// Represents different content types for configuration values.
/// </summary>
public enum SpecGenSdkConfigContentType
{
Unknown,
Command,
ScriptPath
}

/// <summary>
/// Represents different types of configuration that can be retrieved from swagger_to_sdk_config.json
/// </summary>
public enum SpecGenSdkConfigType
{
Build,
UpdateChangelog,
UpdateVersion,
UpdateMetadata,
UpdateCi
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.
using System.Text.Json.Serialization;
using Azure.Sdk.Tools.Cli.Attributes;
using Azure.Sdk.Tools.Cli.Services.Languages;
using Azure.Sdk.Tools.Cli.Services;

namespace Azure.Sdk.Tools.Cli.Models.Responses.Package
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Azure.Sdk.Tools.Cli.Models;
using Azure.Sdk.Tools.Cli.Models.Responses.Package;
using Azure.Sdk.Tools.Cli.Helpers;

namespace Azure.Sdk.Tools.Cli.Services;

/// <summary>
/// Service for creating process options based on configuration and executing processes with standardized response handling.
/// </summary>
public interface IProcessConfigurationService
{
/// <summary>
/// Creates process options based on configuration type and parameters.
/// </summary>
/// <param name="configType">The type of configuration content type.</param>
/// <param name="configValue">The configuration value (e.g., command or script path).</param>
/// <param name="sdkRepoRoot">The root path of the SDK repository.</param>
/// <param name="workingDirectory">The working directory for the process.</param>
/// <param name="parameters">Dictionary of parameters to pass to the script/command.</param>
/// <param name="timeoutMinutes">Timeout in minutes for the process execution.</param>
/// <returns>Configured ProcessOptions for execution.</returns>
ProcessOptions CreateProcessOptionsAsync(
SpecGenSdkConfigContentType configType,
string configValue,
string sdkRepoRoot,
string workingDirectory,
Dictionary<string, string> parameters,
int timeoutMinutes = 30);

/// <summary>
/// Executes a process with common error handling and response formatting.
/// </summary>
/// <param name="options">The process options for execution.</param>
/// <param name="ct">Cancellation token for the operation.</param>
/// <param name="packageInfo">Optional package information to include in the response.</param>
/// <param name="successMessage">Message to include on successful execution.</param>
/// <param name="nextSteps">Array of next steps to include on successful execution.</param>
/// <returns>A response indicating the result of the process execution.</returns>
Task<PackageOperationResponse> ExecuteProcessAsync(
ProcessOptions options,
CancellationToken ct,
PackageInfo? packageInfo = null,
string successMessage = "Process completed successfully.",
string[]? nextSteps = null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@
// Licensed under the MIT License.
using System.Collections.Immutable;
using Azure.Sdk.Tools.Cli.Models;
using Azure.Sdk.Tools.Cli.Helpers;
using Azure.Sdk.Tools.Cli.Models.Responses.Package;

namespace Azure.Sdk.Tools.Cli.Services.Languages
namespace Azure.Sdk.Tools.Cli.Services
{
public class LanguageService
{
private readonly ILanguageSpecificResolver<IPackageInfoHelper> _packageInfoResolver;
private readonly ILogger<LanguageService> _logger;

public LanguageService(ILanguageSpecificResolver<IPackageInfoHelper> packageInfoResolver, ILogger<LanguageService> logger)
{
_packageInfoResolver = packageInfoResolver;
_logger = logger;
}

public static readonly ImmutableDictionary<string, SdkLanguage> RepoToLanguageMap = new Dictionary<string, SdkLanguage>()
{
{ "azure-sdk-for-net", SdkLanguage.DotNet },
Expand All @@ -29,5 +40,27 @@ public static SdkLanguage GetLanguageForRepo(string repoName)
}
return SdkLanguage.Unknown;
}

public async Task<PackageInfo> GetPackageInfo(string packagePath, CancellationToken ct)
{
PackageInfo? packageInfo = null;
try
{
var packageInfoHelper = await _packageInfoResolver.Resolve(packagePath, ct);
if (packageInfoHelper != null)
{
packageInfo = await packageInfoHelper.ResolvePackageInfo(packagePath, ct);
}
else
{
_logger.LogError("No package info helper found for package path: {packagePath}", packagePath);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while parsing package path: {packagePath}", packagePath);
}
return packageInfo;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Azure.Sdk.Tools.Cli.Models.Responses.Package;
using Azure.Sdk.Tools.Cli.Services;
using Azure.Sdk.Tools.Cli.Helpers;
using Microsoft.Extensions.Logging;

namespace Azure.Sdk.Tools.Cli.Services;

/// <summary>
/// .NET-specific implementation for package update operations.
/// </summary>
public class DotNetPackageUpdate : ILanguagePackageUpdate
{
private readonly ILogger<DotNetPackageUpdate> _logger;
private readonly IResponseFactory _responseFactory;

public DotNetPackageUpdate(
ILogger<DotNetPackageUpdate> logger,
IResponseFactory responseFactory)
{
_logger = logger;
_responseFactory = responseFactory;
}

/// <inheritdoc />
public async Task<PackageOperationResponse> UpdateMetadataAsync(string packagePath, CancellationToken ct)
{
await Task.CompletedTask;
throw new NotImplementedException(".NET metadata update is not yet implemented.");
}

/// <inheritdoc />
public async Task<PackageOperationResponse> UpdateChangelogContentAsync(string packagePath, CancellationToken ct)
{
await Task.CompletedTask;
throw new NotImplementedException(".NET changelog update is not yet implemented.");
}

/// <inheritdoc />
public async Task<PackageOperationResponse> UpdateVersionAsync(string packagePath, CancellationToken ct)
{
await Task.CompletedTask;
throw new NotImplementedException(".NET version update is not yet implemented.");
}
}
Loading
Loading