diff --git a/Source/HaloSharp.Test/Config/Halo5Config.cs b/Source/HaloSharp.Test/Config/Halo5Config.cs
index 71729d9..2bc0b2b 100644
--- a/Source/HaloSharp.Test/Config/Halo5Config.cs
+++ b/Source/HaloSharp.Test/Config/Halo5Config.cs
@@ -44,6 +44,9 @@ public static class Halo5Config
public const string WeaponsJsonPath = "JSON/Halo5/Metadata/weapons.json";
public const string WeaponsJsonSchemaPath = "JSON/Halo5/Metadata/weapons.schema.json";
+ public const string PlayerAppearancePath = "JSON/Halo5/Profile/player-appearance.json";
+ public const string PlayerAppearanceSchemaPath = "JSON/Halo5/Profile/player-appearance.schema.json";
+
public const string ArenaMatchJsonPath = "JSON/Halo5/Stats/CarnageReport/arena-match.json";
public const string ArenaMatchJsonSchemaPath = "JSON/Halo5/Stats/CarnageReport/arena-match.schema.json";
public const string CampaignMatchJsonPath = "JSON/Halo5/Stats/CarnageReport/campaign-match.json";
diff --git a/Source/HaloSharp.Test/HaloSharp.Test.csproj b/Source/HaloSharp.Test/HaloSharp.Test.csproj
index f6a9592..bbe4857 100644
--- a/Source/HaloSharp.Test/HaloSharp.Test.csproj
+++ b/Source/HaloSharp.Test/HaloSharp.Test.csproj
@@ -86,6 +86,7 @@
+
@@ -270,6 +271,12 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
@@ -623,6 +630,9 @@
HaloSharp
+
+
+
diff --git a/Source/HaloSharp.Test/JSON/Halo5/Profile/player-appearance.json b/Source/HaloSharp.Test/JSON/Halo5/Profile/player-appearance.json
new file mode 100644
index 0000000..1bf5dcb
--- /dev/null
+++ b/Source/HaloSharp.Test/JSON/Halo5/Profile/player-appearance.json
@@ -0,0 +1,14 @@
+{
+ "Gamertag": "Furiousn00b",
+ "LastModifiedUtc": {
+ "ISO8601Date": "2016-07-03T00:00:00Z"
+ },
+ "FirstModifiedUtc": {
+ "ISO8601Date": "2015-10-19T00:00:00Z"
+ },
+ "ServiceTag": "FURI",
+ "Company": {
+ "Id": "a2f47e59-5cc0-44f8-a542-77c162fe69e8",
+ "Name": "Section 3"
+ }
+}
\ No newline at end of file
diff --git a/Source/HaloSharp.Test/JSON/Halo5/Profile/player-appearance.schema.json b/Source/HaloSharp.Test/JSON/Halo5/Profile/player-appearance.schema.json
new file mode 100644
index 0000000..11c38a2
--- /dev/null
+++ b/Source/HaloSharp.Test/JSON/Halo5/Profile/player-appearance.schema.json
@@ -0,0 +1,47 @@
+{
+ "definitions": {
+ "Player-Appearance": {
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "Gamertag": {
+ "type": "string"
+ },
+ "LastModifiedUTC": {
+ "$ref": "../../common/iso-8061.schema.json"
+ },
+ "FirstModifiedUTC": {
+ "$ref": "../../common/iso-8061.schema.json"
+ },
+ "ServiceTag": {
+ "type": "string"
+ },
+ "Company": {
+ "type": [ "array", "null" ],
+ "items": {
+ "Id": {
+ "type": "guid"
+ },
+ "Name": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "required": [
+ "Gamertag",
+ "LastModifiedUTC",
+ "FirstModifiedUTC",
+ "ServiceTag",
+ "Company"
+ ]
+ }
+ },
+
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Player-Appearance"
+ }
+}
\ No newline at end of file
diff --git a/Source/HaloSharp.Test/Query/Halo5/Profile/GetPlayerAppearanceTests.cs b/Source/HaloSharp.Test/Query/Halo5/Profile/GetPlayerAppearanceTests.cs
new file mode 100644
index 0000000..0d898b3
--- /dev/null
+++ b/Source/HaloSharp.Test/Query/Halo5/Profile/GetPlayerAppearanceTests.cs
@@ -0,0 +1,144 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using HaloSharp.Exception;
+using HaloSharp.Extension;
+using HaloSharp.Model;
+using HaloSharp.Model.Common;
+using HaloSharp.Model.Halo5.Profile;
+using HaloSharp.Query.Halo5.Profile;
+using HaloSharp.Test.Config;
+using HaloSharp.Test.Utility;
+using Moq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Schema;
+using NUnit.Framework;
+
+namespace HaloSharp.Test.Query.Halo5.Profile
+{
+ [TestFixture]
+ public class GetPlayerAppearanceTests
+ {
+
+ private IHaloSession _mockSession;
+ private PlayerAppearance _playerAppearance;
+
+ [SetUp]
+ public void Setup()
+ {
+ _playerAppearance = JsonConvert.DeserializeObject(File.ReadAllText(Halo5Config.PlayerAppearancePath));
+
+ var mock = new Mock();
+ mock.Setup(m => m.Get(It.IsAny()))
+ .ReturnsAsync(_playerAppearance);
+
+ _mockSession = mock.Object;
+ }
+
+ [Test]
+ [TestCase("Sn1p3r C")]
+ [TestCase("Furiousn00b")]
+ public void Uri_MatchesExpected(string gamertag)
+ {
+ var query = new GetPlayerAppearance(gamertag);
+
+ Assert.AreEqual($"https://www.haloapi.com/profile/h5/profiles/{gamertag}/appearance", query.Uri);
+ }
+
+ [Test]
+ [TestCase("Furiousn00b")]
+ public async Task Query_DoesNotThrow(string gamertag)
+ {
+ var query = new GetPlayerAppearance(gamertag)
+ .SkipCache();
+
+ var result = await _mockSession.Query(query);
+
+ Assert.IsInstanceOf(typeof(PlayerAppearance), result);
+ Assert.AreEqual(_playerAppearance, result);
+ }
+
+ [Test]
+ [TestCase("Greenskull")]
+ [TestCase("Furiousn00b")]
+ public async Task GetPlayerAppearance_DoesNotThrow(string gamertag)
+ {
+ var query = new GetPlayerAppearance(gamertag)
+ .SkipCache();
+
+ var result = await Global.Session.Query(query);
+
+ Assert.IsInstanceOf(typeof(PlayerAppearance), result);
+ }
+
+ [Test]
+ [TestCase("Greenskull")]
+ [TestCase("Furiousn00b")]
+ public async Task GetPlayerAppearance_SchemaIsValid(string gamertag)
+ {
+ var playerAppearanceSchema = JSchema.Parse(File.ReadAllText(Halo5Config.PlayerAppearancePath), new JSchemaReaderSettings
+ {
+ Resolver = new JSchemaUrlResolver(),
+ BaseUri = new Uri(Path.GetFullPath(Halo5Config.PlayerAppearanceSchemaPath))
+ });
+
+ var query = new GetPlayerAppearance(gamertag)
+ .SkipCache();
+
+ var jArray = await Global.Session.Get(query.Uri);
+
+ SchemaUtility.AssertSchemaIsValid(playerAppearanceSchema, jArray);
+ }
+
+ [Test]
+ [TestCase("Greenskull")]
+ [TestCase("Furiousn00b")]
+ public async Task GetPlayerAppearance_ModelMatchesSchema(string gamertag)
+ {
+
+ var schema = JSchema.Parse(File.ReadAllText(Halo5Config.PlayerAppearancePath), new JSchemaReaderSettings
+ {
+ Resolver = new JSchemaUrlResolver(),
+ BaseUri = new Uri(Path.GetFullPath(Halo5Config.PlayerAppearanceSchemaPath))
+ });
+
+ var query = new GetPlayerAppearance(gamertag)
+ .SkipCache();
+
+ var result = await Global.Session.Query(query);
+
+ var json = JsonConvert.SerializeObject(result);
+ var jContainer = JsonConvert.DeserializeObject(json);
+
+ SchemaUtility.AssertSchemaIsValid(schema, jContainer);
+ }
+
+ [Test]
+ [TestCase("Greenskull")]
+ [TestCase("Furiousn00b")]
+ public async Task GetPlayerAppearance_IsSerializable(string gamertag)
+ {
+ var query = new GetPlayerAppearance(gamertag)
+ .SkipCache();
+
+ var result = await Global.Session.Query(query);
+
+ SerializationUtility.AssertRoundTripSerializationIsPossible(result);
+ }
+
+ [Test]
+ [TestCase("00000000000000017")]
+ [TestCase("!$%")]
+ [ExpectedException(typeof(ValidationException))]
+ public async Task GetPlayerAppearance_InvalidGamertag(string gamertag)
+ {
+ var query = new GetPlayerAppearance(gamertag);
+
+ await Global.Session.Query(query);
+ Assert.Fail("An exception should have been thrown");
+ }
+
+
+ }
+}
diff --git a/Source/HaloSharp/HaloSharp.csproj b/Source/HaloSharp/HaloSharp.csproj
index d9387ac..00972b6 100644
--- a/Source/HaloSharp/HaloSharp.csproj
+++ b/Source/HaloSharp/HaloSharp.csproj
@@ -91,6 +91,7 @@
+
@@ -262,6 +263,7 @@
+
diff --git a/Source/HaloSharp/Model/Halo5/Profile/PlayerAppearance.cs b/Source/HaloSharp/Model/Halo5/Profile/PlayerAppearance.cs
new file mode 100644
index 0000000..96c86b0
--- /dev/null
+++ b/Source/HaloSharp/Model/Halo5/Profile/PlayerAppearance.cs
@@ -0,0 +1,77 @@
+using System;
+using Newtonsoft.Json;
+using HaloSharp.Model.Common;
+
+namespace HaloSharp.Model.Halo5.Profile
+{
+ [Serializable]
+ public class PlayerAppearance : IEquatable
+ {
+ [JsonProperty(PropertyName = "gamertag")]
+ public string Gamertag { get; set; }
+
+ [JsonProperty(PropertyName = "LastModifiedUtc")]
+ public ISO8601 LastModifiedUtc { get; set; }
+
+ [JsonProperty(PropertyName = "FirstModifiedUtc")]
+ public ISO8601 FirstModifiedUtc { get; set; }
+
+ [JsonProperty(PropertyName = "serviceTag")]
+ public string ServiceTag { get; set; }
+
+ [JsonProperty(PropertyName = "Company")]
+ public Company company { get; set; }
+
+ public bool Equals(PlayerAppearance other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return company.Equals(other.company)
+ && Gamertag == other.Gamertag
+ && LastModifiedUtc == other.LastModifiedUtc
+ && FirstModifiedUtc == other.FirstModifiedUtc
+ && ServiceTag == other.ServiceTag;
+ }
+ }
+
+ [Serializable]
+ public class Company : IEquatable
+ {
+ [JsonProperty(PropertyName = "id")]
+ public Guid Id { get; set; }
+
+ [JsonProperty(PropertyName = "name")]
+ public string Name { get; set; }
+
+
+ public bool Equals(Company other)
+ {
+
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return Id == other.Id
+ && Name == other.Name;
+ }
+
+ }
+
+
+
+
+}
diff --git a/Source/HaloSharp/Query/Halo5/Profile/GetPlayerAppearance.cs b/Source/HaloSharp/Query/Halo5/Profile/GetPlayerAppearance.cs
new file mode 100644
index 0000000..54757ae
--- /dev/null
+++ b/Source/HaloSharp/Query/Halo5/Profile/GetPlayerAppearance.cs
@@ -0,0 +1,37 @@
+using HaloSharp.Exception;
+using HaloSharp.Model;
+using HaloSharp.Model.Halo5.Profile;
+using HaloSharp.Validation.Common;
+
+namespace HaloSharp.Query.Halo5.Profile
+{
+ public class GetPlayerAppearance : Query
+ {
+ protected virtual string Path => $"profile/h5/profiles/{_player}/appearance";
+
+ public override string Uri => HaloUriBuilder.Build(Path);
+
+ private readonly string _player;
+
+ public GetPlayerAppearance(string gamertag)
+ {
+ _player = gamertag;
+ }
+
+ protected override void Validate()
+ {
+ var validationResult = new ValidationResult();
+
+ if(!_player.IsValidGamertag())
+ {
+ validationResult.Messages.Add("GetPlayerAppearance query requires a valid Gamertag (Player) to be set.");
+ }
+
+ if (!validationResult.Success)
+ {
+ throw new ValidationException(validationResult.Messages);
+ }
+ }
+
+ }
+}