| File: VSHostObject.cs | Web Access |
| Project: src\sdk\src\Containers\Microsoft.NET.Build.Containers\Microsoft.NET.Build.Containers.csproj (Microsoft.NET.Build.Containers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; using System.Text.Json; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; namespace Microsoft.NET.Build.Containers.Tasks; internal sealed class VSHostObject(ITaskHost? hostObject, TaskLoggingHelper log) { private const string CredentialItemSpecName = "MsDeployCredential"; private const string UserMetaDataName = "UserName"; private const string PasswordMetaDataName = "Password"; private readonly ITaskHost? _hostObject = hostObject; private readonly TaskLoggingHelper _log = log; /// <summary> /// Tries to extract credentials from the host object. /// </summary> /// <returns>A tuple of (username, password) if credentials were found with non-empty username, null otherwise.</returns> public (string username, string password)? TryGetCredentials() { if (_hostObject is null) { return null; } IEnumerable<ITaskItem>? taskItems = GetTaskItems(); if (taskItems is null) { _log.LogMessage(MessageImportance.Low, "No task items found in host object."); return null; } ITaskItem? credentialItem = taskItems.FirstOrDefault(p => p.ItemSpec == CredentialItemSpecName); if (credentialItem is null) { return null; } string username = credentialItem.GetMetadata(UserMetaDataName); if (string.IsNullOrEmpty(username)) { return null; } string password = credentialItem.GetMetadata(PasswordMetaDataName); return (username, password); } private IEnumerable<ITaskItem>? GetTaskItems() { try { // This call mirrors the behavior of Microsoft.WebTools.Publish.MSDeploy.VSMsDeployTaskHostObject.QueryAllTaskItems. // Expected contract: // - Instance method on the host object named "QueryAllTaskItems". // - Signature: string QueryAllTaskItems(). // - Returns a JSON array of objects with the shape: // [{ "ItemSpec": "<string>", "Metadata": { "<key>": "<value>", ... } }, ...] // The JSON is deserialized into TaskItemDto records and converted to ITaskItem instances. // Only UserName and Password metadata are extracted to avoid conflicts with reserved MSBuild metadata. string? rawTaskItems = (string?)_hostObject!.GetType().InvokeMember( "QueryAllTaskItems", BindingFlags.InvokeMethod, null, _hostObject, null); if (!string.IsNullOrEmpty(rawTaskItems)) { List<TaskItemDto>? dtos = JsonSerializer.Deserialize<List<TaskItemDto>>(rawTaskItems); if (dtos is not null && dtos.Count > 0) { _log.LogMessage(MessageImportance.Low, "Successfully retrieved task items via QueryAllTaskItems."); return dtos.Select(ConvertToTaskItem).ToList(); } } _log.LogMessage(MessageImportance.Low, "QueryAllTaskItems returned null or empty result."); } catch (Exception ex) { _log.LogMessage(MessageImportance.Low, "Exception trying to call QueryAllTaskItems: {0}", ex.Message); } // Fallback: try to use the host object directly as IEnumerable<ITaskItem> (legacy behavior). if (_hostObject is IEnumerable<ITaskItem> enumerableHost) { _log.LogMessage(MessageImportance.Low, "Falling back to IEnumerable<ITaskItem> host object."); return enumerableHost; } return null; static TaskItem ConvertToTaskItem(TaskItemDto dto) { TaskItem taskItem = new(dto.ItemSpec ?? string.Empty); if (dto.Metadata is not null) { if (dto.Metadata.TryGetValue(UserMetaDataName, out string? userName)) { taskItem.SetMetadata(UserMetaDataName, userName); } if (dto.Metadata.TryGetValue(PasswordMetaDataName, out string? password)) { taskItem.SetMetadata(PasswordMetaDataName, password); } } return taskItem; } } private readonly record struct TaskItemDto(string? ItemSpec, Dictionary<string, string>? Metadata); }