Skip to content

fix: ParseObject Relations not working #407

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

Merged
merged 4 commits into from
Feb 2, 2025
Merged

fix: ParseObject Relations not working #407

merged 4 commits into from
Feb 2, 2025

Conversation

YBTopaz8
Copy link
Member

@YBTopaz8 YBTopaz8 commented Feb 2, 2025

This PR aims at fixing only Parse Relations and its related operations (Add, Set, etc..)

  • Changed the return type of ConvertToJSON to return "object" as we now use that one method to pass both primitive and complex datatypes like Dictionnary for Relations.

  • Implemented ConvertToJSON in ParseRelationOperations which now fixes Relations completely (Before, the related object would save but its pointer was never referenced)

  • Removed Encode() as ConvertToJSON is now the method called.

  • Added Unit Tests.

  • Closes fix: Parse Relation not working #404

  • Closes Missing methods in Parse .NET SDK 4.0 #397

- Changed the return time of `ConvertToJSON` to return "object" as we now use that one method to pass both primitive and complex datatypes like Dictionnary for Relations.

- Implemented ConvertToJSON in ParseRelationOperations which now fixes Relations completely (Before, the related object would save but its pointer was never referenced)
Copy link

parse-github-assistant bot commented Feb 2, 2025

Thanks for opening this pull request!

  • ❌ Please link an issue that describes the reason for this pull request, otherwise your pull request will be closed. Make sure to write it as Closes: #123 in the PR description, so I can recognize it.

@YBTopaz8
Copy link
Member Author

YBTopaz8 commented Feb 2, 2025

Here is a full implementation of examples

How to Get Relations

public static async Task<List<ParseObject>> GetUserRelationsAsync(ParseUser user, string relationField)
{
    if (user == null)
    {
        throw new ArgumentNullException(nameof(user), "User must not be null.");
    }

    if (string.IsNullOrEmpty(relationField))
    {
        throw new ArgumentException("Relation field must not be null or empty.", nameof(relationField));
    }

    var relation = user.GetRelation<ParseObject>(relationField);
    
    var results = await relation.Query.FindAsync();
    Debug.WriteLine($"Retrieved {results.Count()} objects from the '{relationField}' relation for user '{user.Username}'.");
    return results.ToList();
}

How to Add Relations

public static async Task AddRelationToUserAsync(ParseUser user, string relationField, IList<ParseObject> relatedObjects)
{
    if (user == null)
        throw new ArgumentNullException(nameof(user), "User must not be null.");

    if (string.IsNullOrEmpty(relationField))
        throw new ArgumentException("Relation field must not be null or empty.", nameof(relationField));

    if (relatedObjects == null || relatedObjects.Count == 0)
    {
        Debug.WriteLine("No objects provided to add to the relation.");
        return;
    }

    // Ensure each related object is saved so it has an ObjectId.
    foreach (var obj in relatedObjects)
    {
        if (string.IsNullOrEmpty(obj.ObjectId))
        {
            await obj.SaveAsync();
        }
    }

    var relation = user.GetRelation<ParseObject>(relationField);
    foreach (var obj in relatedObjects)
    {
        relation.Add(obj);
    }
    
    await user.SaveAsync();
    Debug.WriteLine($"Added {relatedObjects.Count} objects to the '{relationField}' relation for user '{user.Username}'.");
}

How to Update Relations

public static async Task UpdateUserRelationAsync(ParseUser user, string relationField, IList<ParseObject> toAdd, IList<ParseObject> toRemove)
{
    if (user == null)
    {
        throw new ArgumentNullException(nameof(user), "User must not be null.");
    }

    if (string.IsNullOrEmpty(relationField))
    {
        throw new ArgumentException("Relation field must not be null or empty.", nameof(relationField));
    }

    var relation = user.GetRelation<ParseObject>(relationField);

    // Add objects to the relation
    if (toAdd != null && toAdd.Count > 0)
    {
        foreach (var obj in toAdd)
        {
            relation.Add(obj);
        }
        Debug.WriteLine($"Added {toAdd.Count} objects to the '{relationField}' relation.");
    }

    // Remove objects from the relation
    if (toRemove != null && toRemove.Count > 0)
    {
     
        foreach (var obj in toRemove)
        {
            relation.Remove(obj);
        }
        Debug.WriteLine($"Removed {toRemove.Count} objects from the '{relationField}' relation.");
    }

    await user.SaveAsync();
}

How to Delete Relations

public static async Task DeleteUserRelationAsync(ParseUser user, string relationField)
{
    if (user == null)
    {
        throw new ArgumentNullException(nameof(user), "User must not be null.");
    }

    if (string.IsNullOrEmpty(relationField))
    {
        throw new ArgumentException("Relation field must not be null or empty.", nameof(relationField));
    }

    var relation = user.GetRelation<ParseObject>(relationField);
    var relatedObjects = await relation.Query.FindAsync();


    foreach (var obj in relatedObjects)
    {
        relation.Remove(obj);
    }

    await user.SaveAsync();
    Debug.WriteLine($"Removed all objects from the '{relationField}' relation for user '{user.Username}'.");
}

How to Manage Relations

public static async Task ManageUserRelationsAsync()
{
    // Get the current user
    var user = await ParseClient.Instance.GetCurrentUser();

    if (user == null)
    {
        Debug.WriteLine("No user is currently logged in.");
        return;
    }

    const string relationField = "Friends"; // Example relation field name

    // Create related objects to add
    var relatedObjectsToAdd = new List<ParseObject>
{
    new ParseObject("Friend") { ["name"] = "YB" },
    new ParseObject("Friend") { ["name"] = "Topaz" }
};

    // Save related objects to the server before adding to the relation
    foreach (var obj in relatedObjectsToAdd)
    {
        await obj.SaveAsync();
    }

    // Add objects to the relation
    await AddRelationToUserAsync(user, relationField, relatedObjectsToAdd);

    // Query the relation
    var relatedObjects = await GetUserRelationsAsync(user, relationField);

    // Update the relation (add and remove objects)
    var relatedObjectsToRemove = new List<ParseObject> { relatedObjects[0] }; // Remove the first related object
    var newObjectsToAdd = new List<ParseObject>
{
    new ParseObject("Friend") { ["name"] = "Charlie" }
};

    foreach (var obj in newObjectsToAdd)
    {
        await obj.SaveAsync();
    }

    await UpdateUserRelationAsync(user, relationField, newObjectsToAdd, relatedObjectsToRemove);

    // Delete the relation
    await DeleteUserRelationAsync(user, relationField);
}

@mtrezza mtrezza changed the title fix: Parse Relation not working. fix: Parse.Relation not working Feb 2, 2025
@mtrezza mtrezza changed the title fix: Parse.Relation not working fix: ParseObject Relations not working Feb 2, 2025
@mtrezza
Copy link
Member

mtrezza commented Feb 2, 2025

I just noticed that there is no coverage report comment, even though we are uploading it...

- name: Upload code coverage
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

Seems to be an issue with the coverage config, as the link points to Parse Server:

info - 2025-02-02 20:34:53,336 -- > D:\a\Parse-SDK-dotNET\Parse-SDK-dotNET\parse_sdk_dotnet_coverage.xml
info - 2025-02-02 20:34:53,805 -- Your upload is now processing. When finished, results will be available at: https://app.codecov.io/github/parse-community/parse-server/commit/0d07332082c9ef142b9c32cbe13a31daecde980b

@YBTopaz8
Copy link
Member Author

YBTopaz8 commented Feb 2, 2025

Does this require me to do something perhaps?

@mtrezza
Copy link
Member

mtrezza commented Feb 2, 2025

I'm taking a look; it would be good to see the coverage before merging.

Copy link

codecov bot commented Feb 2, 2025

Codecov Report

Attention: Patch coverage is 3.70370% with 26 lines in your changes missing coverage. Please review.

Project coverage is 48.00%. Comparing base (724cd31) to head (0d07332).
Report is 28 commits behind head on master.

Files with missing lines Patch % Lines
...e/Infrastructure/Control/ParseRelationOperation.cs 0.00% 22 Missing ⚠️
...nfiguration/ParseCurrentConfigurationController.cs 0.00% 2 Missing ⚠️
Parse/Infrastructure/Control/ParseSetOperation.cs 0.00% 1 Missing ⚠️
...rse/Infrastructure/Execution/UniversalWebClient.cs 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #407      +/-   ##
==========================================
- Coverage   52.90%   48.00%   -4.90%     
==========================================
  Files         105      106       +1     
  Lines        4699     6199    +1500     
  Branches      830      950     +120     
==========================================
+ Hits         2486     2976     +490     
- Misses       2024     2910     +886     
- Partials      189      313     +124     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@mtrezza
Copy link
Member

mtrezza commented Feb 2, 2025

Fixed the coverage issue. I think the low coverage of this PR is due to the fact that you changed lines which were not covered in the first place. You don't need to add tests for these lines, as that would be a huge effort. In any case, could you still look through the un-covered lines and see if there is any uncovered line that you want to cover in your PR?

@YBTopaz8
Copy link
Member Author

YBTopaz8 commented Feb 2, 2025

I see now.
For the tests on Relations, I added those I could make work. I'd loved to add more but they are uneasy to mock actually.
I do have a plan to work on them later though. So for now, I would be ok with this PR.

@mtrezza
Copy link
Member

mtrezza commented Feb 2, 2025

Sounds good, so this is ready for merge?

@mtrezza
Copy link
Member

mtrezza commented Feb 2, 2025

Btw, what do you mean with "uneasy to mock"? The tests should be easy to write; do the tests in this repo use an actual Parse Server instance, or is that all mocked and therefore so complex to test?

@YBTopaz8
Copy link
Member Author

YBTopaz8 commented Feb 2, 2025

Sounds good, so this is ready for merge?

I would say, Yes.

Btw, what do you mean with "uneasy to mock"? The tests should be easy to write; do the tests in this repo use an actual Parse Server instance, or is that all mocked and therefore so complex to test?

Most of the tests do NOT.
My understanding is that we have this line below designed to run in a simulated "Test Mode" environment, where server interactions are mocked.
Client = new ParseClient(new ServerConnectionData { Test = true });

So, while writing basic tests in 'Test Mode' is generally straightforward, achieving comprehensive and realistic tests for relations, which accurately simulate server-side behavior, requires more stateful mocking . This is what I meant by 'uneasy to mock'. It is definitely doable though.

@mtrezza
Copy link
Member

mtrezza commented Feb 2, 2025

Got it, we should add an actual Parse Server instance to run the tests. It's pretty easy to set this up, I've opened #408.

@mtrezza mtrezza merged commit 9af640a into master Feb 2, 2025
5 of 7 checks passed
parseplatformorg pushed a commit that referenced this pull request Feb 2, 2025
## [4.0.2](4.0.1...4.0.2) (2025-02-02)

### Bug Fixes

* `ParseObject` Relations not working ([#407](#407)) ([9af640a](9af640a))
@parseplatformorg
Copy link

🎉 This change has been released in version 4.0.2

@parseplatformorg parseplatformorg added the state:released Released as stable version label Feb 2, 2025
@YBTopaz8
Copy link
Member Author

YBTopaz8 commented Feb 2, 2025

Oh that'd be great! Thank you for that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:released Released as stable version
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing methods in Parse .NET SDK 4.0
3 participants