remotestorage / remotestorage.js

⬡ JavaScript client library for integrating remoteStorage in apps
https://remotestoragejs.readthedocs.io
MIT License
2.32k stars 141 forks source link

Able to use per-storageId instance of any connection to remoteStorage #64

Closed brolund closed 11 years ago

brolund commented 12 years ago

Hi, Maybe you already thought of this, but I can't quite make it out from the code...

For friendsunhosted, we get the public data from all of the friends for a user, and for the user.

It would be awesome if all the remoteStorage connections (r/rw and localStorage caching integration etc) allows for creating one instance per storageId.

I guess this means that something like the syncer in 0.6.9, or generally the application access point, would export a function like:

return { createCachedSynchedStore: function(storageId, ...) { 
    self.storageId = storageId;
    return {
        get: function(category, key) ...,
        listKeys: function(category)
        set: function(category, key, value) ...,  // will fail for unauthorized users
        on: function(event, listener) ... ,
        ...
    };
};}

and that any localStorage cached keys would also contain the storageId to avoid collisions.

This way, in friendsunhosted, we can create such a store for the main user, then get all his friends, and create the similar store (read-only) for all his friends, and get all the caching etc in exactly the same way.

Does this make sense? Is it like this already? Or do you have something better?

Thx!

nilclass commented 12 years ago

Yes, you're right, there is currently no clear method to access foreign storage.

The syncer no longer exists in 0.7. Also data should only be accessed through modules. So I suppose the right place would be the root module (for generic storage access, like seven20 uses it), as well as all modules, for which it makes sense.

Maybe modules should no longer get a privateBaseClient and a publicBaseClient, but rather something like:


remoteStorage.defineModule('blogposts', function(client) {
  // public baseClient
  client.getPublic();
  // private baseClient
  client.getPrivate();
  // public baseClient connected to different storage
  client.getPublic('other@user.address');
  // private baseClient with (possibly limited) read-write access.
  // could be used for a "inbox". no idea yet, how that will work.
  client.getPrivate('other@user.address');
});

To make it work, I think the store would need to prefix nodes with a userAddress and the wireClient needs to store multiple storageInfos, cc @michielbdejong @Epic720 @jancborchardt

michielbdejong commented 12 years ago

the root module is like running as root on unix. no userspace application should ever require root. if friendsunhosted requires the root module to do its thing, then we failed. let's think about the functionality that it needs. there are friend lists and statuses. the friendlists should go in the contacts module. and then there should probably be a new 'status' module (for instance) that exposes three functions (for instance):

setStatus('cycling along the Elbe') to set your own status getStatus('michiel@unhosted.org') to get a friend's status getNews(friendList) to get the latest 10 statuses of any of the people in friendList

then in contacts we should add:

addToFriendsList('michiel@unhosted.org') getFriendsList()

so you would do remoteStorage.status.getNews(remoteStorage.contacts.getFriendsList()) and display that.

does that make sense?

On Tue, Aug 28, 2012 at 8:17 PM, niklas notifications@github.com wrote:

Yes, you're right, there is currently no clear method to access foreign storage.

The syncer no longer exists in 0.7. Also data should only be accessed through (modules)[ https://github.com/unhosted/remoteStorage.js/tree/master/src/modules]. So I suppose the right place would be the root module (for generic storage access, like (seven20)[https://github.com/unhosted/seven20] uses it), as well as all modules, for which it makes sense.

Maybe modules should no longer get a privateBaseClient and a publicBaseClient, but rather something like:

remoteStorage.defineModule('blogposts', function(client) { client.getPublic(); // public baseClient client.getPrivate(); // private baseClient client.getPublic('other@user.address'); // public baseClient connected to different storage client.getPrivate('other@user.address'); // private baseClient with (possibly limited) read-write access. could be used for a "inbox". no idea yet, how that will work.});

To make it work, I think the store would need to prefix nodes with a userAddress and the wireClient needs to store multiple storageInfos, cc @michielbdejong https://github.com/michielbdejong @Epic720https://github.com/Epic720 @jancborchardt https://github.com/jancborchardt

— Reply to this email directly or view it on GitHubhttps://github.com/unhosted/remoteStorage.js/issues/64#issuecomment-8101502.

KenEucker commented 12 years ago

I have thought a lot about an unhosted social app and the modules that would be involved. I think it would be appropriate to have a module that covers the most common social interactions:

status comment approve/disprove (upvote/downvote) (like/dislike)

And other things that can/should be in other modules? blog messages

These items are social components. A status makes much less sense without social interaction. Same with comments and likes. I think a blog, since mostly written to be viewed by others, makes more sense with social interaction than not.

I'm not sure that 'status' needs to be it's own module, it seems so small and narrow.

brolund commented 12 years ago

About root: No, friendsunhosted should not need root. It should use category 'public' and in time a few more things, see below.

About where to put things: To put the friends in a public part of the contacts module seems reasonable. In that case, the contacts module should also handle connections to different storage ids (for friends of friends and such). We also want to be have the option to encrypt (some of) that data so that only friends can see someones other friends, for instance.

For the "status"/"social" module, we will need encrypted communication for private discussions, group or p2p, which will perhaps pan out into an "encryption" module. ("status"/"social" for me is statuses, comments, messages, "likes", and such)

We want to implement a communication scheme (think "pull-based TCP") to be able to let people know of each other or make "bulletins" or "APBs" over the unhosted friends network. This is probably a "communication" module.

Hopefully, we'll be able to pull in a 'pictures' module to manage albums and stuff, otherwise we'd have to do that.

I would appreciate if we could be authoritative on the "social", "encryption" and "communication" modules, at least for now, and that we pull them out when their apis and interactions stabilize. I'm not sure who decides on the contacts, but maybe we could let the stuff gel a bit in F#Un and then pull it out a bit later?

brolund commented 12 years ago

I also want to say that I'm happy you are interested in what we are doing!

jancborchardt commented 12 years ago

There definitely needs to be a Contacts module which can be used by both Friends#Unhosted and @shybyte Shared Stuff. It should probably use the vCard format, so you can easily export and import everything.

brolund commented 12 years ago

Agreed. I'm only concerned about the timing. Or can you add arbitrary properties to the contacts? That would do (I think). :-}

nilclass commented 12 years ago

The contacts module in 0.7 (checkout the master branch) already works pretty well. It currently only implements vcard attributes (and has validations for those through vcardjs). Properties can be added by extending the definitions, see starting from here.

So, for example if you wanted to invent a property "canjuggle", you would do:

VCard.allKeys.push('canjuggle');

If you want that property to be multivalued (i.e. an array), you set:

VCard.multivaluedKeys.canjuggle = true;

VCard.enums is currently not used by anything but meute (to build select boxes), the values in the code are from RFC6350 and what I found in the wild.

So, short answer: yes, you can add arbitrary properties, but please declare them in the contacts module. that way other apps can understand them as well.

VCards can also model groups, I'm currently working on implementing that. The "kind" attribute in this case is "group" (other options are "individual", "org", "location"). Each "group" can have multiple "member" attributes, which map to URIs, such as mailto:, sip:, xmpp:, urn:, etc. The contacts module currently uses the "uid" attribute as it's primary key, which will usually be a UUID. A urn: as the "member" attribute is supposed to map to that "uid" of another contact. mailto: and xmpp: can be resolved via the "email" and "impp" attributes. See this for an example.

We should probably add an attribute for the user address ("acct" maybe?) soon and could possibly store the storageInfo with the contact as well. But accessing other peoples storage in my opinion should not go into the contacts module, but into individual modules.

brolund commented 12 years ago

I just want to be clear that the list of friends must be in the 'public' category so that potentially anyone can read it.

If everything that 'contact' do is in the private categories(?), and everything the 'social' does goes in the public category, 'social' might be a better place to handle the public friends list? And then social can read contacts an export acct:s to the friends list (eventually)?

nilclass commented 12 years ago

As I see it there can be private and public groups. The contacts module is in fact what you call "social". It shall not just handle private contacts (stored below "/contacts/"), but also public ones (stored below "/public/contacts/"). I sketched groups in the contacts module, but unfortunately it's on another computer, will probably push it tonight. We can give people the option to create custom groups, maybe stored under "/contacts/groups/", but we should also pre-define some groups, like "friends". Then there will be a public pendant to that, such as "/public/contacts/groups/friends", which could just be a copy of the private one. But I think it can also be more fine grained, so that when I add a person to my friends list, I can choose whether I want that to become public.

Through something like this:

contacts.addMember('friends', 'user@host'); // private only
contacts.addMember('friends', 'user@host', true); // public and private

Or maybe (to make it clear in the code, that there is a predefined "friends" group) also:

contacts.addFriend('user@host');
contacts.addFriend('user@host', true);

Or alternatively:

contacts.publishFriendship('user@host');

In either of these cases, the argument can be a userAddress ('user@host', which would be prefixed with acct:), a URI (such as mailto:a@b.c, urn:uuid:1234-abcd-5678...) or a remoteStorage.contacts.Contact instance.

Would that work for you?

brolund commented 12 years ago

That would probably work.

A related question: What are the "messages" and "feeds" modules in the modules section?

nilclass commented 12 years ago

those are just stubs.

brolund commented 12 years ago

Hmm, reading this again it seems as the discussion derailed a bit. Since this issue is placed in the Backlog, I take it that this will not be implemented to 0.7.

Any suggestion on how to access both your own and others storages in 0.7?

nilclass commented 12 years ago

It's been placed in the backlog, because it shouldn't block 0.7 from being released (there has been quite a delay already).

But my feeling is that it will quite likely be implemented before 0.7 actually is released, as many people want it and keep asking.

nilclass commented 12 years ago

Ok, finally there is some code for this (sorry for the delay).

(Currently you need to use the sync-rework branch to test this)

Here's a short example:

remoteStorage.getForeignClient('user@host', function(client) {
  // client is a BaseClient, with some additions.
  client.getPublishedObjects(function(objects) {
    // "objects" is an object mapping paths (below /public/) to objects
    for(var path in objects) {
      console.log(path, objects[key])
    }
  });
});

This only works, if there is an object "publishedItems" present at the root of the target user's public storage. The publishedItems object can also be at the module root (which will be more common, as usually apps don't request root-access), in which case you can pass a module name:

client.getPublishedObjects('documents', function(objects) {
  // do something
});

The getPublishedObjects method is just a helper for the conventional "publishedItems" object. In case the app or module uses custom paths or shared objects (i.e. objects that are stored below /public but cannot be discovered through any well known paths), use getObject or getDocument, from BaseClient:

client.getObject('/path/to/object', function(object) {
  // do something
});

To actually get some data into the publishedItems list, use the seven20 app.

I haven't done any work on coupling this with the contacts module (IIRC @elf-pavlik worked on something to store user addresses, right?).

I'm planning to add some methods to "watch" paths in foreign storage, so they get updated regularly. That way the apps don't have to implement polling.

Please tell me if there's anything else needed here, to port your apps to 0.7, @brolund, @shybyte

nilclass commented 11 years ago

As there is no feedback here, I assume it works for you. If it doesn't, please open another issue.