sawch / Patreon.Net

A .NET implementation of the Patreon V2 API.
MIT License
6 stars 6 forks source link

Member > Relationships > User data not available #3

Closed DurtyFree closed 2 years ago

DurtyFree commented 2 years ago

It seems like the User data bound to a member is not properly parsed. I have checked the raw results of the requests and it seems like the data I am looking for (Social Connections > Discord Id) is available, but not represented in the User model.

Here is what the parsed model looks like, by the example of user "Laze": grafik (Its all null)

For reference, here the related attributes which are correctly returned: grafik

If we now check the raw request response: grafik The desired data is all there, in the "included" part of the response, with a given Id, which maps perfectly fine to the Id in the main data block grafik

I will try to dig a bit more, but I hope this makes sense so far :P

DurtyFree commented 2 years ago

From what I can see the path on the JObject doesnt match what the related User model assembles, maybe this is the issue?

grafik Looks like its member > relationships > user > data > attributes > social_connections (for example)

DurtyFree commented 2 years ago

I can confirm the JObject path seems to be the issue for most of the unavailable relationship data in responses that return arrays of data items.

For the member case I have fixed it by representing the model properly; Introduced MemberUser class

[JsonProperty("user")]
public MemberUser User { get; set; }

Which looks basically like this

public class MemberUser
{
    [JsonProperty("data")]
    public MemberUserData Data { get; set; }
}

public class MemberUserData
{
    [JsonProperty("attributes")]
    public MemberUserDataAttributes Attributes { get; set; }
}

public class MemberUserDataAttributes
{
    /// <summary>
    /// The user's about text, which appears on their profile. Can be <see langword="null"/>.
    /// </summary>
    [JsonProperty("about")]
    public string About { get; set; }
    /// <summary>
    /// Whether or not this user can view NSFW content. Can be <see langword="null"/>.
    /// </summary>
    [JsonProperty("can_see_nsfw")]
    public bool? CanSeeNsfw { get; set; }
    /// <summary>
    /// The time of this user's account creation.
    /// </summary>
    [JsonProperty("created")]
    public DateTimeOffset Created { get; set; }
    /// <summary>
    /// The user's email address.
    /// </summary>
    [JsonProperty("email")]
    public string Email { get; set; }
    /// <summary>
    /// First name. Can be <see langword="null"/>.
    /// </summary>
    [JsonProperty("first_name")]
    public string FirstName { get; set; }
    /// <summary>
    /// Combined first and last name.
    /// </summary>
    [JsonProperty("full_name")]
    public string FullName { get; set; }
    /// <summary>
    /// Whether or not the user has chosen to keep private which creators they pledge to. Can be <see langword="null"/>.
    /// </summary>
    [JsonProperty("hide_pledges")]
    public bool? HidePledges { get; set; }
    /// <summary>
    /// The user's profile picture URL, scaled to width 400px.
    /// </summary>
    [JsonProperty("image_url")]
    public string ImageUrl { get; set; }
    /// <summary>
    /// Whether or not the user has confirmed their email.
    /// </summary>
    [JsonProperty("is_email_verified")]
    public bool IsEmailVerified { get; set; }
    /// <summary>
    /// Last name. Can be <see langword="null"/>.
    /// </summary>
    [JsonProperty("last_name")]
    public string LastName { get; set; }
    /// <summary>
    /// How many posts this user has liked.
    /// </summary>
    [JsonProperty("like_count")]
    public int LikeCount { get; set; }
    /// <summary>
    /// Mapping from user's connected app names to external user Id on the respective app.<para/>
    /// This type lacks official documentation so most unknown types have been defaulted to <see cref="object"/>.
    /// </summary>
    [JsonProperty("social_connections")]
    public SocialConnections SocialConnections { get; set; }
    /// <summary>
    /// The user's profile picture URL, scaled to a square of size 100x100px.
    /// </summary>
    [JsonProperty("thumb_url")]
    public string ThumbUrl { get; set; }
    /// <summary>
    /// URL of this user's creator or patron profile.
    /// </summary>
    [JsonProperty("url")]
    public string Url { get; set; }
    /// <summary>
    /// Deprecated: The public "username" of the user. Non-creator users might not have a vanity. [Deprecated! use campaign.vanity] Can be <see langword="null"/>.
    /// </summary>
    [JsonProperty("vanity")]
    public string Vanity { get; set; }
}

I am not sure if this is the proper solution, I rather thing the JSON Processor has to handle this special case for responses that come with arrays of data, to properly map the attributes of the nested object to the related object.

sawch commented 2 years ago

Hmm, you're right, I'll have to look into it more but it looks like the JSON processor isn't merging the included data into the resource's relationships correctly.

sawch commented 2 years ago

It looks like some of the models Relationship classes are wrong - resources inside the Relationships parts of the JSON aren't the resources themselves like User but are like the top level resources with the "data" object.

A quick fix for any other relationships you encounter that aren't populated correctly should be using Resource and ResourceArray (for example, fixing Member):

public class Member
{
    public class Relationships
    {
-       public User User { get; set; }
+       public Resource<User, User.Relationships> User { get; set; }

It seems to fix the issue for me - relationships use the resource structure which is essentially what your MemberUser class is. Though the User.Relationships in this nested user doesn't exist and should be some sort of NestedResource<User> type without relationships. It feels a little messy having such a deep reference, like members.Data[i].Relationships.User.Data.Attributes.SocialConnections.Discord.UserId, despite it matching the JSON structure. I feel like the JSON processor could maybe condense these objects a little... I need to sleep now but I should be able to correct this pretty soon.

P.S. thanks for the investigating and helping with all these issues!

DurtyFree commented 2 years ago

Ah true, its basically the Resource structure what is needed in those cases. That quick fix seems to be more beautiful than my generated overhead of models :D

But yes I would rather consider shrinking that deep reference mess :D Also np, I am glad to help. I had already planned to work on my own lib before finding this one, happy to help out here to finally have some useful & maintained Patreon .NET API package.

sawch commented 2 years ago

I have some changes in the model-improvements branch, the broken relationships should be fixed and that one line has gotten a bit better! From: members.Data[i].Relationships.User.Data.Attributes.SocialConnections.Discord.UserId To: members.Resources[i].Relationships.User.SocialConnections.Discord.UserId

Now I just want to make a way to enumerate pages in a foreach to skip the members.Resources[i] and I think it'll be good to go.

DurtyFree commented 2 years ago

Neat! I will try it out as soon as its ready & merged :D

DurtyFree commented 2 years ago

Tested it all out, works flawless! Can be closed :)