Re: Forward/Backward versioning, media-types, etc

"mike amundsen [email protected] [rest-discuss]" <[email protected]> Sun, 24 Aug 2014 17:27:44 -0400
Newsgroups gmane.comp.web.services.rest
Message-ID <CAPW_8m5+X4HMip=HgunogO6k-W7BGF6k50TAMa-4gGrqNqY0MA@mail.gmail.com>
--bcaec53f3823f0c560050166be1f
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Kevin:

Your post covers a topic that comes up quite often in my discussion w/ devs
(both server- and client-side). My answer to the questions about how to
handle "change over time" may not be quite what you are looking for. But I
decided to pass it along anyway as it has helped me quite a bit over the
last several years in dealing with the problem.

TL;DNR
- Always make your service changes backward-compatible
- Services should promise to never take anything away, never change
processing of inputs, and always make new elements optional
- Services should only promise to return message formats (like HTML, HAL,
etc.) using pre-defined terms (e.g. "givenName", "familyName") and not
promise any functions to execute or objects in return
- Client apps should not use schema to constrain incoming messages, should
code defensively, and be designed to inspect incoming message for desired
terms, not designed to deserialize objects or execute remote functions.
- Essentially, both client and server should abide by Postel's Law

Doing this reduces the need to use "versioning" to separate one service
instance from another and can make services useful to client apps for quite
a long time w/o the need to "fork" the service and clients.

Hopefully this gives you some ideas on how to deal with changes in APIs.

Cheers.

Here's the long version.

BACKWARD-COMPATIBLE
First, I start from the POV that all changes will be backward-compatible.
IOW, no change in the future will invalidate the past. This is how both
HTTP and HTML have been working over the last couple decades. HTTP 1.0, 1.1
and lots of changes in between (PATCH, LINK methods, new headers, etc.) are
implemented in a way that doesn't break existing implementations.

THREE PROMISES
Next, any assurances/promises my service makes to clients is based on this
First Principle of "no breaking changes." That means it is important for me
to be clear about what I promise going forward. My promise will shape what
the client expects as non-breaking.  And here are my promises to client
apps that implement my service API
1 - i will never "take anything away" So, any URL I promise will either a)
still exists or b) be redirected. Any parameters I describe for URL queries
or request bodies will always be accepted. Any parameters described in a
response body will always be there.
2 - I will never change the processing/meaning of parameters. So, if i
promise the parameter "count" represents the number of users in a list, I
will not change this at some future point such that "count" represents the
number of pages in a collection, etc.
3 - Any new elements I introduce will be optional. I will not create new
*required* parameters for previously promised requests. If i need to create
new requirements, I will create a new promise and that promise will be
optional for any existing implementations.

PROMISE MESSAGES AND TERMS NOT FUNCTIONS AND OBJECTS
Next, my service almost never promise to return an object like a "user" or
"customer", etc. Instead, my service promises to return a message in one or
more formats -- message formats like HTML, HAL, Collection+JSON, Siren,
etc. Each of these formats have their own rules about what is legal, what
is required, etc. and my service will always honor those promises (from the
format designers).

My service publishes a list of valid vocabulary terms that can be returned
within messages. Terms like "givenName", "familyName", etc. The vocabulary
also includes action terms such as "update", "create", "share", "move",
"copy" or whatever other terms make sense for this service space. Client
applications need to be able to recognize and process these terms when the
appear.

This means client apps need to focus on the message and the terms, not some
idea of object or function when requesting, accepting, and parsing
responses. I make no promise that URL X will return object Y. Client apps
take on the task of parsing the message and then looking at that message to
find the terms (and their associated values) and be prepared to deal with
them when the appear in a response.

NON-BREAKING CHANGES BASED ON THESE PROMISES
Because my service makes promises based on message formats and vocabulary
terms, there are a series of changes that my service can make and claim as
non-breaking:
- a new optional parameter can now be used to filter requests (e.g.
&sort=3DfamilyName is now available for an existing search operation)
- a new operation exists that returns a summary of existing data (e.g. a
new URL w/ new arguments is published)
- one or more response terms have been moved in the message (e.g. givenName
is now at the end of the message instead of the top).

Client apps are expected to continue to function under the above
circumstances. That means client apps need to refrain from memorizing the
"shape" of responses or assuming certain values or terms will ALWAYS appear
in responses in the EXACT SAME PLACE as they did in the past. That means
strong schema-checking for inbound messages doesn't help -- actually hurts
-- client apps. It also means client apps need to safely ignore things they
don't recognize in responses. Ultimately, this means client apps are coded
"defensively" and must be implemented in a way that accounts for minor
changes like those listed here.

POSTEL'S LAW, RULE OF ROBUSTNESS
This is the embodiment of Postel's Law: "Be conservative in what you send,
be liberal in what you accept." HTML browsers work this way today. I don't
see examples of these kinds of client apps being talked about much in the
public space. Most of the time web API clients are implemented as scripted,
static consumers of strongly-typed response objects guaranteed against
pre-defined URLs. When that happens, even the smallest change in a request
or response body and cause the client app fail.






mamund
+1.859.757.1449
skype: mca.amundsen
http://amundsen.com/blog/
http://twitter.com/mamund
https://github.com/mamund
http://linkedin.com/in/mamund


On Sat, Aug 16, 2014 at 11:48 PM, Kevin Duffey [email protected]
[rest-discuss] <[email protected]> wrote:

>
>
>  Hi all,
>
> I've been reading up quite a bit on versioning, HATEOAS, forward/backward
> compatibility changes and versioning, etc with REST APIs. Some recent doc=
s
> are suggesting that more people are using versioning in the URI but only
> for major version changes of the entire API and update the version very
> rarely. I fall into the camp that the resource URI should not be versione=
d,
> instead using the Accept header and version the media-type (conneg).
>
> I do have a couple of questions though around all this.
>
> From some stuff I've read, it sounded like if I have a version 1 XML
> document, with say <a><b>data</b></a>, and that was public, and then I
> wanted to add a new element to that doc (representation) (e.g.
> <a><b/><c/></a> or <a><b><c/></b></a>), existing clients should not break
> because they would simply ignore the newly added element. However, I am
> seeing something somewhat different, in that we support JSON and XML, and
> in some cases, we're seeing exceptions when the new document is trying to
> be marshaled into a POJO object. I should say that we provide an XSD
> document that is used by our server side as well as clients to generate
> POJOs. We do so with Python and Java presently, but I assume any language
> could take our XSD and generate POJOs, then use those when
> marshaling/un-marshaling representations. So it seems, at least with XML,
> there may be a way (I don't know off hand) to "relax" the requirement for
> newly added elements to match the generated POJO objects, such that a
> client with our first version could actually receive the 2nd version
> document, but would only see the elements that their first version POJO
> understood during the marshaling process. Hope that makes sense. Anyway, =
I
> am confused if this is how it should be, with both XML and JSON, or if th=
e
> exception we're seeing means that our clients MUST indicate version 2 of
> the representation?
>
> I think the right answer is that ALL of our resources should always
> require the Accept header to include the version of the document, and fro=
m
> day one, it should be v1, so that when v2 comes out, existing clients are
> still using v1 (and we return v1 to them), but those clients that know
> about v2, can update their clients to request the v2 representation. At
> least from all that I've read, this seems safer than assuming a client
> using our XSDs to generate POJOs to use in their language, will get a
> properly marshaled v2 representation within a v1 POJO.
>
> Along similar lines.. when we add new items to an existing doc, then it's
> correct to bump the version value to get the new representation.. right?
> That is, we're currently working through a beta program and we're taking
> customer feedback to adjust/add/fix/remove/etc various API resource
> representations. During this cycle, some of our customers are still using
> version 1, some moved to version 2, and we're working on version 3 now. I=
s
> it "ok" to keep bumping the version in this case? At some point we'll
> release a GA and whatever version that is, stays in GA. However, our goal
> is to move to a more rapid release schedule, such that we may be adding
> features (new resources) along with updating (extending/adding to) existi=
ng
> resources (their representations). As we do this, I assume we should be
> bumping the versions of the specific resource representations that change
> (be it add, change or remove elements from the representation).. does tha=
t
> make sense?
>
>
>
>
>
>
>=20
>

--bcaec53f3823f0c560050166be1f
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable




<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/htm=
l4/strict.dtd">
<html>
<head>
</head>






=20
<body style=3D"background-color: #fff;">
<span style=3D"display:none">&nbsp;</span>

<!--~-|**|PrettyHtmlStartT|**|-~-->
<div id=3D"ygrp-mlmsg" style=3D"position:relative;">
  <div id=3D"ygrp-msg" style=3D"z-index: 1;">
<!--~-|**|PrettyHtmlEndT|**|-~-->

    <div id=3D"ygrp-text" >
=20=20=20=20=20=20
=20=20=20=20=20=20
      <p><div dir=3D"ltr">Kevin:<div><br></div><div>Your post covers a topi=
c that comes up quite often in my discussion w/ devs (both server- and clie=
nt-side). My answer to the questions about how to handle &quot;change over =
time&quot; may not be quite what you are looking for. But I decided to pass=
 it along anyway as it has helped me quite a bit over the last several year=
s in dealing with the problem.</div>

<div><br></div><div>TL;DNR</div><div>- Always make your service changes bac=
kward-compatible</div><div>- Services should promise to never take anything=
 away, never change processing of inputs, and always make new elements opti=
onal</div>

<div>- Services should only promise to return message formats (like HTML, H=
AL, etc.) using pre-defined terms (e.g. &quot;givenName&quot;, &quot;family=
Name&quot;) and not promise any functions to execute or objects in return</=
div>

<div>- Client apps should not use schema to constrain incoming messages, sh=
ould code defensively, and be designed to inspect incoming message for desi=
red terms, not designed to deserialize objects or execute remote functions.=
</div>

<div>- Essentially, both client and server should abide by Postel&#39;s Law=
=C2=A0</div><div><br></div><div>Doing this reduces the need to use &quot;ve=
rsioning&quot; to separate one service instance from another and can make s=
ervices useful to client apps for quite a long time w/o the need to &quot;f=
ork&quot; the service and clients.</div>

<div><br></div><div>Hopefully this gives you some ideas on how to deal with=
 changes in APIs.</div><div><br></div><div>Cheers.</div><div><br></div><div=
>Here&#39;s the long version.</div><div><br></div><div>BACKWARD-COMPATIBLE<=
/div>

<div>First, I start from the POV that all changes will be backward-compatib=
le. IOW, no change in the future will invalidate the past. This is how both=
 HTTP and HTML have been working over the last couple decades. HTTP 1.0, 1.=
1 and lots of changes in between (PATCH, LINK methods, new headers, etc.) a=
re implemented in a way that doesn&#39;t break existing implementations.=C2=
=A0</div>



<div><br></div><div>THREE PROMISES</div><div>Next, any assurances/promises =
my service makes to clients is based on this First Principle of &quot;no br=
eaking changes.&quot; That means it is important for me to be clear about w=
hat I promise going forward. My promise will shape what the client expects =
as non-breaking. =C2=A0And here are my promises to client apps that impleme=
nt my service API</div>


<div>1 - i will never &quot;take anything away&quot; So, any URL I promise =
will either a) still exists or b) be redirected. Any parameters I describe =
for URL queries or request bodies will always be accepted. Any parameters d=
escribed in a response body will always be there.</div>



<div>2 - I will never change the processing/meaning of parameters. So, if i=
 promise the parameter &quot;count&quot; represents the number of users in =
a list, I will not change this at some future point such that &quot;count&q=
uot; represents the number of pages in a collection, etc.</div>



<div>3 - Any new elements I introduce will be optional. I will not create n=
ew *required* parameters for previously promised requests. If i need to cre=
ate new requirements, I will create a new promise and that promise will be =
optional for any existing implementations.</div>



<div><br></div><div>PROMISE MESSAGES AND TERMS NOT FUNCTIONS AND OBJECTS</d=
iv><div>Next, my service almost never promise to return an object like a &q=
uot;user&quot; or &quot;customer&quot;, etc. Instead, my service promises t=
o return a message in one or more formats -- message formats like HTML, HAL=
, Collection&#43;JSON, Siren, etc. Each of these formats have their own rul=
es about what is legal, what is required, etc. and my service will always h=
onor those promises (from the format designers).</div>


<div><br></div><div>My service publishes a list of valid vocabulary terms t=
hat can be returned within messages. Terms like &quot;givenName&quot;, &quo=
t;familyName&quot;, etc. The vocabulary also includes action terms such as =
&quot;update&quot;, &quot;create&quot;, &quot;share&quot;, &quot;move&quot;=
, &quot;copy&quot; or whatever other terms make sense for this service spac=
e. Client applications need to be able to recognize and process these terms=
 when the appear.</div>


<div>=C2=A0</div><div>This means client apps need to focus on the message a=
nd the terms, not some idea of object or function when requesting, acceptin=
g, and parsing responses. I make no promise that URL X will return object Y=
. Client apps take on the task of parsing the message and then looking at t=
hat message to find the terms (and their associated values) and be prepared=
 to deal with them when the appear in a response.<br>


</div><div><br></div><div>NON-BREAKING CHANGES BASED ON THESE PROMISES</div=
><div>Because my service makes promises based on message formats and vocabu=
lary terms, there are a series of changes that my service can make and clai=
m as non-breaking:</div>


<div>- a new optional parameter can now be used to filter requests (e.g. &a=
mp;sort=3DfamilyName is now available for an existing search operation)</di=
v><div>- a new operation exists that returns a summary of existing data (e.=
g. a new URL w/ new arguments is published)</div>


<div>- one or more response terms have been moved in the message (e.g. give=
nName is now at the end of the message instead of the top).</div><div><br><=
/div><div>Client apps are expected to continue to function under the above =
circumstances. That means client apps need to refrain from memorizing the &=
quot;shape&quot; of responses or assuming certain values or terms will ALWA=
YS appear in responses in the EXACT SAME PLACE as they did in the past. Tha=
t means strong schema-checking for inbound messages doesn&#39;t help -- act=
ually hurts -- client apps. It also means client apps need to safely ignore=
 things they don&#39;t recognize in responses. Ultimately, this means clien=
t apps are coded &quot;defensively&quot; and must be implemented in a way t=
hat accounts for minor changes like those listed here.</div>

<div><br></div><div>POSTEL&#39;S LAW, RULE OF ROBUSTNESS<br>This is the emb=
odiment of Postel&#39;s Law: &quot;Be conservative in what you send, be lib=
eral in what you accept.&quot; HTML browsers work this way today. I don&#39=
;t see examples of these kinds of client apps being talked about much in th=
e public space. Most of the time web API clients are implemented as scripte=
d, static consumers of strongly-typed response objects guaranteed against p=
re-defined URLs. When that happens, even the smallest change in a request o=
r response body and cause the client app fail.<br>

</div><div><br></div><div><br></div><div><br></div><div><br></div>
</div><div class=3D"gmail_extra"><br clear=3D"all"><div><div dir=3D"ltr"><d=
iv><br></div>mamund<div><span><span title=3D"Call with Google Voice"><span =
title=3D"Call with Google Voice">&#43;1.859.757.1449</span></span></span><b=
r>skype: mca.amundsen<br>

<a href=3D"http://amundsen.com/blog/" target=3D"_blank">http://amundsen.com=
/blog/</a><br><a href=3D"http://twitter.com/mamund" target=3D"_blank">http:=
//twitter.com/mamund</a><br><a href=3D"https://github.com/mamund" target=3D=
"_blank">https://github.com/mamund</a><br>

<a href=3D"http://linkedin.com/in/mamund" target=3D"_blank">http://linkedin=
.com/in/mamund</a></div></div></div>
<br><br><div class=3D"gmail_quote">On Sat, Aug 16, 2014 at 11:48 PM, Kevin =
Duffey <a href=3D"mailto:[email protected]">[email protected]</a> [rest=
-discuss] <span dir=3D"ltr">&lt;<a href=3D"mailto:rest-discuss-noreply@yaho=
ogroups.com" target=3D"_blank">[email protected]</a>&gt;=
</span> wrote:<br>

<blockquote class=3D"gmail_quote" style=3D"border-left:1px #ccc solid;">






=20=20=20=20=20=20=20=20
<div>




<br><br>




<div style=3D"color:#000;background-color:#fff;font-family:HelveticaNeue,He=
lvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;font-size:12pt;"><div=
><div><div style=3D"color:rgb(0,0,0);font-family:HelveticaNeue,'Helvetica N=
eue',Helvetica,Arial,'Lucida Grande',sans-serif;font-size:12pt;background-c=
olor:rgb(255,255,255);">

<div>Hi all,</div><div><br></div><div style=3D"color:rgb(0,0,0);font-size:1=
6px;font-family:HelveticaNeue,'Helvetica Neue',Helvetica,Arial,'Lucida Gran=
de',sans-serif;font-style:normal;background-color:transparent;">

I&#39;ve been reading up quite a bit on versioning, HATEOAS, forward/backwa=
rd compatibility changes and versioning, etc with REST APIs. Some recent do=
cs are suggesting that
 more people are using versioning in the URI but only for major version cha=
nges of the entire API and update the version very rarely. I fall into the =
camp that the resource URI should not be versioned, instead using the Accep=
t header and version the media-type
 (conneg).=C2=A0</div><div style=3D"color:rgb(0,0,0);font-size:16px;font-fa=
mily:HelveticaNeue,'Helvetica Neue',Helvetica,Arial,'Lucida Grande',sans-se=
rif;font-style:normal;background-color:transparent;"><br></div>

<div style=3D"color:rgb(0,0,0);font-size:16px;font-family:HelveticaNeue,'He=
lvetica Neue',Helvetica,Arial,'Lucida Grande',sans-serif;font-style:normal;=
background-color:transparent;">I do have a couple of questions though aroun=
d all this.</div>

<div style=3D"color:rgb(0,0,0);font-size:16px;font-family:HelveticaNeue,'He=
lvetica Neue',Helvetica,Arial,'Lucida Grande',sans-serif;font-style:normal;=
background-color:transparent;"><br></div><div style=3D"color:rgb(0,0,0);fon=
t-size:16px;font-family:HelveticaNeue,'Helvetica Neue',Helvetica,Arial,'Luc=
ida Grande',sans-serif;font-style:normal;background-color:transparent;">

From some stuff I&#39;ve read, it sounded like if I have a version 1 XML do=
cument, with say &lt;a&gt;&lt;b&gt;data&lt;/b&gt;&lt;/a&gt;, and that was p=
ublic, and then I wanted to add a new element to that doc (representation) =
(e.g. &lt;a&gt;&lt;b/&gt;&lt;c/&gt;&lt;/a&gt; or &lt;a&gt;&lt;b&gt;&lt;c/&g=
t;&lt;/b&gt;&lt;/a&gt;), existing clients should not break because they wou=
ld simply ignore the newly added element. However, I am seeing something so=
mewhat different, in that we support JSON and XML, and in some cases, we&#3=
9;re seeing exceptions when the new document is trying to be marshaled into=
 a POJO object. I should say that we provide an XSD document that is used b=
y our server side as well as clients to generate POJOs. We do
 so with Python and Java presently, but I assume any language could take ou=
r XSD and generate POJOs, then
 use those when marshaling/un-marshaling representations. So it seems, at l=
east with XML, there may be a way (I don&#39;t know off hand) to &quot;rela=
x&quot; the requirement for newly added elements to match the generated POJ=
O objects, such that a client with our first version could actually receive=
 the 2nd version document, but would only see the elements that their first=
 version POJO understood during the marshaling process. Hope that makes sen=
se. Anyway, I am confused if this is how it should be, with both XML and JS=
ON, or if the exception we&#39;re seeing means that our clients MUST indica=
te version 2 of the representation?</div>

<div style=3D"color:rgb(0,0,0);font-size:16px;font-family:HelveticaNeue,'He=
lvetica Neue',Helvetica,Arial,'Lucida Grande',sans-serif;font-style:normal;=
background-color:transparent;"><br></div><div style=3D"color:rgb(0,0,0);fon=
t-size:16px;font-family:HelveticaNeue,'Helvetica Neue',Helvetica,Arial,'Luc=
ida Grande',sans-serif;font-style:normal;background-color:transparent;">

I think the right answer is that ALL of our resources should always require=
 the Accept header to include the version of the document, and from day one=
, it should be v1, so that when v2 comes out, existing clients are still us=
ing v1 (and we return v1 to them), but those clients that know about v2, ca=
n update their clients to request the v2 representation. At least from all =
that I&#39;ve read, this seems safer than assuming a client using our XSDs =
to generate POJOs to use in their language, will get a properly marshaled v=
2 representation within a v1 POJO.</div>

<div style=3D"color:rgb(0,0,0);font-size:16px;font-family:HelveticaNeue,'He=
lvetica Neue',Helvetica,Arial,'Lucida Grande',sans-serif;font-style:normal;=
background-color:transparent;"><br></div><div style=3D"color:rgb(0,0,0);fon=
t-size:16px;font-family:HelveticaNeue,'Helvetica Neue',Helvetica,Arial,'Luc=
ida Grande',sans-serif;font-style:normal;background-color:transparent;">

Along similar lines.. when we add new items to an existing doc, then it&#39=
;s correct to bump the version value to get the new representation.. right?=
 That is, we&#39;re currently working through a beta program and we&#39;re =
taking customer feedback to adjust/add/fix/remove/etc various API resource =
representations. During this cycle, some of our customers are still using v=
ersion 1, some moved to version 2, and we&#39;re working on version 3 now. =
Is it &quot;ok&quot; to keep bumping the version in this case? At some poin=
t we&#39;ll release a GA and whatever version that is, stays in GA. However=
, our goal
 is to move to a more rapid release schedule, such that we may be adding fe=
atures (new resources) along with
 updating (extending/adding to) existing resources (their representations).=
 As we do this, I assume we should be bumping the versions of the specific =
resource representations that change (be it add, change or remove elements =
from the representation).. does that make sense?</div>

<div style=3D"color:rgb(0,0,0);font-size:16px;font-family:HelveticaNeue,'He=
lvetica Neue',Helvetica,Arial,'Lucida Grande',sans-serif;font-style:normal;=
background-color:transparent;"><br></div><div style=3D"color:rgb(0,0,0);fon=
t-size:16px;font-family:HelveticaNeue,'Helvetica Neue',Helvetica,Arial,'Luc=
ida Grande',sans-serif;font-style:normal;background-color:transparent;">

<br></div><div style=3D"color:rgb(0,0,0);font-size:16px;font-family:Helveti=
caNeue,'Helvetica Neue',Helvetica,Arial,'Lucida Grande',sans-serif;font-sty=
le:normal;background-color:transparent;"><br></div><div style=3D"color:rgb(=
0,0,0);font-size:16px;font-family:HelveticaNeue,'Helvetica Neue',Helvetica,=
Arial,'Lucida Grande',sans-serif;font-style:normal;background-color:transpa=
rent;">

<br></div></div></div></div></div>





<br>


<br>




<div width=3D"1" style=3D"color:white;"></div>
</div></blockquote></div><br></div>
</p>

    </div>
=20=20=20=20=20

    <!--~-|**|PrettyHtmlStart|**|-~-->
    <div style=3D"color: #fff; height: 0;">__._,_.___</div>

=20=20=20=20=20=20=20=20=20=20
=20=20
=20

=20=20=20=20
    <div style=3D"clear:both"> </div>

    <div id=3D"fromDMARC" style=3D"margin-top: 10px;">
        <hr style=3D"height:2px ; border-width:0; color:#E3E3E3; background=
-color:#E3E3E3;">
        Posted by: mike amundsen &lt;[email protected]&gt;        <hr style=
=3D"height:2px ; border-width:0; color:#E3E3E3; background-color:#E3E3E3;">
     </div>
    <div style=3D"clear:both"> </div>

    <table cellspacing=3D4px style=3D"margin-top: 10px; margin-bottom: 10px=
; color: #2D50FD;">
      <tbody>
        <tr>
          <td style=3D"font-size: 12px; font-family: arial; font-weight: bo=
ld; padding: 7px 5px 5px;"  >
                          <a style=3D"text-decoration: none; color: #2D50FD=
" href=3D"https://groups.yahoo.com/neo/groups/rest-discuss/conversations/me=
ssages/19704;_ylc=3DX3oDMTJxbXE2cWhwBF9TAzk3MzU5NzE0BGdycElkAzQzMTkyNTUEZ3J=
wc3BJZAMxNzA1NzAxMDE0BG1zZ0lkAzE5NzA0BHNlYwNmdHIEc2xrA3JwbHkEc3RpbWUDMTQwOD=
kxNTY5MQ--?act=3Dreply&messageNum=3D19704">Reply via web post</a>
                      </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;" >
            <a href=3D"mailto:[email protected]?subject=3DRe%3A%20%5Brest-di=
scuss%5D%20Forward%2FBackward%20versioning%2C%20media-types%2C%20etc" style=
=3D"text-decoration: none; color: #2D50FD;">
               Reply to sender            </a>
          </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;">
            <a href=3D"mailto:[email protected]?subject=3DRe%3A%=
20%5Brest-discuss%5D%20Forward%2FBackward%20versioning%2C%20media-types%2C%=
20etc" style=3D"text-decoration: none; color: #2D50FD">
              Reply to group            </a>
          </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;" >
            <a href=3D"https://groups.yahoo.com/neo/groups/rest-discuss/con=
versations/newtopic;_ylc=3DX3oDMTJlcHIyZnZsBF9TAzk3MzU5NzE0BGdycElkAzQzMTky=
NTUEZ3Jwc3BJZAMxNzA1NzAxMDE0BHNlYwNmdHIEc2xrA250cGMEc3RpbWUDMTQwODkxNTY5MQ-=
-" style=3D"text-decoration: none; color: #2D50FD">Start a New Topic</a>
          </td>
          <td>&bull;</td>
          <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p=
x 5px;color: #2D50FD;" >
                            <a href=3D"https://groups.yahoo.com/neo/groups/=
rest-discuss/conversations/topics/19703;_ylc=3DX3oDMTM2OXZpZGJiBF9TAzk3MzU5=
NzE0BGdycElkAzQzMTkyNTUEZ3Jwc3BJZAMxNzA1NzAxMDE0BG1zZ0lkAzE5NzA0BHNlYwNmdHI=
Ec2xrA3Z0cGMEc3RpbWUDMTQwODkxNTY5MQR0cGNJZAMxOTcwMw--" style=3D"text-decora=
tion: none; color: #2D50FD;">Messages in this topic</a>
                (2)
                      </td>
        </tr>
      </tbody>
    </table>

=20=20=20=20=20=20=20=20

<!------- Start Nav Bar ------>




=20

<!-- |**|begin egp html banner|**| -->
<div id=3D"ygrp-vital" style=3D"background-color: #f2f2f2; font-family: Ver=
dana; font-size: 10px; margin-bottom: 10px; padding: 10px;">

    <span id=3D"vithd" style=3D"font-weight: bold; color: #333; text-transf=
orm: uppercase; "><a href=3D"https://groups.yahoo.com/neo/groups/rest-discu=
ss/info;_ylc=3DX3oDMTJlc2hubjFpBF9TAzk3MzU5NzE0BGdycElkAzQzMTkyNTUEZ3Jwc3BJ=
ZAMxNzA1NzAxMDE0BHNlYwN2dGwEc2xrA3ZnaHAEc3RpbWUDMTQwODkxNTY5MQ--" style=3D"=
text-decoration: none;">Visit Your Group</a></span>

     <ul style=3D"list-style-type: none; margin: 0; padding: 0; display: in=
line;">
            <li style=3D"border-right: 1px solid #000; font-weight: 700; di=
splay: inline; padding: 0 5px; margin-left: 0;">
      <span class=3D"cat"><a href=3D"https://groups.yahoo.com/neo/groups/re=
st-discuss/members/all;_ylc=3DX3oDMTJmMjJxNzRwBF9TAzk3MzU5NzE0BGdycElkAzQzM=
TkyNTUEZ3Jwc3BJZAMxNzA1NzAxMDE0BHNlYwN2dGwEc2xrA3ZtYnJzBHN0aW1lAzE0MDg5MTU2=
OTE-" style=3D"text-decoration: none;">New Members</a></span>
      <span class=3D"ct" style=3D"color: #ff7900;">2</span>
    </li>
                                              </ul>
  </div>


<div id=3D"ft" style=3D"font-family: Arial; font-size: 11px; margin-top: 5p=
x; padding: 0 2px 0 0; clear: both;">
  <a href=3D"https://groups.yahoo.com/neo;_ylc=3DX3oDMTJkNDloaDdqBF9TAzk3ND=
c2NTkwBGdycElkAzQzMTkyNTUEZ3Jwc3BJZAMxNzA1NzAxMDE0BHNlYwNmdHIEc2xrA2dmcARzd=
GltZQMxNDA4OTE1Njkx" style=3D"float: left;"><img src=3D"http://l.yimg.com/r=
u/static/images/yg/img/email/new_logo/logo-groups-137x15.png" height=3D"15"=
 width=3D"137" alt=3D"Yahoo! Groups" style=3D"border: 0;"/></a>
  <div style=3D"color: #747575; float: right;"> &bull; <a href=3D"https://i=
nfo.yahoo.com/privacy/us/yahoo/groups/details.html" style=3D"text-decoratio=
n: none;">Privacy</a> &bull; <a href=3D"mailto:rest-discuss-unsubscribe@yah=
oogroups.com?subject=3DUnsubscribe" style=3D"text-decoration: none;">Unsubs=
cribe</a> &bull; <a href=3D"https://info.yahoo.com/legal/us/yahoo/utos/term=
s/" style=3D"text-decoration: none;">Terms of Use</a> </div>
</div>
<br>

<!-- |**|end egp html banner|**| -->

  </div> <!-- ygrp-msg -->

=20
  <!-- Sponsor -->
  <!-- |**|begin egp html banner|**| -->
  <div id=3D"ygrp-sponsor" style=3D"width:160px; float:right; clear:none; m=
argin:0 0 25px 0; background: #fff;">

<!-- Start Recommendations -->
<div id=3D"ygrp-reco">
     </div>
<!-- End Recommendations -->



  </div>   <!-- |**|end egp html banner|**| -->

  <div style=3D"clear:both; color: #FFF; font-size:1px;">.</div>
</div>

  <img src=3D"http://geo.yahoo.com/serv?s=3D97359714/grpId=3D4319255/grpspI=
d=3D1705701014/msgId=3D19704/stime=3D1408915691" width=3D"1" height=3D"1"> =
<br>

<img src=3D"http://y.analytics.yahoo.com/fpc.pl?ywarid=3D515FB27823A7407E&a=
=3D10001310322279&js=3Dno&resp=3Dimg" width=3D"1" height=3D"1">=20

<div style=3D"color: #fff; height: 0;">__,_._,___</div>
<!--~-|**|PrettyHtmlEnd|**|-~-->

</body>

<!--~-|**|PrettyHtmlStart|**|-~-->
<head>
  <style type=3D"text/css">
  <!--
  #ygrp-mkp {
  border: 1px solid #d8d8d8;
  font-family: Arial;
  margin: 10px 0;
  padding: 0 10px;
}

#ygrp-mkp hr {
  border: 1px solid #d8d8d8;
}

#ygrp-mkp #hd {
  color: #628c2a;
  font-size: 85%;
  font-weight: 700;
  line-height: 122%;
  margin: 10px 0;
}

#ygrp-mkp #ads {
  margin-bottom: 10px;
}

#ygrp-mkp .ad {
  padding: 0 0;
}

#ygrp-mkp .ad p {
  margin: 0;
}

#ygrp-mkp .ad a {
  color: #0000ff;
  text-decoration: none;
}
  #ygrp-sponsor #ygrp-lc {
  font-family: Arial;
}

#ygrp-sponsor #ygrp-lc #hd {
  margin: 10px 0px;
  font-weight: 700;
  font-size: 78%;
  line-height: 122%;
}

#ygrp-sponsor #ygrp-lc .ad {
  margin-bottom: 10px;
  padding: 0 0;
}

  #actions {
    font-family: Verdana;
    font-size: 11px;
    padding: 10px 0;
  }

  #activity {
    background-color: #e0ecee;
    float: left;
    font-family: Verdana;
    font-size: 10px;
    padding: 10px;
  }

  #activity span {
    font-weight: 700;
  }

  #activity span:first-child {
    text-transform: uppercase;
  }

  #activity span a {
    color: #5085b6;
    text-decoration: none;
  }

  #activity span span {
    color: #ff7900;
  }

  #activity span .underline {
    text-decoration: underline;
  }

  .attach {
    clear: both;
    display: table;
    font-family: Arial;
    font-size: 12px;
    padding: 10px 0;
    width: 400px;
  }

  .attach div a {
    text-decoration: none;
  }

  .attach img {
    border: none;
    padding-right: 5px;
  }

  .attach label {
    display: block;
    margin-bottom: 5px;
  }

  .attach label a {
    text-decoration: none;
  }
=20=20
  blockquote {
    margin: 0 0 0 4px;
  }

  .bold {
    font-family: Arial;
    font-size: 13px;
    font-weight: 700;
  }

  .bold a {
    text-decoration: none;
  }

  dd.last p a {
    font-family: Verdana;
    font-weight: 700;
  }

  dd.last p span {
    margin-right: 10px;
    font-family: Verdana;
    font-weight: 700;
  }

  dd.last p span.yshortcuts {
    margin-right: 0;
  }

  div.attach-table div div a {
    text-decoration: none;
  }

  div.attach-table {
    width: 400px;
  }

  div.file-title a, div.file-title a:active, div.file-title a:hover, div.fi=
le-title a:visited {
    text-decoration: none;
  }

  div.photo-title a, div.photo-title a:active, div.photo-title a:hover, div=
.photo-title a:visited {
    text-decoration: none;
  }

  div#ygrp-mlmsg #ygrp-msg p a span.yshortcuts {
    font-family: Verdana;
    font-size: 10px;
    font-weight: normal;
  }

  .green {
    color: #628c2a;
  }

  .MsoNormal {
    margin: 0 0 0 0;
  }

  o {
    font-size: 0;
  }

  #photos div {
    float: left;
    width: 72px;
  }

  #photos div div {
    border: 1px solid #666666;
    height: 62px;
    overflow: hidden;
    width: 62px;
  }

  #photos div label {
    color: #666666;
    font-size: 10px;
    overflow: hidden;
    text-align: center;
    white-space: nowrap;
    width: 64px;
  }

  #reco-category {
    font-size: 77%;
  }

  #reco-desc {
    font-size: 77%;
  }

  .replbq {
    margin: 4px;
  }

  #ygrp-actbar div a:first-child {
   /* border-right: 0px solid #000;*/
    margin-right: 2px;
    padding-right: 5px;
  }

  #ygrp-mlmsg {
    font-size: 13px;
    font-family: Arial, helvetica,clean, sans-serif;
    *font-size: small;
    *font: x-small;
  }

  #ygrp-mlmsg table {
    font-size: inherit;
    font: 100%;
  }

  #ygrp-mlmsg select, input, textarea {
    font: 99% Arial, Helvetica, clean, sans-serif;
  }

  #ygrp-mlmsg pre, code {
    font:115% monospace;
    *font-size:100%;
  }

  #ygrp-mlmsg * {
    line-height: 1.22em;
  }

  #ygrp-mlmsg #logo {
    padding-bottom: 10px;
  }


  #ygrp-msg p a {
    font-family: Verdana;
  }

  #ygrp-msg p#attach-count span {
    color: #1E66AE;
    font-weight: 700;
  }

  #ygrp-reco #reco-head {
    color: #ff7900;
    font-weight: 700;
  }

  #ygrp-reco {
    margin-bottom: 20px;
    padding: 0px;
  }

  #ygrp-sponsor #ov li a {
    font-size: 130%;
    text-decoration: none;
  }

  #ygrp-sponsor #ov li {
    font-size: 77%;
    list-style-type: square;
    padding: 6px 0;
  }=20

  #ygrp-sponsor #ov ul {
    margin: 0;
    padding: 0 0 0 8px;
  }

  #ygrp-text {
    font-family: Georgia;
  }

  #ygrp-text p {
    margin: 0 0 1em 0;
  }

  #ygrp-text tt {
    font-size: 120%;
  }

  #ygrp-vital ul li:last-child {
    border-right: none !important;=20
  }=20
  -->
  </style>
</head>

<!--~-|**|PrettyHtmlEnd|**|-~-->
</html>
<!-- end group email -->


--bcaec53f3823f0c560050166be1f--