Hypermedia APIs
@jankronquist
Hypermedia API:s @ Jayway
Rickard Öberg (~2010)!
Qi4j & Streamflow!
Usecases as your API!
Mattias Arthursson & Karl-Johan Stenflo (~2011)!
JAX-RS-HATEOAS!
HATEOAS with Standard Java APIs!
Mads Enevoldsen & Jan Kronquist (~2011)!
Forest (CQS conventions, sensible default links, constraints...)!
Gustaf Nilsson Kotte (~2012)!
Adaptive HTML as your API
Outline
Example domain!
REST introduction (Richardson maturity model)!
Exploring media types!
html!
collection+json!
hal+json
Example domain
Rock - Paper - Scissors
Opening gambit
http://www.worldrps.com/gambit-play
Opening gambit
http://www.worldrps.com/gambit-play
http://rps.com
The future Facebook of Rock Paper Scissors!
Millions of users!
Many games per user
Playing the game
Player A
Player B
Server
rock
paper
Player B: paper
Player A: rock
Game 123
Game 123	

winner: Player B	

loser: Player A
CREATED WAITING
GAME WON
GAME TIED
any move
other move	

(victory)
other move	

(tie)
T
Introducing REST
Towards REST
REST architectural style!
Defined by Roy Fielding !
Richardson maturity model
http://martinfowler.com/articles/richardsonMaturityModel.html
Level 0 - Not at all RESTful
Single resource, single verb!
Examples:!
SOAP, XML-RPC, JSON-RPC
Level 0 - Example
POST /api
--> { "method": “viewGame", "params": ["123"]}
<-- { "result": { "state": "waiting"},
"error": null}
POST /api
--> { "method": "makeMove", "params": ["123","rock"]}
<-- { "result": { "state": "won",
"winner": "player1",
"loser": "player2"},
"error": null}
Level 0 - Analysis
HTTP as transport protocol!
Problems!
No client side caching!
Tight coupling
Level 0 - In the wild
JSON/RPC over HTTP!
Why?!
Simplicity!
Track all user actions!
If it ain’t broke, don't fix it
Level 1 - Resources
Many resources, single verb!
/api/games/123
/api/players/player1
Almost “object oriented”
Level 1 - Example
POST /api/games/123
--> { "method": "viewGame"}
<-- { "result": { "state": "waiting"},
"error": null}
POST /api/games/123
--> { "method": "makeMove", "params": ["rock"]}
<-- { "result": { "state": "won",
"winner": "player1",
"loser": "player2"},
"error": null}
Level 1 - Analysis
Still just using HTTP as transport
Level 2 - Verbs
Resources, verb and status codes!!
GET, POST, PUT, DELETE...
200 OK, 304 Not Modified, 404 Not found…
RESTish!
Uniform interface
HTTP 101
GET - safe!
POST - danger!!
PUT - idempotent!
!
Note!
GET will not modify the resource, but other clients might!
Interleaving other requests might break idempotency
GET /api/games/123
<-- 200 OK
<-- { "state": "waiting"}
POST /api/games/123
--> { "move": "rock"}
<-- 200 OK
<-- { "state": "won",
"winner": "player1",
"loser": “player2"}
Level 2 - Example
GET /api/games/123
<-- 200 OK
<-- { "state": "waiting"}
POST /api/games/123
--> Content-Type: application/json
--> { "move": "rock"}
<-- 200 OK
<-- { "state": "won",
"winner": "player1",
"loser": "player2"}
Level 2 - Mediatype json
Level 2 - Mediatype form
GET /api/games/123
<-- 200 OK
<-- { "state": "waiting"}
POST /api/games/123
--> Content-Type: application/x-www-form-urlencoded
--> move=rock
<-- 200 OK
<-- { "state": "won",
"winner": "player1",
"loser": "player2"}
$ curl -d move=rock http://rps.com/api/games/123
Level 2 - In the wild
Verbs: GET, DELETE, PUT!
Resources: buckets, objects, acls, policies…!
Why?!
CRUD operations!
GET allows caching
Level 2 - Analysis
Using HTTP, not fighting it!
Problems!
Client must construct URLs!
Client must contain business rules
GET /api/games
<-- 200 OK
<-- { "totalCount": 23,
"offset": 0,
"pageSize": 10,
"items": [{"id": "111",
"players": ["jan", "cecilia"]},
{"id": "222",
"players": ["jan", "mike"]},
{"id": "333",
"players": ["cecilia", "mike"]},
...]}
Level 2 - Constructing URLs
GET /api/games?offset=10 GET /api/games?page=2
How to navigate to page 2?
GET /api/games
<-- 200 OK
<-- { "totalCount": 23,
"offset": 0,
"pageSize": 10,
"items": [{"id": "111",
"players": ["jan", "cecilia"]},
{"id": "222",
"players": ["jan", "mike"]},
{"id": "333",
"players": ["cecilia", "mike"]},
...]}
Level 2 - Constructing URLs
GET /api/games/{game.id}
Navigating to game
Level 2 - Business logic
GET /api/games/123
<-- 200 OK
<-- { "state": "waiting",
"players": ["ply1", "ply2"],
"moves": { "ply1" : true}
}
Should the client display the move selector?
Level 2 - Business logic solved?
GET /api/games/123
<-- 200 OK
<-- { "state": "waiting",
"players": ["ply1", "ply2"],
"moves": { "ply1" : true},
"shouldMove": true
}
Level 3 - Hypermedia Controls
Links & forms!
REST as defined by Roy Fielding
Hypermedia is defined by the presence of application control information embedded within, !
or as a layer above, the presentation of information
The simultaneous presentation of information and controls such that the information
becomes the affordance through which the user obtains choices and selects actions.
GET /api/games
<-- 200 OK
<-- { "totalCount": 23,
"offset": 0,
"pageSize": 10,
"next": "/api/games?offset=10",
"items": [{"href": "/api/games/111",
"players": ["jan", "cecilia"]},
{"href": "/api/games/222",
"players": ["jan", "mike"]},
{"href": "/api/games/333"
"players": ["cecilia", "mike"]},
...]}
Level 3 - Example (links)
Level 3 - Example (form)
<html>	
<body>	
<ol id="players">	
<li>Jan</li>	
<li>Cecilia</li>	
</ol>	
<form action="/api/games/111" method="POST">	
<select name="move">	
<option value="rock">Rock</option>	
<option value="paper">Paper</option>	
<option value="scissors">Scissors</option>	
</select>	
<input type="submit"/>	
</form>	
</body>	
</html>
Level 3 - Opportunity: Extra moves
<select name="move">	
<option value="rock">Rock</option>	
<option value="paper">Paper</option>	
<option value="scissors">Scissors</option>	
<option value="lizard">Lizard</option>	
<option value="spock">Spock</option>	
</select>
Level 3 - Changing the rules
For one of the players: Flip a coin!
head - he must play rock!
tail - he can play whatever he wants!
!
How can the other player take advantage of this?
http://blog.gtorangebuilder.com/2014/04/gto-brain-teaser-1-exploitation-and.html
<select name="move">	
<option value="rock">Rock</option>	
</select>
Level 3 - In the wild
Media types &
other tools
Media types
Format!
application/json, text/xml, text/plain, text/csv!
Domain specific!
text/vcard, text/calendar, application/calendar+json!
Hypermedia!
text/html, application/xhtml+xml!
application/vnd.collection+json, application/vnd.hal+json!
application/vnd.siren+json, application/vnd.amundsen-uber+json
GET /api/games
<-- 200 OK
<-- Content-Type: application/json
<-- { "totalCount": 23,
"offset": 0,
"pageSize": 10,
"next": "/api/games?offset=10",
...
}
json
xhtml
GET /api/games
<-- 200 OK
<-- Content-Type: application/xhtml+xml
<--
<html>	
<body>	
	<a href="/api/games?offset=10">Next</a>	
	 <div id="totalCount">23</div>	
<ol id="games">	
<li>...</li>	
<li>...</li>	
</ol>	
</body>	
</html>
collection+json
CRUD for collections of objects!
Parts of the document:!
items!
links!
queries!
templates!
errors
GET /api/games
<-- 200 OK
<-- Content-Type: application/vnd.collection+json
<-- { "collection": {
"version" : "1.0",
"href" : "http://rps.com/api/games",
"links" : [
{"rel" : "next", "href" : "/api/games?offset=10"}
],
"items" : [
{ ... },
{ ... }
]
}
}
collection+json
Anatomy of a Link
target url (href)!
human readable string (title)!
semantic information (rel)!
returned media types (type)!
!
http method (method)!
secondary key (name)!
support media types for requests (accept)
RFC 5988
Standard rels
item!
collection!
next!
edit!
enclosure!
latest-version!
self
http://www.iana.org/assignments/link-relations/link-relations.xml
Custom rels
Register with IANA :-)!
Just make up your own :-)!
move!
URI!
http://rps.com/rels/move!
CURIE!
rps:move!
http://rps.com/rels/{rel}
Forms / templates
More than links!
Allow user input!
May provide!
Default values!
Data types!
Possible values
html form
<html>	
<body>	
<ol id="players">	
<li>Jan</li>	
<li>Cecilia</li>	
</ol>	
<form action="/api/games/111" method=“POST" name="move">	
<select name="move">	
<option value="rock">Rock</option>	
<option value="paper">Paper</option>	
<option value="scissors">Scissors</option>	
</select>	
<input type="submit"/>	
</form>	
</body>	
</html>
GET /api/games/111
<-- 200 OK
<-- Content-Type: application/vnd.collection+json
<-- { "collection": {
"version" : "1.0",
"href" : "http://rps.com/api/games/111",
"template" : {
"data" : [{
"name" : "move",
"value" : "",
"prompt" : "Your move"
}]
}
}
}
collection+json template for writes
Forced resource structure
Collection	

of games
/api/games/
Game 111
/api/games/111
Collection of
moves for game
111
/api/games/111/moves
Player 1 move
for game 111
/api/games/111/moves/ply1
item
rps:moves
item
GET /api/games/111
<-- 200 OK
<-- Content-Type: application/vnd.collection+json
<-- { "collection": {
"version" : "1.0",
"href" : "http://rps.com/api/games/111",
"links" : [
{"rel" : "rps:moves", "href" : "/api/games/111/moves"}
],
...
}
}
The game links to moves
GET /api/games/111/moves
<-- 200 OK
<-- Content-Type: application/vnd.collection+json
<-- { "collection": {
"version" : "1.0",
"href" : "http://rps.com/api/games/111/moves",
"template" : {
"data" : {
"name" : "move",
"value" : "",
"prompt" : "Your move"
}
}
}
}
moves collection contain template
URI templates
Constructing paths



Form-style!
Making a move



Creating a new game
RFC 6570
"http://rps.com/api/games/111{?move}"
"http://rps.com/api/games/{gameId}"
"http://rps.com/api/games{?player1,player2}"
application/vnd.hal+json
Minimalistic (plain old json)!
"_links" with mandatory rels!
"_embedded" (recursive!)
GET http://rps.com/api/games/123
<-- {
!
!
!
!
!
!
!
!
!
!
!
!
!
!
"createdAt" : "2014-05-22T16:24:01",
"state" : "waiting"
}
hal+json
GET http://rps.com/api/games/123
<-- {
!
!
!
!
!
!
!
!
"_links": {
"rps:move": {
"href": "http://rps.com/api/games/123{?move}",
"templated": true
}
}
"createdAt" : "2014-05-22T16:24:01",
"state" : "waiting"
}
hal+json
GET http://rps.com/api/games/123
<-- { "_embedded": {
"rps:player" : [{
"_links": { "self": {"href" : "http://rps.com/api/players/111"},
"name": "Jan"
}, {
"_links": { "self": {"href" : "http://rps.com/api/players/222"},
"name": "Cecilia"
}]
},
"_links": {
"rps:move": {
"href": "http://rps.com/api/games/123{?move}",
"templated": true
}
}
"createdAt" : "2014-05-22T16:24:01",
"state" : "waiting"
}
hal+json
Application state, not objects
GET http://rps.com/api/players/111/games/123
<-- { "_links": {
"rps:game": {"href" : "http://rps.com/api/games/123"},
"rps:move": {
"href": "http://rps.com/api/games/123{?move}",
"templated": true
}
}
}
GET http://rps.com/api/players/111/games/123
<-- { "_embedded": {
"rps:game" : [{
"_links": { "self": {"href" : "http://rps.com/api/games/123"},
"createdAt" : "2014-05-22T16:24:01",
"state" : "waiting"
}]
},
"_links": {
"rps:move": {
"href": "http://rps.com/api/games/123{?move}",
"templated": true
}
}
}
Application state, not objects
Summary
Hypermedia simplifies client development!
No constructing of URLs!
Opportunities for less business logic!
Pick a mediatype with reasonable semantics!
Consider form-encoded when POSTing!
Some useful tools!
CURIE, URITemplates

Hypermedia APIs - GeekOut

  • 1.
  • 2.
    Hypermedia API:s @Jayway Rickard Öberg (~2010)! Qi4j & Streamflow! Usecases as your API! Mattias Arthursson & Karl-Johan Stenflo (~2011)! JAX-RS-HATEOAS! HATEOAS with Standard Java APIs! Mads Enevoldsen & Jan Kronquist (~2011)! Forest (CQS conventions, sensible default links, constraints...)! Gustaf Nilsson Kotte (~2012)! Adaptive HTML as your API
  • 3.
    Outline Example domain! REST introduction(Richardson maturity model)! Exploring media types! html! collection+json! hal+json
  • 4.
  • 5.
    Rock - Paper- Scissors
  • 6.
  • 7.
  • 8.
    http://rps.com The future Facebookof Rock Paper Scissors! Millions of users! Many games per user
  • 9.
    Playing the game PlayerA Player B Server rock paper Player B: paper Player A: rock Game 123 Game 123 winner: Player B loser: Player A CREATED WAITING GAME WON GAME TIED any move other move (victory) other move (tie) T
  • 10.
  • 11.
    Towards REST REST architecturalstyle! Defined by Roy Fielding ! Richardson maturity model http://martinfowler.com/articles/richardsonMaturityModel.html
  • 12.
    Level 0 -Not at all RESTful Single resource, single verb! Examples:! SOAP, XML-RPC, JSON-RPC
  • 13.
    Level 0 -Example POST /api --> { "method": “viewGame", "params": ["123"]} <-- { "result": { "state": "waiting"}, "error": null} POST /api --> { "method": "makeMove", "params": ["123","rock"]} <-- { "result": { "state": "won", "winner": "player1", "loser": "player2"}, "error": null}
  • 14.
    Level 0 -Analysis HTTP as transport protocol! Problems! No client side caching! Tight coupling
  • 15.
    Level 0 -In the wild JSON/RPC over HTTP! Why?! Simplicity! Track all user actions! If it ain’t broke, don't fix it
  • 16.
    Level 1 -Resources Many resources, single verb! /api/games/123 /api/players/player1 Almost “object oriented”
  • 17.
    Level 1 -Example POST /api/games/123 --> { "method": "viewGame"} <-- { "result": { "state": "waiting"}, "error": null} POST /api/games/123 --> { "method": "makeMove", "params": ["rock"]} <-- { "result": { "state": "won", "winner": "player1", "loser": "player2"}, "error": null}
  • 18.
    Level 1 -Analysis Still just using HTTP as transport
  • 19.
    Level 2 -Verbs Resources, verb and status codes!! GET, POST, PUT, DELETE... 200 OK, 304 Not Modified, 404 Not found… RESTish! Uniform interface
  • 20.
    HTTP 101 GET -safe! POST - danger!! PUT - idempotent! ! Note! GET will not modify the resource, but other clients might! Interleaving other requests might break idempotency
  • 21.
    GET /api/games/123 <-- 200OK <-- { "state": "waiting"} POST /api/games/123 --> { "move": "rock"} <-- 200 OK <-- { "state": "won", "winner": "player1", "loser": “player2"} Level 2 - Example
  • 22.
    GET /api/games/123 <-- 200OK <-- { "state": "waiting"} POST /api/games/123 --> Content-Type: application/json --> { "move": "rock"} <-- 200 OK <-- { "state": "won", "winner": "player1", "loser": "player2"} Level 2 - Mediatype json
  • 23.
    Level 2 -Mediatype form GET /api/games/123 <-- 200 OK <-- { "state": "waiting"} POST /api/games/123 --> Content-Type: application/x-www-form-urlencoded --> move=rock <-- 200 OK <-- { "state": "won", "winner": "player1", "loser": "player2"} $ curl -d move=rock http://rps.com/api/games/123
  • 24.
    Level 2 -In the wild Verbs: GET, DELETE, PUT! Resources: buckets, objects, acls, policies…! Why?! CRUD operations! GET allows caching
  • 25.
    Level 2 -Analysis Using HTTP, not fighting it! Problems! Client must construct URLs! Client must contain business rules
  • 26.
    GET /api/games <-- 200OK <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "items": [{"id": "111", "players": ["jan", "cecilia"]}, {"id": "222", "players": ["jan", "mike"]}, {"id": "333", "players": ["cecilia", "mike"]}, ...]} Level 2 - Constructing URLs GET /api/games?offset=10 GET /api/games?page=2 How to navigate to page 2?
  • 27.
    GET /api/games <-- 200OK <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "items": [{"id": "111", "players": ["jan", "cecilia"]}, {"id": "222", "players": ["jan", "mike"]}, {"id": "333", "players": ["cecilia", "mike"]}, ...]} Level 2 - Constructing URLs GET /api/games/{game.id} Navigating to game
  • 28.
    Level 2 -Business logic GET /api/games/123 <-- 200 OK <-- { "state": "waiting", "players": ["ply1", "ply2"], "moves": { "ply1" : true} } Should the client display the move selector?
  • 29.
    Level 2 -Business logic solved? GET /api/games/123 <-- 200 OK <-- { "state": "waiting", "players": ["ply1", "ply2"], "moves": { "ply1" : true}, "shouldMove": true }
  • 30.
    Level 3 -Hypermedia Controls Links & forms! REST as defined by Roy Fielding Hypermedia is defined by the presence of application control information embedded within, ! or as a layer above, the presentation of information The simultaneous presentation of information and controls such that the information becomes the affordance through which the user obtains choices and selects actions.
  • 31.
    GET /api/games <-- 200OK <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "next": "/api/games?offset=10", "items": [{"href": "/api/games/111", "players": ["jan", "cecilia"]}, {"href": "/api/games/222", "players": ["jan", "mike"]}, {"href": "/api/games/333" "players": ["cecilia", "mike"]}, ...]} Level 3 - Example (links)
  • 32.
    Level 3 -Example (form) <html> <body> <ol id="players"> <li>Jan</li> <li>Cecilia</li> </ol> <form action="/api/games/111" method="POST"> <select name="move"> <option value="rock">Rock</option> <option value="paper">Paper</option> <option value="scissors">Scissors</option> </select> <input type="submit"/> </form> </body> </html>
  • 33.
    Level 3 -Opportunity: Extra moves <select name="move"> <option value="rock">Rock</option> <option value="paper">Paper</option> <option value="scissors">Scissors</option> <option value="lizard">Lizard</option> <option value="spock">Spock</option> </select>
  • 34.
    Level 3 -Changing the rules For one of the players: Flip a coin! head - he must play rock! tail - he can play whatever he wants! ! How can the other player take advantage of this? http://blog.gtorangebuilder.com/2014/04/gto-brain-teaser-1-exploitation-and.html <select name="move"> <option value="rock">Rock</option> </select>
  • 35.
    Level 3 -In the wild
  • 36.
  • 37.
    Media types Format! application/json, text/xml,text/plain, text/csv! Domain specific! text/vcard, text/calendar, application/calendar+json! Hypermedia! text/html, application/xhtml+xml! application/vnd.collection+json, application/vnd.hal+json! application/vnd.siren+json, application/vnd.amundsen-uber+json
  • 38.
    GET /api/games <-- 200OK <-- Content-Type: application/json <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "next": "/api/games?offset=10", ... } json
  • 39.
    xhtml GET /api/games <-- 200OK <-- Content-Type: application/xhtml+xml <-- <html> <body> <a href="/api/games?offset=10">Next</a> <div id="totalCount">23</div> <ol id="games"> <li>...</li> <li>...</li> </ol> </body> </html>
  • 40.
    collection+json CRUD for collectionsof objects! Parts of the document:! items! links! queries! templates! errors
  • 41.
    GET /api/games <-- 200OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games", "links" : [ {"rel" : "next", "href" : "/api/games?offset=10"} ], "items" : [ { ... }, { ... } ] } } collection+json
  • 42.
    Anatomy of aLink target url (href)! human readable string (title)! semantic information (rel)! returned media types (type)! ! http method (method)! secondary key (name)! support media types for requests (accept) RFC 5988
  • 43.
  • 44.
    Custom rels Register withIANA :-)! Just make up your own :-)! move! URI! http://rps.com/rels/move! CURIE! rps:move! http://rps.com/rels/{rel}
  • 45.
    Forms / templates Morethan links! Allow user input! May provide! Default values! Data types! Possible values
  • 46.
    html form <html> <body> <ol id="players"> <li>Jan</li> <li>Cecilia</li> </ol> <formaction="/api/games/111" method=“POST" name="move"> <select name="move"> <option value="rock">Rock</option> <option value="paper">Paper</option> <option value="scissors">Scissors</option> </select> <input type="submit"/> </form> </body> </html>
  • 47.
    GET /api/games/111 <-- 200OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games/111", "template" : { "data" : [{ "name" : "move", "value" : "", "prompt" : "Your move" }] } } } collection+json template for writes
  • 48.
    Forced resource structure Collection ofgames /api/games/ Game 111 /api/games/111 Collection of moves for game 111 /api/games/111/moves Player 1 move for game 111 /api/games/111/moves/ply1 item rps:moves item
  • 49.
    GET /api/games/111 <-- 200OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games/111", "links" : [ {"rel" : "rps:moves", "href" : "/api/games/111/moves"} ], ... } } The game links to moves
  • 50.
    GET /api/games/111/moves <-- 200OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games/111/moves", "template" : { "data" : { "name" : "move", "value" : "", "prompt" : "Your move" } } } } moves collection contain template
  • 51.
    URI templates Constructing paths
 
 Form-style! Makinga move
 
 Creating a new game RFC 6570 "http://rps.com/api/games/111{?move}" "http://rps.com/api/games/{gameId}" "http://rps.com/api/games{?player1,player2}"
  • 52.
    application/vnd.hal+json Minimalistic (plain oldjson)! "_links" with mandatory rels! "_embedded" (recursive!)
  • 53.
  • 54.
    GET http://rps.com/api/games/123 <-- { ! ! ! ! ! ! ! ! "_links":{ "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } "createdAt" : "2014-05-22T16:24:01", "state" : "waiting" } hal+json
  • 55.
    GET http://rps.com/api/games/123 <-- {"_embedded": { "rps:player" : [{ "_links": { "self": {"href" : "http://rps.com/api/players/111"}, "name": "Jan" }, { "_links": { "self": {"href" : "http://rps.com/api/players/222"}, "name": "Cecilia" }] }, "_links": { "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } "createdAt" : "2014-05-22T16:24:01", "state" : "waiting" } hal+json
  • 56.
    Application state, notobjects GET http://rps.com/api/players/111/games/123 <-- { "_links": { "rps:game": {"href" : "http://rps.com/api/games/123"}, "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } }
  • 57.
    GET http://rps.com/api/players/111/games/123 <-- {"_embedded": { "rps:game" : [{ "_links": { "self": {"href" : "http://rps.com/api/games/123"}, "createdAt" : "2014-05-22T16:24:01", "state" : "waiting" }] }, "_links": { "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } } Application state, not objects
  • 58.
    Summary Hypermedia simplifies clientdevelopment! No constructing of URLs! Opportunities for less business logic! Pick a mediatype with reasonable semantics! Consider form-encoded when POSTing! Some useful tools! CURIE, URITemplates