Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hyper-V settings card styled adaptive cards #2489

Merged
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 2 additions & 0 deletions HyperVExtension/src/HyperVExtension/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ internal sealed class Constants
#else
public const string ExtensionIcon = "ms-resource://Microsoft.Windows.DevHome.Dev/Files/HyperVExtension/Assets/hyper-v-provider-icon.png";
#endif

public const string ExtensionIconInternal = "ms-appx:///HyperVExtension/Assets/hyper-v-provider-icon.png";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HyperVExtension.Exceptions;

public class AdaptiveCardInvalidActionException : Exception
{
public AdaptiveCardInvalidActionException(string message)
: base(message)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public string? Type
get; set;
}

public string? ActionMode
{
get; set;
}

public bool IsCancelAction()
{
return Id == "cancelAction";
Expand All @@ -54,4 +59,14 @@ public bool IsExecuteAction()
{
return Type == "Action.Execute";
}

public bool IsSecondaryAction()
{
return ActionMode == "Secondary";
}

public bool IsPrimaryAction()
{
return ActionMode == "Primary";
}
}
9 changes: 8 additions & 1 deletion HyperVExtension/src/HyperVExtension/HyperVExtension.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<Content Include="Templates\InitialVMGalleryCreationForm.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Templates\ReviewFormForVMGallery.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MessageFormat" Version="6.0.2" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ public sealed class VMGalleryDisk : VMGalleryItemWithHashBase
{
public string ArchiveRelativePath { get; set; } = string.Empty;

public long SizeInBytes { get; set; }
public ulong SizeInBytes { get; set; }
}
16 changes: 12 additions & 4 deletions HyperVExtension/src/HyperVExtension/Providers/HyperVProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// Licensed under the MIT License.

using System.Text.Json;
using DevHome.Common;
using HyperVExtension.Common;
using HyperVExtension.Helpers;
using HyperVExtension.Models;
using HyperVExtension.Models.VirtualMachineCreation;
using HyperVExtension.Services;
using Microsoft.Windows.DevHome.SDK;
Expand All @@ -25,14 +27,21 @@ public class HyperVProvider : IComputeSystemProvider

private readonly VmGalleryCreationOperationFactory _vmGalleryCreationOperationFactory;

private readonly IVMGalleryService _vmGalleryService;

// Temporary will need to add more error strings for different operations.
public string OperationErrorString => _stringResource.GetLocalized(errorResourceKey);

public HyperVProvider(IHyperVManager hyperVManager, IStringResource stringResource, VmGalleryCreationOperationFactory vmGalleryCreationOperationFactory)
public HyperVProvider(
IHyperVManager hyperVManager,
IStringResource stringResource,
VmGalleryCreationOperationFactory vmGalleryCreationOperationFactory,
IVMGalleryService vmGalleryService)
{
_hyperVManager = hyperVManager;
_stringResource = stringResource;
_vmGalleryCreationOperationFactory = vmGalleryCreationOperationFactory;
_vmGalleryService = vmGalleryService;
}

/// <summary> Gets or sets the default compute system properties. </summary>
Expand Down Expand Up @@ -75,9 +84,8 @@ public IAsyncOperation<ComputeSystemsResult> GetComputeSystemsAsync(IDeveloperId

public ComputeSystemAdaptiveCardResult CreateAdaptiveCardSessionForDeveloperId(IDeveloperId developerId, ComputeSystemAdaptiveCardKind sessionKind)
{
// This won't be supported until creation is supported.
var notImplementedException = new NotImplementedException($"Method not implemented by Hyper-V Compute System Provider");
return new ComputeSystemAdaptiveCardResult(notImplementedException, OperationErrorString, notImplementedException.Message);
var imageList = _vmGalleryService.GetGalleryImagesAsync().GetAwaiter().GetResult();
bbonaby marked this conversation as resolved.
Show resolved Hide resolved
return new ComputeSystemAdaptiveCardResult(new VMGalleryCreationAdaptiveCardSession(imageList, _stringResource));
}

public ComputeSystemAdaptiveCardResult CreateAdaptiveCardSessionForComputeSystem(IComputeSystem computeSystem, ComputeSystemAdaptiveCardKind sessionKind)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public async Task<byte[]> DownloadByteArrayAsync(string sourceWebUri, Cancellati
return await httpClient.GetByteArrayAsync(sourceWebUri, cancellationToken);
}

public async Task<long> GetHeaderContentLength(Uri sourceWebUri, CancellationToken cancellationToken)
{
var httpClient = _httpClientFactory.CreateClient();
return GetTotalBytesToReceive(await httpClient.GetAsync(sourceWebUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken));
}

private long GetTotalBytesToReceive(HttpResponseMessage response)
{
if (response.Content.Headers.ContentLength.HasValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ public interface IDownloaderService
/// <param name="cancellationToken">A token that can allow the operation to be cancelled while it is running</param>
/// <returns>Content returned by web server represented as an array of bytes</returns>
public Task<byte[]> DownloadByteArrayAsync(string sourceWebUri, CancellationToken cancellationToken);

public Task<long> GetHeaderContentLength(Uri sourceWebUri, CancellationToken cancellationToken);
}
10 changes: 10 additions & 0 deletions HyperVExtension/src/HyperVExtension/Services/VMGalleryService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Globalization;
using System.Security.Cryptography;
using System.Text.Json;
using HyperVExtension.Models.VMGalleryJsonToClasses;
Expand Down Expand Up @@ -73,6 +74,15 @@ public async Task<VMGalleryImageList> GetGalleryImagesAsync()

image.Symbol.Base64Image = Convert.ToBase64String(byteArray);
}

if (!string.IsNullOrEmpty(image.Disk.Uri))
{
var totalSizeOfDisk = await _downloaderService.GetHeaderContentLength(new Uri(image.Disk.Uri), cancellationTokenSource.Token);
if (ulong.TryParse(image.Requirements.DiskSpace, CultureInfo.InvariantCulture, out var requiredDiskSpace))
{
image.Disk.SizeInBytes = (ulong)totalSizeOfDisk;
}
}
}
}
catch (Exception ex)
Expand Down
68 changes: 68 additions & 0 deletions HyperVExtension/src/HyperVExtension/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,72 @@
<value>Please log on to your Hyper-V</value>
<comment>Title text of the dialog asking to log in to Hyper-V VM.</comment>
</data>
<data name="AdaptiveCardStateNotRecognizedError" xml:space="preserve">
<value>Adaptive card state not recognized</value>
<comment>Error text to show when we don't recognize the state of the adaptive card that was given to us</comment>
</data>
<data name="AdaptiveCardUnRecognizedAction" xml:space="preserve">
<value>Action passed to the extension was not recognized. View the extension logs for more information</value>
<comment>Error text to show when we don't recognize the adaptive card action that was passed to the extension</comment>
</data>
<data name="ButtonToLaunchContentDialogLabel" xml:space="preserve">
<value>More Info</value>
<comment>Text for a button that will launch a content dialog that displays more information to the user about a disk image</comment>
</data>
<data name="DownloadForContentDialog" xml:space="preserve">
<value>Download</value>
<comment>label text for the download size of the disk image</comment>
</data>
<data name="EnterNewVMNameLabel" xml:space="preserve">
<value>New virtual machine name</value>
<comment>Label text for textbox where users will enter the name for their new virtual machine</comment>
</data>
<data name="EnterNewVMNamePlaceHolder" xml:space="preserve">
<value>Enter the name of your new virtual machine</value>
<comment>place holder text that will appear within a text box</comment>
</data>
<data name="InitialCreationFormGenerationFailedError" xml:space="preserve">
<value>Failed to generate the initial creation form</value>
<comment>Error text to show the user when the was an error getting the initial disk image selection page in the creation wizard flow</comment>
</data>
<data name="LastUpdatedForContentDialog" xml:space="preserve">
<value>Last updated</value>
<comment>label text for when the disk image was last updated</comment>
</data>
<data name="LocaleForContentDialog" xml:space="preserve">
<value>Locale</value>
<comment>label text for locale of operation system that is installed on the disk image</comment>
</data>
<data name="NameLabelForNewVirtualMachine" xml:space="preserve">
<value>Name{0}</value>
<comment>Locked="{0}" text label that will be on top of the name the user provides in a textbox. {0} is the colon e.g ":" special character</comment>
</data>
<data name="OsVersionForContentDialog" xml:space="preserve">
<value>Version</value>
<comment>label text for version of operating system that is installed on the disk image</comment>
</data>
<data name="PrimaryButtonForContentDialogText" xml:space="preserve">
<value>Ok</value>
<comment>Text for primary button of the content dialog </comment>
</data>
<data name="PrimaryButtonLabelForCreationFlow" xml:space="preserve">
<value>Next</value>
<comment>Text to display to the user about what the primary button does in the UI</comment>
</data>
<data name="ReviewFormGenerationFailedError" xml:space="preserve">
<value>Failed to generate the review form</value>
<comment>Error text to show the user when the was an error getting the review page in the creation wizard flow</comment>
</data>
<data name="SecondaryButtonForContentDialogText" xml:space="preserve">
<value>Cancel</value>
<comment>Text for the secondary button of the content dialog</comment>
</data>
<data name="SecondaryButtonLabelForCreationFlow" xml:space="preserve">
<value>Previous</value>
<comment>Text to display to the user about what the secondary button does in the UI.</comment>
</data>
<data name="SettingsCardLabel" xml:space="preserve">
<value>Choose an image to use</value>
<comment>Label text for a list of cards that appear in the UI</comment>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Input.Text",
"id": "NewVMNameTextBox",
"label": "${EnterNewVMNameLabel}",
"placeholder": "${EnterNewVMNamePlaceHolder}",
"maxLength": 100,
"isRequired": true,
"Spacing": "Large"
},
{
"type": "DevHome.SettingsCardChoiceSet",
"id": "HyperVVmGalleryImageList",
"label": "${SettingsCardLabel}",
"DevHomeSettingsCards": [
{
"type": "DevHome.SettingsCard",
"$data": "${GalleryImages}",
"DevHomeSettingsCardDescription": "${SubDescription}",
"DevHomeSettingsCardHeader": "${Header}",
"DevHomeSettingsCardHeaderIcon": "${HeaderIcon}",
"DevHomeSettingsCardActionElement": {
"type": "DevHome.LaunchContentDialogButton",
"DevHomeActionText": "${ButtonToLaunchContentDialogLabel}",
"DevHomeContentDialogContent": {
"DevHomeContentDialogTitle": "${Header}",
"DevHomeContentDialogBodyAdaptiveCard": {
"type": "AdaptiveCard",
"version": "1.5",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"body": [
{
"type": "Container",
"$data": "${ContentDialogInfo}",
"items": [
{
"type": "TextBlock",
"text": "${ImageDescription}",
"isMultiline": true,
"Spacing": "Medium",
"size": "Medium",
"wrap": true
},
{
"$data": "${GalleryImageFacts}",
"type": "FactSet",
"facts": [
{
"title": "${title}",
"value": "${value}"
}
]
}
]
}
]
},
"DevHomeContentDialogSecondaryButtonText": "${SecondaryButtonForContentDialogText}"
}
}
}
]
},
{
"type": "ActionSet",
"actions": [
{
"id": "VmGalleryPrimaryButtonId",
"type": "Action.Submit",
"title": "${PrimaryButtonLabelForCreationFlow}",
"ActionMode": "Primary",
"style": "positive"
},
{
"id": "VmGallerySecondaryButtonId",
"type": "Action.Submit",
"title": "${SecondaryButtonLabelForCreationFlow}",
"ActionMode": "Secondary"
}
]
}
]
}
Loading
Loading