I’ve written Resgate client in .NET; Planned for my Unity (3D) based game.
Definitely not completed, but I will get back to it if I get some time
Feel free to fork or submit pull requests.
I will push to nugets later on
I’ve written Resgate client in .NET; Planned for my Unity (3D) based game.
Definitely not completed, but I will get back to it if I get some time
Feel free to fork or submit pull requests.
I will push to nugets later on
That is awesome!
This is something that @novagen should be interested very in, as they were also using Unity together with Resgate. I will make sure he is properly notified.
Great work! And if you need someone to bounce ideas with, I’m more than happy to assist .
/Samuel
Actually there is something still ahead of myself - authentication (and my subscription model that survives re-connection via resubscribing and ignoring the same resources returned - if they are the same, of course ).
I wonder if there is a way to re-authenticate after connection loss safely with minimal user interaction (for example token used for auth might be invalid for re-submitting). I believe the safest bet would be to provide callback (“Please, re-authenticate!”) to the API user, but that way I am blocking my threads until authentication is done (or cancelled). For example you won’t get subscriptions updates for guest-accessable resources till you either accept or cancel that auth request. There might be a problem with calls also, as I do not have the way to check whenever authentication is required for call or not.
Or perhaps I’m overthinking there, and there is a safer road…?
Yes and no. But yes.
Resgate will drop all client state on disconnect, so it is up to the client to reconnect and reauthenticate.
In the Javascript client, ResClient, this is done through the setOnConnect callback.
function relogin() {
let reloginKey = localStorage.getItem('reloginKey');
if (!reloginKey) return;
return client.authenticate('session', 'relogin', { reloginKey }).then(result => {
localStorage.setItem("reloginKey", result.reloginKey);
}).catch(err => { /* ... */ });
}
client.setOnConnect(relogin);
The idea is that, on initial login, you get back some sort of token that can be used for reauthentication, to prevent the user from having to enter credentials again.
This reloginKey is just some kind of token that can be used as credentials instead of username/password. Maybe a JWT bearer token issued by some oauth2 provider? Maybe just some string or short-lived JWT token that your login-service returned when the user logged in.
So, on disconnect, the Javascript ResClient will do this:
If you want to see this in action, you could try the resgate-test-app which has an auth part that does this. Try run it (simple node.js app):
/Samuel
Thanks Samuel, it lighten up things! Seems it covers my idea of callback on re-connection
Just two more questions:
1.) If API user wants to make a “Get” call to an already subscribed resource is it enough to return client-side cached value?
2.) I saw in docs that “Call” requests may return rid and I should consider them directly subscribed. Can you elaborate a bit more on that? What are the use-cases?
Yes, no need to fetch it again. In fact, since Resgate knows it is subscribed by the client, it will not even include it in the returned resource set, if you were to send a request:
Any request or event resulting in new subscriptions will contain a set of resources that contains any subscribed resource previously not subscribed by the client.
But, a little “warning” if that resource is indirectly subscribed, a.k.a. was referenced by a subscribed resource, then you might want to do differently. Let’s assume you have these models:
{ "refToB": { "rid": "example.b" }}
{ "foo": "bar"}
If you first subscribe to example.a, Resgate will include example.b in the returned resource set, and it will be seen as indirectly subscribed.
If the API user wants to get example.b, you should should be aware that, if example.a is unsubscribed, the indirect subscription to example.b will also be gone. So, if you want to keep example.b’s subscription, you need to send a separate subscribe request for example.b.
ResClient does not send any unnecessary subscribes. If example.a would be unsubscribed while some UI component is still listening on example.b, ResClient will mark example.b as stale (as the indirect subscription is lost), send a subscribe for example.b, and when it gets the response, it will compare the result with the cached value. If there are difference, it will generate events in the same way as when re-synchronizing on a reconnect.
I will quote from another answer I just made
The resource response is to make method calls that return a resource. This is commonly used when making calls to create resources, as you often want to get that newly created resource back:
client.call('inventory.items', 'create', { name: "Rubber duck", quantity: 20 }) .then(item => console.log(item.name)); // Logs Rubber duck
The client doesn’t know before-hand the resource ID of the newly created resource, but when the service responds with the resource ID, Resgate will fetch that resource and return it to the client in the response.
So you can also use it for letting clients subscribe to resources they don’t know the resource ID for. Maybe, after logging in, you want to get your own user profile, without knowing your own user ID. Eg:
client.call('directory.users', 'getCurrentlyLoggedInUser') .then(user => console.log(user.name)); // Samuel
Basically, if you get an rid
instead of a payload
in the call (or auth) response, you can treat the response as if it was a response to a subscribe request for that resource ID in the rid
field.
And if you get more questions, just bring them on!
Always happy to help out in client-writing endeavors.
/Samuel
Ah, I see!
I actually think I got a little overboard in case of first - I compose entire object on-the-fly (supporting cyclic references) and return it to the user. So in the example the returned type would be:
class ExampleA
{
public ExampleB refToB;
}
class ExampleB
{
public string foo;
}
The object ExampleA is populated as initial. Then if B change, the library will know it was indirect subscription and lookups all direct subscriptions using it, triggering changed event on them with entire new object ExampleA passed as argument. I must think for better solution for large objects through…
Sounds like an interesting approach, and I think a good one.
So, if foo changes, you will create both a new ExampleA and ExampleB?
And if you have a ExampleC that also references ExampleB:
class ExampleC
{
public ExampleB refToB;
}
You would also create a new ExampleC?
In my pipeline of things to do, I have the task of writing a Resgate client for Go.
While thinking about how to do it, I also ended up thinking that I must try to work with “immutable” data (well, can’t really do immutables in Go, but you can tell developers not to mutate them!). So that, when a change happens, I would create new entities. Just like you are describing.
My option would otherwise be to go down the path of single mutating entities protected by mutexes and locks… which I believe would be hard to work with.
And yes, for large models or collections, this might be an issue. But, large objects are anyway discouraged, and can often be broken into parts or made partial (like pagination).
And sorry about me letting the RES client protocol allow cyclic references. Sometimes I wonder if that was a poor design choice as it added quite a bit to the complexity when writing the client. But then again, it can be a fun task trying to implement it
Yes, exactly - and the new ExampleC will be created as well.
The algorithm follows:
First, let us use specific data container, called BijectiveDictionary
- it is many-to-many container, indexable both ways. It wastes some space, but has very fast lookup of single-to-many objects both ways (actually the same lookup speed as Dictionary in C#, which Microsoft describes as “approaches O(1)” - believe or not )
So in the left side we have all currently subscribed models (both direct and indirect) and right side is all items that are used by that object.
So if we subscribe object ExampleA it will:
So in this case we will have bi-dictionary of:
ExampleA -> ExampleB
ExampleC -> ExampleB
And querying ExampleB reverse way will yield the result of both ExampleA and ExampleC.
Now, if we know that ExampleB was modified via event we do:
Rebuild algorithm:
It is immutable, but you are right - the large object will be rare, so I think I’ll keep it that way. Oh, and cyclic references actually allows much more freedom for server writers, I don’t think it is a bad design decision after all
Wow.
I really like this!
It seems to simplify what I had quite a bit of struggle with when doing the Javascript client.
The Javascript client only keeps a single direction reference from a resource to all the referenced “children”, together with counters on how many times a resource is indirect/direct referenced.
Whenever something changes (due to subscribes/unsubscribes or change/add/remove events), these references are recursively traversed, the counters changed, and any resource reaching 0 counts are evicted (or just marked as stale, if they are still needed, in which case they will be resubscribed).
But that recursive traverse together with possible cyclic reference, was a bit tricky to implement. Easy to make logical mistakes.
I will look more into your approach!
And, glad you think it was a good choice allowing cyclic refs . It gives more freedom, after all.
Thanks for kind words
Just for the sake of completeness:
BijectiveDictionary
is implemented as two ordinary dictionaries Left -> Set<Right>
and Right -> Set<Left>
packed into class, so they don’t go out of syncObj17, Obj32
and Obj32, Obj17
are not duplicates (order is important)Oh, and I need to re-write auth part. After that - and after testing some corner cases for registration - the library should be ready for Release Candidate version and be included in NPM
From what I’ve seen, it looks both well structured and really promising.
I haven’t really gone into its logic to see if it works in battle, only been trying out the examples, and checking out the API.
But when time is given (at the moment I am trying to add some community requested features), I wish to look much closer at it, and see if I can help in any way.
Speaking about examples, I posted issue #1 on the GitHub repository for Example1
If it is all right with you, I’d be glad to add this .NET client to Resgate.io - Resources.
Fixed, thanks!
I need to finish up API for auth, I don’t like the form it has now. Also some more general error checking (like custom callback for Get methods, alongside with existing Subscriptions) would be nice. After I’m done with that I’ll be more than happy to have this added into .NET client to resources.
… real life can be really tricky these times
After 1.5 years I’ve circled around different tech-stacks and projects and I’m back to the place where I need again a Resgate client in C#. And guess what? The development of my utility will resume shortly! Expect bringing it up to date with current Resgate protocol version within days, fixing error checking and some touches for auth api as well.
After I’m done with that I will add it to Nugets and be very grateful for adding to the official resources list. Just give me a week or two to process everything
Merry Christmas!