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

App Center Integration #234

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

awolowiecki720
Copy link
Contributor

@jwittner App center integration with API token for auth.

@awolowiecki720 awolowiecki720 changed the title Dev/credential support App Center Integration Jan 10, 2018
@awolowiecki720
Copy link
Contributor Author

@davidkline-ms Hey! Please take a look at these changes. This is the beginning of a feature that integrates the MR Commander with the Visual Studio App Center. The goal is to streamline the build and release process for our projects so new builds can get installed on device quickly and more easily. The feature uses token auth to make web calls to the App Center and then deserializes the JSON response and updates the view model. I built functionality for storing the auth token, classes for storing JSON responses, and a view model that will display available apps and allow the user to download them. I would like feedback on where this code should live in the project so that it is easiest to maintain.

Copy link

@david-c-kline david-c-kline left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a cool feature idea.

Other than the user token, are there any values the user may need/want to set to effectively use the app center? Is the token a well known value to the user or should the Commander app enable launching a browser to allow them to aquire the token.

Thanks!
David

((SetAPItokenDialogViewModel)this.DataContext).UpdateAPItoken(this.apiToken);
}

private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to get rid of this method by not specifying a click event for the secondary button. This is what the Connect to Device dialog does.

}
set
{
if (this.apiToken!=value)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this.apiToken != value

Copy link

@david-c-kline david-c-kline left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also share screenshots of the UI changes (main window and dialogs)? That would help understand if the overall interface can be optimized.

Thanks!


namespace HoloLensCommander
{
public class UserToken

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we will need anything other than the token string?
Is the token string in a special format that should be validated?
If not, I would recommend removing the UserToken class and just use the string throughout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms No, so far I have only needed the token. I am also working to add username and password support, but am using a NetworkCredentials object for that.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe encapsulate everything here (network credential / token string)? i definitely recommend requiring the user to enter credentials at least on first use during a session. these credentials would pose a security risk.

As an asside, i am considering removing the device portal credential storage in a future release

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I agree it should definitely not be stored as plain text. What about storing it as a secure string? What kind of credential manager is built in to UWP apps?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecureString may work, but I haven't tried storing it in a config file. Havent researched credential manangement in UWP apps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.microsoft.com/en-us/appcenter/api-docs/ This is what we're attempting to use. App Center recommends issuing a token to make API calls. I can't find any docs on app center for how to use OAuth. Do you know how?

x:Name="UserEmailLabel"
Text="Email"
FontSize="16"/>
<ListBox

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to make this multi-select?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms Yes, we could make it multi-select to allow for multiple downloads at once. That would require some additional steps for making the download call async and queuing up concurrent downloads, correct?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are probably right. Unless the AppCenter has an endpoint for "install these". Probably something best left for the next rev :)

Copy link
Contributor Author

@awolowiecki720 awolowiecki720 Jan 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my knowledge there is not. I am using the download URL and then caching that downloaded data. There is "install" api other than to install to a mobile device for testing.
imgo

//close the dialog
}

private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no secondary button in the XAML, no need to have this method.

@@ -0,0 +1,26 @@
using Newtonsoft.Json;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, this project's only dependency is on WindowsDevicePortalWrapper. Is the json data from the app center complex enough to warrant using the Netwtonsoft component? If not, I would prefer we not take an additional dependency.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can help with the deserialization code if we can avoid the component,.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms Yes, there are only 2 instances of deserializing the JSON response data, and it’s a relatively simple object (~15 fields total, but I only use 4). Help with this would be awesome so that we do not add more dependencies.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Since you only use the four, we should be able to only define the subset when configuring the object for the built in json serializer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms what's the easiest way to write our own json parser for these objects? Are you thinking we parse the strings ourselves? Or are there built in functions I am not aware of?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here is a sample class definition from the WindowsDevicePortalWrapper project:

     using System.Runtime.Serialization;

    [DataContract]
    public class BatteryState
    {
        [DataMember(Name = "AcOnline")]
        public bool IsOnAcPower { get; private set; }

        [DataMember(Name = "BatteryPresent")]
        public bool IsBatteryPresent { get; private set; }

        [DataMember(Name = "Charging")]
        public bool IsCharging { get; private set; }

        [DataMember(Name = "DefaultAlert1")]
        public int DefaultAlert1 { get; private set; }

        [DataMember(Name = "DefaultAlert2")]
        public int DefaultAlert2 { get; private set; }

        [DataMember(Name = "EstimatedTime")]
        public uint EstimatedTimeRaw { get; private set; }

        [DataMember(Name = "MaximumCapacity")]
        public int MaximumCapacity { get; private set; }

        [DataMember(Name = "RemainingCapacity")]
        public int RemainingCapacity { get; private set; }
    }

Copy link

@david-c-kline david-c-kline Jan 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the BatteryState example, if all you cared about was whether or not a battery existed and if the system was plugged in, you could include only the first two members of the class.


namespace HoloLensCommander
{
public class AppResponse

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on this class definition, we can easily eliminate the newtonsoft dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms Sounds good; How do you recommend deserializing without newtonsoft?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Over in the WindowsDevicePortalWrapper project (https://github.com/Microsoft/WindowsDevicePortalWrapper), take a look at how the OperatingSystemInformation object is defined (https://github.com/Microsoft/WindowsDevicePortalWrapper/blob/master/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/Core/OsInformation.cs).

The ResponseHelpers.cs file (https://github.com/Microsoft/WindowsDevicePortalWrapper/blob/master/WindowsDevicePortalWrapper/WindowsDevicePortalWrapper.Shared/HttpRest/ResponseHelpers.cs) shows how we validate the data format and read the contents.

As mentioned before, i am happy to help with this as i put together the original version of the WDPW json code.

request.Headers.Accept.Add(new HttpMediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Append("X-API-Token", "9b8e356d29f80de93d88fac0252e48f8fbe22e96");

Uri uri = new Uri("https://api.appcenter.ms/v0.1/apps");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to read this from settings. I expect it will change given that it is a v0.1 endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms Same as above, I am curious about best practices for saving API endpoints in a way that they can be easily maintained/updated.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way the app currently handles this is to create a static readonly or const variable that stores the default value (ex: https://api.appcenter.ms/v0.1/apps). If the user enters a different value into the settings dialog, the value is written into the cache. on startup, if there is a value in the cache it is used, otherwise use the default

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so saving the apis in a config file and loading it on startup could work here. If the user needs to change the endpoints, they could open the config file in the app's cache and make changes. It seems weird to expose request header names and endpoints through the UI.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it is a bit odd to expose that info in the UI. Maybe that should be something we just document in the issues section to update the code / config file when the endpoint changes.

var request = new HttpRequestMessage();

request.Headers.Accept.Add(new HttpMediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Append("X-API-Token", "9b8e356d29f80de93d88fac0252e48f8fbe22e96");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to read this from settings (with a const variable that defines the currently known value) just in case it changes when the API / endpoint is revised,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms I intend to save/read the token from localcache. What are best practices for defining API endpoints in the app and reading them from settings?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see below

@@ -2,6 +2,7 @@
"dependencies": {
"Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0",
"WindowsDevicePortalWrapper": "0.9.4"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 0.9.5.1 to take advantage of updates and fixes (looks like I may have missed that in a checkin last month-ish)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidkline-ms Sounds good. Is there a cadence for updating dependency versions?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I typically try to update whenever the wrapper issues a new version. There may be a delay if the changes do not directly impact the Commander app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants