Control Type Meanings

42 views
Skip to first unread message

Stephen Mizell

unread,
Feb 10, 2017, 10:35:10 AM2/10/17
to alps.io, pa...@apiary.io
Hi all,

I'm currently working on a change [0] to a format called API Elements [1] that will allow for a way to define affordances for a hypermedia API alongside existing API semantics. This format is the parse result that is used to parse both API Blueprint and Swagger to the same format. I have proposed in my change to borrow the control type definitions from ALPS in order to reuse the research and work that is already been done by the fine folks here. However, a good question came up in discussion with a friend on the project, Pavan (cc'ed), around the meanings of these controls. If this has already been discussed in detail in another thread, please let me know :)

The question was, why is the term "idempotent" used to mean "unsafe and idempotent?" And why does "unsafe" mean "unsafe and non-idempotent?"

My personal guess was that this is done to be able to fit all protocol methods into one and only one category, as "idempotent" doesn't allow you to GET and DELETE. However, it seems to overloading what "idempotent" and "unsafe" mean, which may be a little confusing to people coming into this area.

For our use case, I think our options—apart from the ALPS direction—are to either allow users to provide multiple categories for the control type, such as saying a control is both idempotent and unsafe, or to provide booleans for two terms, safety and idempotency. However, neither capture what the ALPS categories do in defining strict combinations of those terms (i.e. safe transitions are not non-idempotent, and idempotency alone doesn't convey safety).

Thanks everyone!

Stephen


Mark W. Foster

unread,
Feb 10, 2017, 10:58:54 AM2/10/17
to alps.io, pa...@apiary.io
Interesting question. I think what is implied, but not explicitly stated is that "safe" is "safe and idempotent" and maybe it needs that clarification. If that is explicit, is there an obvious scenario that the "safe", "idempotent" and "unsafe" definitions don't cover?

Mark

Stephen Mizell

unread,
Feb 10, 2017, 12:13:47 PM2/10/17
to alps.io, pa...@apiary.io
In the ALPS spec it says "safe" is a "hypermedia control that triggers a safe, idempotent state transition." So it goes:
  1. Safe = safe + idempotent
  2. Unsafe = unsafe + non-idempotent
  3. Idempotent = unsafe + idempotent
Note the only combination left out is "safe + non-idempotent," but I'm not sure that exists? There is a line in the HTTP spec [0] about a sequence of requests that are not-idempotent together, but may have parts of them as idempotent. I don't know enough about that though to provide any feedback.

mca

unread,
Feb 10, 2017, 12:23:46 PM2/10/17
to Stephen Mizell, alps.io, pa...@apiary.io
Yep:

  1. Safe = safe + idempotent
  2. Unsafe = unsafe + non-idempotent
  3. Idempotent = unsafe + idempotent
That takes a moment to grok, for sure. So far, once I get past the initial fog, it's been easy for me to code against this; except for one case: DELETE.

there is nothing in the ALPS language that standardizes how to indicate a DELETE/REMOVE/DESTROY kind of network promise. Initially, Leonard and I were working through this, we tried to keep in mind that DELETE could be modeled as PUTting a blank body. It's *logically* correct, but not really very helpful when writing code generation, etc.

Also, some network interaction styles (e.g. sockets) don't even *use* a DELETE promise, just READ and WRITE. In those cases, generators need to decide how to deal with any ALPS promise that is not SAFE. for TCP/IP i use type="idempotent" to indicate a WRITE. but for WebSockets over HTTP, i've been using type="unsafe" for WRITE.

not sure i can definitely defend that, but that's what i've been doing<g>.
 

Stephen Mizell

unread,
Feb 10, 2017, 1:15:24 PM2/10/17
to alps.io, smi...@gmail.com, pa...@apiary.io
It seems to me the control types intend to be an abstraction of a uniform interface rather than describing safety or idempotency directly (though they do indirectly). In other words, "safe" does not mean safe, but is an identifier for a type of control that is both safe and idempotent—for instance, you could call it "retrieve" and it would still fit.

This makes me wonder if there should be a more in-depth abstraction for uniform interfaces. I've had this discussion with Mark before, though I've forgotten a lot of my thoughts. I'll have to go through my gists to see if I wrote something down. Ideally, it would just be categories of affordances that map to existing protocols, where those protocols define the safety and idempotency of the methods rather than the profile and application semantics..

Lastly, one thought that is sort of related here is that an affordance may have multiple controls associated with it. For example, the "edit" link relation per the Atom spec says the "href IRI can be used to retrieve, update, and delete the Resource represented by that Entry." The presence of that link relation means there are several actions that may be taken, and describing a single control type seems to not be able to cover this scenario (i.e. it can be safe/idempotent and unsafe/idempotent).

Stephen Mizell

unread,
Feb 10, 2017, 1:17:41 PM2/10/17
to alps.io, smi...@gmail.com, pa...@apiary.io
Sorry, I'm wrong here.

for instance, you could call it "retrieve" and it would still fit

Safe also conveys HEAD and OPTION in HTTP. Still, I hope my thought about the separation makes sense :) 

mca

unread,
Feb 10, 2017, 1:49:51 PM2/10/17
to Stephen Mizell, alps.io, pa...@apiary.io
It is important to keep the ALPS document loosely-coupled from the resulting hypermedia message. the layout of the ALPS document is not meant to represent that layout of the resulting message.

GIST FOR REFERENCE
Heres an example of ALPS for Atom:

BTW - ALPS was also created to allow people to favor their own patterns -- you'll readily recognize my "data", "actions", "containers" pattern here. something i did not want to bake into the spec, but want to support (more abstractions....) anyway...

ATOMPUB AFFORDANCES
note that in AtomPub CREATE is afforded by using the href property of the <link> element that is the child of the <feed> element.
   <link href="http://example.org/"/>
   ...
</feed>

and the READ, UPDATE, and DELETE actions are afforded by using the href property of the <link> element that is the child of each <entry> element:
<entry>
   ...
 </entry>
 
That is -- no matter how you say it *four* different actions. yes, Atom affords these through *two* physical elements in the message, but it is still four actions. 

ALPS DESCRIPTION
so, when i write up my ALPS document, I'll be including *four* action elements:

<descriptor id="create" name="link" type="unsafe" />
<descriptor id="read" name="link" type="safe" />
<descriptor id="update" name="link" type="idempotent" />
<descriptor id="delete" name="link" type="idempotent" />

Note my use of [name="link"] to accommodate that Atom will be matching this up to the same physical affordance within the document. Actually, this [name="link"] thing is an embellishment that is not really needed, but helps for this example.

SUMMARY
ALPS does not attempt to mimic a message model -- you'll find that really difficult if you try.  ALPS describes the possibilities and it is up to someone/something else to map one or more of those possibilities into a real protocol and message model instance.

hope this helps.


Stephen Mizell

unread,
Feb 10, 2017, 2:41:07 PM2/10/17
to alps.io, smi...@gmail.com, pa...@apiary.io
This is good stuff and clears up some things for me. Thanks for taking the time to put it together.

I think the thing that I have unclear now is how to define a link relation in ALPS like the "edit" link relation, where it allows for multiple actions. I'm guessing you'd do it in a similar way as you have with your Atom example and use three (descriptors read, update, and delete) that have a name of "edit?" Or would you wrap them in some descriptor container?

mca

unread,
Feb 10, 2017, 2:51:00 PM2/10/17
to Stephen Mizell, alps.io, Pavan Kumar Sunkara
yeah - i currently make these different descriptors and then work on some "bindings" for Atom vs Cj vs Siren where each of them have slightly different ways to turn the description into a running instance.

i've not dealt with definition languages enough to have a coherent story about them, tho.


Jeff Michaud

unread,
Apr 10, 2017, 8:47:35 PM4/10/17
to alps.io, smi...@gmail.com, pa...@apiary.io
Hello Stephen and others,

How does OPTIONS fit in context? It COULD be considered a meta method so I don't see it fitting cleanly in any of the non-semantic types. It's effect SHOULD be "safe" however.

I would be hard pressed to throw an ALPS profile into a storm of complexity by duplicating everything to support OPTIONS if it was forced to be bundled under the notional "safe"

Thoughts?

Regards
Jeff Michaud

mca

unread,
Apr 10, 2017, 10:16:15 PM4/10/17
to Jeff Michaud, alps.io, Stephen Mizell, Pavan Kumar Sunkara
ALPS is meant to describe data and actions of an interface, not the details of a transfer protocol.

ALPS focuses on general network promises (safe/unsafe, idempotent/non-idempotent) and, as has been pointed out here, conflates this set of four possible cases into three (leaving safe-non-idemporent out of the list)

So, ALPS does not contain direct information about any single protocol. 

Right now ALPS can be used to support HTTP.OPTIONS the same way it can be used to support HTTP.CONNECT or FTP.PWD.  IOW, there are lots of protocol-specific commands that have no direct mapping to the various commands within any single protocol.

ALPS was also designed to be mapped to media-types, not protocols. For this reason, there is nothing in the ALPS specs that allow us to indicate protocol details for an affordance. 

I'm very interested in hearing about what folks cannot do when using ALPS for API descriptions. IOW, if this lack of ability to indicate protocol methods a stumbler for some project you're working on? 

Jeff Michaud

unread,
Apr 10, 2017, 10:40:38 PM4/10/17
to alps.io, come...@comcast.net, smi...@gmail.com, pa...@apiary.io
Hello Mike,


On Monday, April 10, 2017 at 7:16:15 PM UTC-7, Mike Amundsen wrote:
ALPS is meant to describe data and actions of an interface, not the details of a transfer protocol.

ALPS focuses on general network promises (safe/unsafe, idempotent/non-idempotent) and, as has been pointed out here, conflates this set of four possible cases into three (leaving safe-non-idemporent out of the list)

So, ALPS does not contain direct information about any single protocol. 

Right now ALPS can be used to support HTTP.OPTIONS the same way it can be used to support HTTP.CONNECT or FTP.PWD.  IOW, there are lots of protocol-specific commands that have no direct mapping to the various commands within any single protocol.

ALPS was also designed to be mapped to media-types, not protocols. For this reason, there is nothing in the ALPS specs that allow us to indicate protocol details for an affordance. 

I'm very interested in hearing about what folks cannot do when using ALPS for API descriptions. IOW, if this lack of ability to indicate protocol methods a stumbler for some project you're working on?

More or less. I guess I'm just observing that some protocol methods don't seem to fit cleanly in the defined descriptor types; the "meta" HTTP.OPTIONS seems like a good example of this (it's a method that provides information about supported methods for a given resources). Forcing these recalcitrant methods into a category (e.g. mapping HTTP.OPTIONS to the "safe" non-semantic type would yield seemingly useless complexity under ALPS just to support one method (essentially having to create a duplicate "safe" case everywhere where an "unsafe" or "idempotent" case is presented if we, for example, want to support HTTP.OPTIONS on all resources).

IOW, what descriptor.type would you bind HTTP.CONNECT or HTTP.OPTIONS to for a hypothetical ALPS media-type bindings? This may not be a non-problem for media-types that revolve around "actions" and provide a mechanism for binding to a protocol agnostically but may not yield sufficient expressiveness when a media-type does not specifically support actions or a mechanism for binding to a protocol agnostically, or at all.

Cheers,
Jeff Michaud
 

mca

unread,
Apr 10, 2017, 10:59:42 PM4/10/17
to Jeff Michaud, alps.io, Stephen Mizell, Pavan Kumar Sunkara
I am going to go out on a limb and assert that what's going on here is a confusion on the use/value of ALPS vs. specs like RAML, OAI, Blueprint, WSDL, etc.

ALPS was not designed to provide protocol promises. attempts to use ALPS to describe a single protocol detail is always going to be frustrating.

consider this...

<descriptor id="status" type="safe" />

with HTTP, this could be mapped to HTTP.GET, HTTP.OPTIONS, even HTTP.POST (e.g. dropbox's POST-only world, SOAP, etc.).

with FTP, this could be mapped to FTP.RETR, FTP.SIZE, FTP.PWD, and others.

and the list goes on.

We can't predict the exact protocol *method* will be used by a service for this affordance, but we can predict the network *promise* (this is a safe action) that will be offered by that service.

even more important, we don't need to predict the method -- that will be provided at runtime with the actual representation that has the affordance details themselves:

<form id="status" action="/order/123" method="get" />
OR
<form id="status" action="/order/123" method="options" />
etc.

I understand the appeal of using ALPS to *generate* service code rather than describe service interfaces, but that's not what ALPS was designed to do. RAML, OAI, Blueprint, WSDL, etc. -- these are designed to define the actual runtime instance of a service described by ALPS.

does this help?
 




Jeff Michaud

unread,
Apr 10, 2017, 11:48:40 PM4/10/17
to alps.io, come...@comcast.net, smi...@gmail.com, pa...@apiary.io


On Monday, April 10, 2017 at 7:59:42 PM UTC-7, Mike Amundsen wrote:
I am going to go out on a limb and assert that what's going on here is a confusion on the use/value of ALPS vs. specs like RAML, OAI, Blueprint, WSDL, etc.

Hmm, perhaps, although I'm not getting that impression. I think I understand that ALPS offers a protocol/network abstraction with non-semantic descriptor types.
 
ALPS was not designed to provide protocol promises. attempts to use ALPS to describe a single protocol detail is always going to be frustrating.

consider this...

<descriptor id="status" type="safe" />

with HTTP, this could be mapped to HTTP.GET, HTTP.OPTIONS, even HTTP.POST (e.g. dropbox's POST-only world, SOAP, etc.).

with FTP, this could be mapped to FTP.RETR, FTP.SIZE, FTP.PWD, and others.

and the list goes on.

We can't predict the exact protocol *method* will be used by a service for this affordance, but we can predict the network *promise* (this is a safe action) that will be offered by that service.

even more important, we don't need to predict the method -- that will be provided at runtime with the actual representation that has the affordance details themselves:

This is where I perceive a small rub. I generally agree that we don't need to predict the method exactly, however, in certain contexts, ball-parking certain methods in certain abstractions/categories (e.g. generally expecting that HTTP.OPTIONS will get parked in the "safe" descriptor type) seems to yield "unpleasantness" in certain cases. For example, if there's a need for a service to support a limited HTTP.POST as a primary focus on a slew of resources but also wants to allow HTTP.OPTIONS on all resources, it seems like, to be able to accommodate this in a given service and maintain a coherent network level promise, ALPS descriptors tied into an "unsafe" network promise would have to be "duplicated" to also allow for a "safe" network promise counterpart, just for the benefit of HTTP.OPTIONS; unless we feel that we could also coherently park HTTP.OPTIONS in "unsafe" or "idempotent"; I can't really see it).

<snip/>
 
I understand the appeal of using ALPS to *generate* service code rather than describe service interfaces, but that's not what ALPS was designed to do. RAML, OAI, Blueprint, WSDL, etc. -- these are designed to define the actual runtime instance of a service described by ALPS.

does this help?

Somewhat. There's still a small rub I'm having trouble shaking out. It agree that we seem to be able to do what we need with ALPS. It just seems like we may end up creating seemingly "useless" complexity by multiplying descriptors for the sake of allowing for coherent and very a narrowly focused "exact protocol method for an given affordance" under certain practical cross-cutting circumstances (e.g. a given resource that needs to support a wide cross-cutting gamut of HTTP verb semantics).

This may very well turn out to be a paper tiger but it seems like we'll see this type of complexity inflation (in the ALPS profile) everywhere where resources will offer a collection of methods that cross-cut the various non-semantic descriptor types, regardless of protocol. Some mechanism to help avoid descriptor multiplication MAY help (e.g. allowing for a descriptor.type to contain more than one non-semantic value; <descriptor id="cross-cutting" type="safe unsafe idempotent"/> eventually bound into a narrow HTTP.OPTIONS, HTTP.POST, HTTP.DELETE).

Hopefully that made more sense.

It may just be me being in a frugal mood :).

Cheers
Jeff Michaud
 

mca

unread,
Apr 11, 2017, 12:02:11 AM4/11/17
to Jeff Michaud, alps.io, Stephen Mizell, Pavan Kumar Sunkara
ahhh...

i think i'm getting you now. you see value in keeping the description small by "reusing" elements of the ALPS document such that a single <descriptor /> describes multiple affordances. 

ALPS is not meant to encourage that level of optimization. In ALPS, an action descriptor is not meant to be a direct one-to-one map in a representation affordance. I don't think it is much help to try to fight this and get ALPS descriptors to mean multiple things.

FWIW, i see using the same descriptor to mean multiple things to be adding complexity, not reducing it. that's an "eye of the beholder" moment, I suspect.



Jeff Michaud

unread,
Apr 11, 2017, 2:58:44 PM4/11/17
to alps.io
Yes, but only to allow multiple network promises for a given descriptor.

However your response seems to evoke a nuance that may have escaped me. My current understanding of non-semantic descriptors tells me that even for a given non-semantic type value (e.g. "Safe"), that we're effectively already describing a potentially large swath of affordances so I don't perceive that stacking up non-sematic types changes the nature of the description.

Maybe I can spin this differently. How would you describe a link to a rsrc that ends up needing to support network promises for all 3 non-semantic types?

Cheers,
Jeff Michaud

mca

unread,
Apr 11, 2017, 3:52:39 PM4/11/17
to Jeff Michaud, alps.io
I don't use ALPS to describe *resources* at all. I describe a set of data and action elements for a domain. which ones appear on which resources it totally an implementation detail left to the individual services.

in the case you ask about here, i'd simply provide the affordances and let the service make its own decisions about whether one or more of them appear at runtime -- which might even be determined by the rights of the logged-in user and/or the availability of operations on the server side (e.g. "between midnight and 2AM we don't allow POSTing to this resource...")

does that help?

Jeff Michaud

unread,
Apr 11, 2017, 9:32:07 PM4/11/17
to alps.io, come...@comcast.net
On Tuesday, April 11, 2017 at 12:52:39 PM UTC-7, Mike Amundsen wrote:
I don't use ALPS to describe *resources* at all. I describe a set of data and action elements for a domain. which ones appear on which resources it totally an implementation detail left to the individual services.

Fair enough. I understand the same.
 
in the case you ask about here, i'd simply provide the affordances and let the service make its own decisions about whether one or more of them appear at runtime -- which might even be determined by the rights of the logged-in user and/or the availability of operations on the server side (e.g. "between midnight and 2AM we don't allow POSTing to this resource...")

does that help?

Let me take a slightly different angle with a small example to reverse an ALPS profile fragment from to try and pinpoint the rock I keep stubbing my toe on.

Imagine a resource (simplified and not defined here for the purpose of the example) that contains a single affordance that leads to a resource that contains blog post semantics. The affordance allows for safe=(HTTP.OPTIONS, HTTP.GET) and idempotent=(HTTP.PUT, HTTP.DELETE).

As one would expect:
  • HTTP.OPTIONS lists HTTP.OPTIONS, HTTP.GET, HTTP.PUT, HTTP.DELETE
  • HTTP.GET fetches the resource containing blog-post semantics
  • HTTP.PUT updates the resource containing blog-post semantics
  • HTTP.DELETE deletes the resource containing blog-post semantics
What would the ALPS profile fragment need to look like to express the possibility of an affordance that ties into both safe AND idempotent network promises?

{
  "_links":
  {
    "blog-post":
    {
      "href": "/blog-post/1",
      "profile" : "http://example.org/blogger",
      "type" : "http://example.org/blogger#blog-post"     
    }
  }
}

Cheers
Jeff Michaud
 

mca

unread,
Apr 11, 2017, 9:50:08 PM4/11/17
to Jeff Michaud, alps.io
LOL,we'll just keep doing this over and over, right?

"The map is not the territory"[0] and ALPS is not the representation. ALPS is not the resource, either.

<alps>
...
<descriptor id="bp-read" name="blog-post" type="safe" />
<descriptor id="bp-write" name="blog-post" type="unsafe" />
...
</alps>

bindings for a selected media type will result in different representations. 
- HTML will have two forms
- Atom might have a single <link id="blog-post" href="..." rel="edit" />
- UBER will have multiple <data .. /> elements with the appropriate "action" values
- and so forth.




Jeff Michaud

unread,
Apr 12, 2017, 12:52:59 AM4/12/17
to alps.io, come...@comcast.net

On Tuesday, April 11, 2017 at 6:50:08 PM UTC-7, Mike Amundsen wrote:
LOL,we'll just keep doing this over and over, right?

Nope, we seem to be exactly where I was hoping to get to :).
 
"The map is not the territory"[0] and ALPS is not the representation. ALPS is not the resource, either.
<alps>
...
<descriptor id="bp-read" name="blog-post" type="safe" />
<descriptor id="bp-write" name="blog-post" type="unsafe" />
...
</alps>

bindings for a selected media type will result in different representations. 
- HTML will have two forms
- Atom might have a single <link id="blog-post" href="..." rel="edit" />
- UBER will have multiple <data .. /> elements with the appropriate "action" values
- and so forth.

There it is! There's the interesting case I was wondering about where multiple descriptors with a common name MAY end up yielding a single affordance that touches on multiple network promises.

I wonder if these finer distinctions will yield expressiveness/ambiguity problems under media-type binding specs. I'm loosely sensing that it will but I haven't seen enough media-type binding specs to say for sure yet. I have to think about it some more.

In any case, this is what I was looking for. Tx!

Cheers
Jeff Michaud

mca

unread,
Apr 12, 2017, 1:05:26 AM4/12/17
to Jeff Michaud, alps.io
bindings definitely need attention. that's one of the things i've pressed for in these threads - active work on bindings.

when we talk about the bindings between ALPS and a media type, we're likely to find out where the ALPS def weak, confusing, or lacking.

hoping to see a sample HAL binding soon.


Jeff Michaud

unread,
Apr 12, 2017, 11:39:01 AM4/12/17
to alps.io
There's already an advanced draft of the HAL-ALPS bindings spec available; filled with examples. No protocol bindings yet (I was initially thinking of leaving it out. Some feedback would be useful https://github.com/cometaj2/I-D/tree/master/hal-alps

Cheers
Jeff Michaud

mca

unread,
Apr 12, 2017, 11:44:23 AM4/12/17
to Jeff Michaud, alps.io
ahh, yes. i forgot that you'd announced this already. cool.

If it is OK w/ you, I'll start some issues there and we can work through details on the spec.


Jeff Michaud

unread,
Apr 12, 2017, 2:39:22 PM4/12/17
to alps.io
Sounds like a plan :)

Cheers,
Jeff Michaud

Reply all
Reply to author
Forward
0 new messages