Skip to content
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

Evolving intents #201

Open
y-lohse opened this issue Aug 28, 2017 · 12 comments
Open

Evolving intents #201

y-lohse opened this issue Aug 28, 2017 · 12 comments

Comments

@y-lohse
Copy link
Contributor

y-lohse commented Aug 28, 2017

Lately, we have discovered new use cases for intents, and in the past we've had to add things to the Intent API that weren't part of the initial plan. We should start thinking about the next version of the intent specs.

Before we start, two strongly recomended reads:

A) Things that are not in the spec but are in the API

  1. A hook for the client to know when the iframe is loaded — See Intents improvements (onReadyCallback, exposeFrameRemoval) #189, used to wait until the service is loaded to display it.
  2. A hook for the client to remove the iframe manually — See Intents improvements (onReadyCallback, exposeFrameRemoval) #189, used to animate the iframe closing.
  3. The service tells the client to change the size of the service's frame — See feat: make possible for the service to specify the size of the intent modale #175 and Add intent transition handling #194, used when a service wants to be displayed at a specific size and the client couldn't predict it. Can happen after the service is initialized or when the user interacts with the service (eg. expanding Claudy).

B) Things that we need but are not in the spec

1. Linking to a specific part of an app

The most common example for this is wanting to link to a certain document inside cozy-drive. Except cozy-drive, no one has any clue what the exact URL is — in fact, even the URL for the drive app is mostly unknown for apps.

The solution here is to use intents. The client will express that it wants a redirection (maybe through a dedicated method like cozy.intents.redirect(), or maybe by adding a disposition parameter).

The service can indicate in it's manifest that it handles redirections, as well as what parameters the new URL accepts (see this comment).

2. Complex communication

The current model specifies that the client sends some data at the start of the intent, and the service replies with some data at the end, and nothing in between. It would be good to have the ability for the service to send intermediate messages as well.

  • What are some concrete use cases for this?
  • Should the reverse be possible as well, ie. the client can send data to the service after the creation?

3. Silent Communication

Kinda vague at this point, but a client might need access to some data from another app, but without showing the service's UI. This could be because the client itself will show that data, aggregated with dat from other sources. Or it could be because there's just no need for a UI.

This has some implications in terms of security that need to be considered.

???

You tell me. I'll try to keep this post updated.

@y-lohse
Copy link
Contributor Author

y-lohse commented Aug 28, 2017

Now a few subjective points.

There's probably a new use case, which is "Silent Computing". A client wants another app to perform some computation on some data (probably data the client has not access to) and get a result back, but the service shouldn't be visible.
This can be hacked by hiding the service iframe, but maybe it should become a legit thing. It also needs very special consideration, because while intents in general are there to work around permissions, it's usually with the user's consent. In this case... maybe not ☠️ .
I think @Ljinod and @Gara64 are interested in this, and @ptbrowne may have actually done it. I may also have a use case for this, but not sure yet.

Regarding A), I think all use cases are good ones and I have no problem keeping them, but maybe we could clean up the API a bit? Especially the callbacks and flags used to load / unload the iframe could probably be replaced by introducing lifecycle hooks or something like that? @gregorylegarec is probably the wisest 🐒 on this topic.

@gregorylegarec
Copy link
Contributor

gregorylegarec commented Aug 28, 2017

Thanks for opening this issue @y-lohse. The more we use intents the more we are facing the limits you described.

Linking to a specific part of an app

Concerning your point B.1, we actually already faced this situation in Collect, when we need to link to a given folder into cozy-drive. At first we have been thinking using an intent, but we quickly realized that we were over-complicating the feature. We concluded that the best way to achieve our link was to use a redirection from the cozy stack. A redirection is a simple link, having the form /redirect/io.cozy.files/121238468868448/. This link is managed by the stack, which looks which app is able to handle the given doctype. We assume that Cozy-Drive should declare into its manifest something like:

"redirections": [{
  "doctype": "io.cozy.files",
  "template": ":id"
  "target:" "/#/files/{id}"

We discussed this point with @poupotte and I think that is something backlogged for the back team (@nono ?), but not yet implemeted yet. Perhaps if you need it too it could be done sooner as expected.

Cleaning the API

Totally agree, we should pass a list of callbacks at intent's creation. We just have to keep backward compatibility for a time.

Silent computing

Like you, we need to have what you call Silent Computing (love that name). At this time, using intents to handle this kind of feature is a practical solution, but I am not sure that it was intent's initial purpose. In my opinion, intents are expecting an action from the user. We still don't know what the best thing to do or implement. Maybe some kind of simplified intent ?

Complex communication

As we dig deeper into Claudy's logic (espacially @CPatchane), we are needing some more complex communication between service and client. For example, sometimes the client expect some information from the service without having the service terminated. This could imply that we may improve the intent mechanism to be able to send several messages from the service, and not only final one as it is working now.

EDIT: s/slack/stack

@y-lohse
Copy link
Contributor Author

y-lohse commented Aug 28, 2017

B1) Linking

Uh ok, I missed that info. At first sight, it seems like a better solution than using intents, especially if it's already planned. I'll just wait for confirmation of that and if it is indeed planned, I'll scrap it from this list :)

Silent Computing

I feel like it's ok if we make that part of intents. Intents are there for cross-app communication, and the mechanics will probably be super-similar.
Again, we may use the disposition here, eg. a disposition of none or hidden would mean that the iframe should be kept hidden. But I haven't thought too much about it. I'll add it to the list, as it seems it will be needed.

Complex communication

Sounds good! Do you have a concrete example of information that the client needs while the service is running?

Conceptually, I think it's perfectly fine if the client and service exchange data more than once. A few thoughts about the API:

  • We should differentiate the data received callback from the intent terminated callback. The second one wouldn't receive any data, it's only there to perform clean-up operations if needed.
  • The same rules regarding permissions should apply, no matter if the data transmitted is the last bit or not (ie. the service must grant the requested permissions before transmitting data to the client).
  • Should we plan for the reverse as well : the client sends data to the service, after the initial data?

@nono
Copy link
Member

nono commented Aug 28, 2017

Linking to a specific part of an app

I haven't heard of this proposition until this issue. As far as I can tell, it is not in our backlog.

In fact, I'm not sure if it is really better than intents. Intents have some nice properties. For example, it's possible to pass structured informations as JSON (it's harder in an URL). What advantages do you see to use a new redirections system instead of the intents?

@CPatchane
Copy link
Contributor

I could add more ideas about what you describe @y-lohse, thanks for opening this issue.

Complex communication

Completely agree about that and that could be better implemented in the API. We could use a kind of lifecycle of the intent to be better handled by the client and inspire us from components lifecycle in (P)React.

createService(...) // initial data sent to service 
.mount(...) // current start()
.intentDidMount(...) // current onReadyCallback usage
.intentWillUnmount(...) // ~ current terminate with exposeRemoval
.intentDidUnmount(finalDataFromService...) // current then()
.intentWillResize(newSize...) // better handling resizing from the client

That's a pure example and could be not correct in some points but it's to show the main idea. Also, sending data from the Client already started during the process could be a good idea but that must come in a second time since we doesn't know if we really need it IMHO.

Linking to a specific part of an app

I think this part is just a use case of a more general thing that I could name Smart Redirection. This is different from the intent currently implemented since:

  • There is no iframe (and user action too, I excluded the "very first app choice" for now)
  • There is no complex (JSON) data exchange here
  • The communication is unique and unidirectional only from the client to the service.

In this case, the client cannot know which app will open its request and what will be the result from the redirection.

I like to use the search use case as example for here:

Smart redirection declarations from the service application:

"redirections": [{
  "service": "TERM_SEARCH",
  "targetURL": "/#/search/web/:term"
}, {
  "service": "IMAGES_SEARCH",
  "targetURL": "/#/search/images/:term"
}, {
  "service": "SEARCH",
  "targetURL": "/#/search/:type/:term"
}]

So, on the client side:

const urlToRedirect = await cozy.redirect("SEARCH", { type: "web", term: "Recherche depuis Cozy" })

And for files of folder that could be :

cozy.redirect("FILE_ID", { id: "zekdze892jdeh221d" })
cozy.redirect("FOLDER_ID", { id: "zekdze892jdeh221d" })
cozy.redirect("FOLDER_NAME", { name: "/Administration/SFR" })

Ideally, calling this function, if a service matches, the client will receive an obfuscated URL from the stack to use (as for the OAuth connexion) null if no service was found.

Silent computing

I would say, in a first place, that the response need to be an information that doesn't need permissions for the client. For me, if we use a service to silently compute an information that the initial application doesn't have the permission, it would be a way to hack the stack permissions system and access informations without permissions.
Or maybe we could use like an intent to start the computing and when the result is coming back, it will be displayed to the user as the information that will be received by the current application which asked for it.
It's just some thinkings but we have to more discuss about the real needs and the implementation for this latter.

My 2 cents.

@y-lohse
Copy link
Contributor Author

y-lohse commented Aug 30, 2017

Complex coms

@CPatchane I also think this API design is probably what makes the most sense. Regarding complex coms in itself, do you have a concrete example of information that the client needs while the service is running?

Silent computing

I've added a note about this topic in the first post. Not much else to add at this point imo.

Linking

Hot topic 🔥
So just to be clear since there were some talks outside of Github as well, there is no redirection API that is planned at the moment. So I think it's up for discussion wether it's better to introduce such a separate API, or use the intent system.
Here are some pros and cons that have come up, feel free to add more:

Using Intents Using a redirection API
Specification work small-ish update to an existing spec (introducing dispositions) creating a small new spec
👎 Requires non-trivial front end work
👎 Requires non-trivial back end work
👎 Introduces a new manifest field
👍 Privacy (redirecting app doesn't know what happens next)
👍 Redirect based on structured data ☑️

My opinion on this: If the stack can handle the redirection, apps don't have to implement it which is very neat. Having to add a new section to the manifest for this is a compromise I'm ok with.
I also understand if the stack doesn't want to take care of this, since it's probably a fair amount of work (more manifest inspecting, URL pattern matching, etc) — especially since intents in their current incarnation can kind of already do redirections. It's also logical enough — the window disposition is meant for this.

@nono
Copy link
Member

nono commented Aug 30, 2017

For privacy, it's a tie. Using a redirection API doesn't protect against a rogue application.

About the disposition, I thought it could be also used for hidden computing. If if it the case, I really think it would be more logical to use intents and disposition for links.

@gregorylegarec
Copy link
Contributor

gregorylegarec commented Aug 30, 2017

After discussing with @nono two days ago : intents are already designed to return an URL for a given ACTION on a given doctype. We could "hack" this feature to get an URL for a REDIRECT action.

For example an app's manifest should be able to declare:

"intents": [{
    "action": "REDIRECT",
    "type": ["io.cozy.files"],
    "href": "/#/files/:_id"
  }]

And we would just have to implement a method cozy.client.intents.redirect(document). Passing the document should give us all the information we need : doctype, _id, or whatever we need to replace in the url. The method should handle the redirection/opening too.

The only limit I see is the case with redirection url with unknown segments, for example /#/page/:_id/:unavailable_property. But I am not sure this case could happen if applications and routes are well designed.

@y-lohse
Copy link
Contributor Author

y-lohse commented Aug 30, 2017

So essentially here instead of injecting the service iframe, you'd do some pattern matching on the href and then change the url to that?
I think that would be a smart way to do it. I can't think of use cases where the client doesn't know enough to give some params. If we can somehow declare optional params in the href in the manifest, that would probably cover what we need.

@gregorylegarec
Copy link
Contributor

That's it.

@y-lohse
Copy link
Contributor Author

y-lohse commented Sep 1, 2017

Cool, I guess we solved links! Onto the 2 other topics.

After thinking about this a bit, looking at code and writing some, here's what I think:

  • Intents are usually about documents, as implied by the preconised list of action and type.
  • Silent computing and complex communications are not really about documents. They are essentially about arbitrary inter app communication.
  • The intent spec doesn't address the needs of inter-app communication.
  • The technical solutions to both problems are nearly identical (stack request + iframes)
  • The intent system is already used for things that are not related to documents (see GET_URL and CLAUDY)

That being said, I think it would be too much overhead to introduce a separate API. I think we should make these intent hacks official by extending the intent spec to support messaging between client and service, and reshaping the API to provide more hooks (see @CPatchane 's draft for a general idea). I don't think the stack will need updates at all.

What do you think?

@kosssi
Copy link
Contributor

kosssi commented Mar 29, 2018

On a décidé en "Front Transverse" que l'on déplacera les intentes dans un package séparé dans le repo de cozy-client. Mais que ce n'était pas forcément une priorité du moment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests