From 78b4f6d0ee5b80682b8da4bd20fc6601fe086e51 Mon Sep 17 00:00:00 2001 From: Amber Erickson Date: Wed, 5 Oct 2022 15:16:46 -0700 Subject: [PATCH 1/3] Beginning of impl for converting xml to PSResourceInfo obj --- src/code/FindPSResource.cs | 5 ++ src/code/HttpFindPSResource.cs | 55 +++++++++++--- src/code/IFindPSResource.cs | 3 +- src/code/IServerAPICalls.cs | 4 +- src/code/PSResourceInfo.cs | 131 ++++++++++++++++++++++++++++++++- src/code/Utils.cs | 16 +++- src/code/V2ServerAPICalls.cs | 7 +- 7 files changed, 202 insertions(+), 19 deletions(-) diff --git a/src/code/FindPSResource.cs b/src/code/FindPSResource.cs index 41644153a..d6fd50035 100644 --- a/src/code/FindPSResource.cs +++ b/src/code/FindPSResource.cs @@ -182,6 +182,11 @@ protected override void ProcessRecord() private void ProcessResourceNameParameterSet() { + // This is for testing/debugging purposes, will be removed later + HttpFindPSResource httpFindPSResource = new HttpFindPSResource(); + httpFindPSResource.FindName(Name.FirstOrDefault(), Repository.FirstOrDefault(), Prerelease, out string errRecord); + + if (!MyInvocation.BoundParameters.ContainsKey(nameof(Name))) { // only cases where Name is allowed to not be specified is if Type or Tag parameters are diff --git a/src/code/HttpFindPSResource.cs b/src/code/HttpFindPSResource.cs index 509e2c8cd..e2c90b68d 100644 --- a/src/code/HttpFindPSResource.cs +++ b/src/code/HttpFindPSResource.cs @@ -1,6 +1,8 @@ +using System.Xml; using Microsoft.PowerShell.PowerShellGet.UtilClasses; using NuGet.Versioning; using System.Collections.Generic; +using System.Xml.Schema; namespace Microsoft.PowerShell.PowerShellGet.Cmdlets { @@ -8,6 +10,12 @@ internal class HttpFindPSResource : IFindPSResource { V2ServerAPICalls v2ServerAPICall = new V2ServerAPICalls(); + #region Constructor + + public HttpFindPSResource() {} + + #endregion + #region Methods /// @@ -36,16 +44,6 @@ public PSResourceInfo FindAll(PSRepositoryInfo repository, bool includePrereleas /* // Convert to PSResourceInfo object - if (!PSResourceInfo.TryConvert( - metadataToParse: pkg, - psGetInfo: out PSResourceInfo currentPkg, - repositoryName: repositoryName, - type: _type, - errorMsg: out string errorMsg)) - { - errRecord = $"Error parsing IPackageSearchMetadata to PSResourceInfo with message: {errorMsg}"; - yield break; - } */ return currentPkg; } @@ -148,11 +146,25 @@ public PSResourceInfo FindCommandName(string[] commandNames, PSRepositoryInfo re /// - Include prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet' /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease dependening on user preference) /// - public PSResourceInfo FindName(string packageName, PSRepositoryInfo repository, bool includePrerelease, out string errRecord) + /// // Note, change repository back to PSRepositoryInfo + public PSResourceInfo FindName(string packageName, string repository, bool includePrerelease, out string errRecord) { // Same API calls for both prerelease and non-prerelease var response = v2ServerAPICall.FindName(packageName, repository, out errRecord); + var elemList = ConvertResponseToXML(response); + + // Loop through and try to convert each xml entry into a PSResourceInfo object + for (int i = 0; i < elemList.Length; i++) + { + PSResourceInfo.TryConvertFromXml( + elemList[i], + out PSResourceInfo2 psGetInfo, + "PSGallery", + null, + out string errorMsg); + } + PSResourceInfo currentPkg = null; if (!string.IsNullOrEmpty(errRecord)) { @@ -366,6 +378,27 @@ public PSResourceInfo FindNamesAndVersionGlobbing(string[] packageNames, Version } + #endregion + + #region HelperMethods + + // TODO: in progress + public XmlNode[] ConvertResponseToXML(string httpResponse) { + + //Create the XmlDocument. + XmlDocument doc = new XmlDocument(); + doc.LoadXml(httpResponse); + + XmlNodeList elemList = doc.GetElementsByTagName("m:properties"); + + XmlNode[] nodes = new XmlNode[elemList.Count]; + for (int i=0; i - PSResourceInfo FindName(string packageName, PSRepositoryInfo repository, bool includePrerelease, out string errRecord); + /// // TODO: change repository from string to PSRepositoryInfo + PSResourceInfo FindName(string packageName, string repository, bool includePrerelease, out string errRecord); /// /// Find method which allows for searching for single name with wildcards and returns latest version. diff --git a/src/code/IServerAPICalls.cs b/src/code/IServerAPICalls.cs index 99babbb42..c205ac210 100644 --- a/src/code/IServerAPICalls.cs +++ b/src/code/IServerAPICalls.cs @@ -92,7 +92,8 @@ public interface IServerAPICalls /// - Include prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet' /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease dependening on user preference) /// - string FindName(string packageName, PSRepositoryInfo repository, out string errRecord); + /// // TODO: change repository from string to PSRepositoryInfo + string FindName(string packageName, string repository, out string errRecord); /// /// Find method which allows for searching for single name with version range. @@ -114,7 +115,6 @@ public interface IServerAPICalls /// Implementation Note: filter additionally and verify ONLY package name was a match. /// string FindNameGlobbingWithPrerelease(string packageName, PSRepositoryInfo repository, out string errRecord); - { /// diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs index ca6057002..ba4bdff4a 100644 --- a/src/code/PSResourceInfo.cs +++ b/src/code/PSResourceInfo.cs @@ -1,3 +1,4 @@ +using System.Xml; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -9,6 +10,7 @@ using System.Globalization; using System.Linq; using System.Management.Automation; +using System.Xml.Serialization; using Dbg = System.Diagnostics.Debug; @@ -535,7 +537,7 @@ public static bool TryConvert( licenseUri: ParseMetadataLicenseUri(metadataToParse), name: ParseMetadataName(metadataToParse), packageManagementProvider: null, - powershellGetFormatVersion: null, + powershellGetFormatVersion: null, prerelease: ParsePrerelease(metadataToParse), projectUri: ParseMetadataProjectUri(metadataToParse), publishedDate: ParseMetadataPublishedDate(metadataToParse), @@ -560,6 +562,45 @@ public static bool TryConvert( } } + // TODO: in progress + // write a serializer + public static bool TryConvertFromXml( + XmlNode entry, // XmlDocument doc that is already loaded + out PSResourceInfo2 psGetInfo, + string repositoryName, + ResourceType? type, + out string errorMsg) + { + psGetInfo = null; + errorMsg = String.Empty; + + if (entry == null) + { + errorMsg = "TryConvertXmlToPSResourceInfo: Invalid XmlNodeList object. Object cannot be null."; + return false; + } + + try + { + //XmlNodeList elemList = doc.GetElementsByTagName("m:properties"); + + var xNodeReader = new XmlNodeReader(entry); + var xmlSerializer = new XmlSerializer(typeof(PSResourceInfo2)); + psGetInfo = xmlSerializer.Deserialize(xNodeReader) as PSResourceInfo2; + + // Note: still need to add some info to the psGetInfo obj, + return true; + } + catch (Exception ex) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryReadPSGetInfo: Cannot parse PSResourceInfo from XmlNode with error: {0}", + ex.Message); + return false; + } + } + #endregion #region Private static methods @@ -949,6 +990,94 @@ private PSObject ConvertToCustomObject() #endregion + // TODO: tmp class for testing/debugging + #region PSResourceInfo2 + + public sealed class PSResourceInfo2 + { + #region Properties + + public string Author { get; set;} + public string Copyright { get; set; } + public string Description { get; set;} + public string IconUri { get; set; } + public ResourceIncludes Includes { get; set;} + public DateTime? InstalledDate { get; set; } + public string InstalledLocation { get; set; } + public bool IsPrerelease { get; set;} + public string LicenseUri { get; set;} + public string Name { get; set;} + public string PackageManagementProvider { get; set;} + public string PowerShellGetFormatVersion { get; set;} + public string Prerelease { get; set;} + public string ProjectUri { get; set;} + public DateTime? PublishedDate { get; set;} + public string ReleaseNotes { get; set;} + public string Repository { get; set; } + public string RepositorySourceLocation { get; set; } + public string[] Tags { get; set;} + public ResourceType Type { get; set;} + public DateTime? UpdatedDate { get; set;} + public Version Version { get; set;} + + #endregion + + #region Constructors + + private PSResourceInfo2() { } + + private PSResourceInfo2( + string author, + string copyright, + string description, + string iconUri, + ResourceIncludes includes, + DateTime? installedDate, + string installedLocation, + bool isPrelease, + string licenseUri, + string name, + string packageManagementProvider, + string powershellGetFormatVersion, + string prerelease, + string projectUri, + DateTime? publishedDate, + string releaseNotes, + string repository, + string repositorySourceLocation, + string[] tags, + ResourceType type, + DateTime? updatedDate, + Version version) + { + Author = author ?? string.Empty; + Copyright = copyright ?? string.Empty; + Description = description ?? string.Empty; + IconUri = iconUri; + Includes = includes ?? new ResourceIncludes(); + InstalledDate = installedDate; + InstalledLocation = installedLocation ?? string.Empty; + IsPrerelease = isPrelease; + LicenseUri = licenseUri; + Name = name ?? string.Empty; + PackageManagementProvider = packageManagementProvider ?? string.Empty; + PowerShellGetFormatVersion = powershellGetFormatVersion ?? string.Empty; + Prerelease = prerelease ?? string.Empty; + ProjectUri = projectUri; + PublishedDate = publishedDate; + ReleaseNotes = releaseNotes ?? string.Empty; + Repository = repository ?? string.Empty; + RepositorySourceLocation = repositorySourceLocation ?? string.Empty; + Tags = tags ?? Utils.EmptyStrArray; + Type = type; + UpdatedDate = updatedDate; + Version = version ?? new Version(); + } + #endregion + + } + #endregion + #region Test Hooks public static class TestHooks diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 3173b832c..5c8b214b7 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -1193,7 +1193,7 @@ private static void RestoreDirContents( #region HttpRequest - public static async Task SendRequestAsync(HttpRequestMessage message, HttpClient s_client) + public static async Task SendV3RequestAsync(HttpRequestMessage message, HttpClient s_client) { try { @@ -1207,6 +1207,20 @@ public static async Task SendRequestAsync(HttpRequestMessage message, H } } + public static async Task SendV2RequestAsync(HttpRequestMessage message, HttpClient s_client) + { + try + { + HttpResponseMessage response = await s_client.SendAsync(message); + response.EnsureSuccessStatusCode(); + return response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + } + catch (HttpRequestException e) + { + throw new HttpRequestException("Error occured while trying to retrieve response: " + e.Message); + } + } + #endregion } diff --git a/src/code/V2ServerAPICalls.cs b/src/code/V2ServerAPICalls.cs index 3803140e3..1ea6e1b90 100644 --- a/src/code/V2ServerAPICalls.cs +++ b/src/code/V2ServerAPICalls.cs @@ -165,9 +165,10 @@ public string FindCommandNameWithPrerelease(string[] commandNames, PSRepositoryI /// - Include prerelease: http://www.powershellgallery.com/api/v2/FindPackagesById()?id='PowerShellGet' /// Implementation Note: Need to filter further for latest version (prerelease or non-prerelease dependening on user preference) /// - public string FindName(string packageName, PSRepositoryInfo repository, out string errRecord) { + /// // TODO: change repository from string to PSRepositoryInfo + public string FindName(string packageName, string repository, out string errRecord) { // Make sure to include quotations around the package name - var requestUrlV2 = $"{repository.Uri}/FindPackagesById()?id='{packageName}'"; + var requestUrlV2 = $"{repository}/FindPackagesById()?id='{packageName}'"; return HttpRequestCall(requestUrlV2, out errRecord); } @@ -284,7 +285,7 @@ private static string HttpRequestCall(string requestUrlV2, out string errRecord) HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrlV2); // We can have this return a Task, or the response (json string) - var response = Utils.SendRequestAsync(request, s_client).GetAwaiter().GetResult(); + var response = Utils.SendV2RequestAsync(request, s_client).GetAwaiter().GetResult(); // Do we want to check if response is 200? // response will be json metadata object that will get returned From 2c82bcd82a3e6ef477f471ceee6058658563053f Mon Sep 17 00:00:00 2001 From: Amber Erickson Date: Sun, 9 Oct 2022 23:41:37 -0700 Subject: [PATCH 2/3] Add xml properties to PSResourceInfo class --- src/code/HttpFindPSResource.cs | 2 +- src/code/PSResourceInfo.cs | 117 ++++++--------------------------- 2 files changed, 22 insertions(+), 97 deletions(-) diff --git a/src/code/HttpFindPSResource.cs b/src/code/HttpFindPSResource.cs index e2c90b68d..fade89680 100644 --- a/src/code/HttpFindPSResource.cs +++ b/src/code/HttpFindPSResource.cs @@ -159,7 +159,7 @@ public PSResourceInfo FindName(string packageName, string repository, bool inclu { PSResourceInfo.TryConvertFromXml( elemList[i], - out PSResourceInfo2 psGetInfo, + out PSResourceInfo psGetInfo, "PSGallery", null, out string errorMsg); diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs index 17142f68b..c1a0ab39e 100644 --- a/src/code/PSResourceInfo.cs +++ b/src/code/PSResourceInfo.cs @@ -230,34 +230,51 @@ public PSCommandResourceInfo(string name, PSResourceInfo parentResource) #region PSResourceInfo + [Serializable] + [XmlRoot(ElementName = "properties", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")] public sealed class PSResourceInfo { #region Properties public Dictionary AdditionalMetadata { get; } + [XmlElement("Authors", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public string Author { get; } + [XmlElement("CompanyName", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public string CompanyName { get; internal set; } + [XmlElement("Copyright", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public string Copyright { get; internal set; } + [XmlElement("Dependencies", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public Dependency[] Dependencies { get; } + [XmlElement("Description", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public string Description { get; } + [XmlElement("IconUrl", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public Uri IconUri { get; } public ResourceIncludes Includes { get; } public DateTime? InstalledDate { get; internal set; } public string InstalledLocation { get; internal set; } + [XmlElement("IsPrerelease", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public bool IsPrerelease { get; } + [XmlElement("LicenseUrl", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public Uri LicenseUri { get; } + [XmlElement("Id", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public string Name { get; } public string PackageManagementProvider { get; } public string PowerShellGetFormatVersion { get; } public string Prerelease { get; } + [XmlElement("ProjectUrl", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public Uri ProjectUri { get; } + [XmlElement("Published", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public DateTime? PublishedDate { get; } + [XmlElement("ReleaseNotes", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public string ReleaseNotes { get; internal set; } public string Repository { get; } public string RepositorySourceLocation { get; internal set; } + [XmlElement("Tags", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] + public string[] Tags { get; } public ResourceType Type { get; } public DateTime? UpdatedDate { get; } + [XmlElement("Version", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] public Version Version { get; } #endregion @@ -565,8 +582,8 @@ public static bool TryConvert( // TODO: in progress // write a serializer public static bool TryConvertFromXml( - XmlNode entry, // XmlDocument doc that is already loaded - out PSResourceInfo2 psGetInfo, + XmlNode entry, + out PSResourceInfo psGetInfo, string repositoryName, ResourceType? type, out string errorMsg) @@ -583,8 +600,8 @@ public static bool TryConvertFromXml( try { var xNodeReader = new XmlNodeReader(entry); - var xmlSerializer = new XmlSerializer(typeof(PSResourceInfo2)); - psGetInfo = xmlSerializer.Deserialize(xNodeReader) as PSResourceInfo2; + var xmlSerializer = new XmlSerializer(typeof(PSResourceInfo)); + psGetInfo = xmlSerializer.Deserialize(xNodeReader) as PSResourceInfo; // Note: still need to add some info to the psGetInfo obj, return true; @@ -988,98 +1005,6 @@ private PSObject ConvertToCustomObject() #endregion - // TODO: tmp class for testing/debugging - #region PSResourceInfo2 - - [Serializable] - [XmlRoot(ElementName = "properties", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")] - public sealed class PSResourceInfo2 - { - #region Properties - - public string Author { get; set;} - public string Copyright { get; set; } - public string Description { get; set;} - public string IconUri { get; set; } - public ResourceIncludes Includes { get; set;} - public DateTime? InstalledDate { get; set; } - public string InstalledLocation { get; set; } - public bool IsPrerelease { get; set;} - public string LicenseUri { get; set;} - - [XmlElement("Id", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public string Name { get; set;} - public string PackageManagementProvider { get; set;} - public string PowerShellGetFormatVersion { get; set;} - public string Prerelease { get; set;} - public string ProjectUri { get; set;} - public DateTime? PublishedDate { get; set;} - public string ReleaseNotes { get; set;} - public string Repository { get; set; } - public string RepositorySourceLocation { get; set; } - public string[] Tags { get; set;} - public ResourceType Type { get; set;} - public DateTime? UpdatedDate { get; set;} - public Version Version { get; set;} - - #endregion - - #region Constructors - - private PSResourceInfo2() { } - - private PSResourceInfo2( - string author, - string copyright, - string description, - string iconUri, - ResourceIncludes includes, - DateTime? installedDate, - string installedLocation, - bool isPrelease, - string licenseUri, - string name, - string packageManagementProvider, - string powershellGetFormatVersion, - string prerelease, - string projectUri, - DateTime? publishedDate, - string releaseNotes, - string repository, - string repositorySourceLocation, - string[] tags, - ResourceType type, - DateTime? updatedDate, - Version version) - { - Author = author ?? string.Empty; - Copyright = copyright ?? string.Empty; - Description = description ?? string.Empty; - IconUri = iconUri; - Includes = includes ?? new ResourceIncludes(); - InstalledDate = installedDate; - InstalledLocation = installedLocation ?? string.Empty; - IsPrerelease = isPrelease; - LicenseUri = licenseUri; - Name = name ?? string.Empty; - PackageManagementProvider = packageManagementProvider ?? string.Empty; - PowerShellGetFormatVersion = powershellGetFormatVersion ?? string.Empty; - Prerelease = prerelease ?? string.Empty; - ProjectUri = projectUri; - PublishedDate = publishedDate; - ReleaseNotes = releaseNotes ?? string.Empty; - Repository = repository ?? string.Empty; - RepositorySourceLocation = repositorySourceLocation ?? string.Empty; - Tags = tags ?? Utils.EmptyStrArray; - Type = type; - UpdatedDate = updatedDate; - Version = version ?? new Version(); - } - #endregion - - } - #endregion - #region Test Hooks public static class TestHooks From a57e85a22a54b8cc7fd36270b1c04bab57b07d1f Mon Sep 17 00:00:00 2001 From: Amber Erickson Date: Mon, 10 Oct 2022 00:08:35 -0700 Subject: [PATCH 3/3] Add changes for proper deserialization --- src/code/PSResourceInfo.cs | 41 +++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs index c1a0ab39e..ebeca1743 100644 --- a/src/code/PSResourceInfo.cs +++ b/src/code/PSResourceInfo.cs @@ -181,6 +181,11 @@ public sealed class Dependency #region Constructor + /// + /// Parameterless constructor needed for XmlSerializer + /// + public Dependency() { } + /// /// Constructor /// @@ -236,42 +241,42 @@ public sealed class PSResourceInfo { #region Properties + [XmlIgnoreAttribute] public Dictionary AdditionalMetadata { get; } [XmlElement("Authors", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public string Author { get; } + public string Author { get; set; } [XmlElement("CompanyName", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public string CompanyName { get; internal set; } + public string CompanyName { get; set; } [XmlElement("Copyright", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public string Copyright { get; internal set; } + public string Copyright { get; set; } [XmlElement("Dependencies", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public Dependency[] Dependencies { get; } + public Dependency[] Dependencies { get; set; } [XmlElement("Description", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public string Description { get; } + public string Description { get; set; } [XmlElement("IconUrl", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public Uri IconUri { get; } + public Uri IconUri { get; set; } public ResourceIncludes Includes { get; } - public DateTime? InstalledDate { get; internal set; } - public string InstalledLocation { get; internal set; } + public DateTime? InstalledDate { get; set; } + public string InstalledLocation { get; set; } [XmlElement("IsPrerelease", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public bool IsPrerelease { get; } + public bool IsPrerelease { get; set; } [XmlElement("LicenseUrl", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public Uri LicenseUri { get; } + public Uri LicenseUri { get; set; } [XmlElement("Id", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public string Name { get; } + public string Name { get; set; } public string PackageManagementProvider { get; } public string PowerShellGetFormatVersion { get; } public string Prerelease { get; } [XmlElement("ProjectUrl", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public Uri ProjectUri { get; } + public Uri ProjectUri { get; set; } [XmlElement("Published", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public DateTime? PublishedDate { get; } + public DateTime? PublishedDate { get; set; } [XmlElement("ReleaseNotes", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - public string ReleaseNotes { get; internal set; } - public string Repository { get; } - public string RepositorySourceLocation { get; internal set; } + public string ReleaseNotes { get; set; } + public string Repository { get; set; } + public string RepositorySourceLocation { get; set; } [XmlElement("Tags", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")] - - public string[] Tags { get; } + public string[] Tags { get; set; } public ResourceType Type { get; } public DateTime? UpdatedDate { get; } [XmlElement("Version", Namespace = "http://schemas.microsoft.com/ado/2007/08/dataservices")]