Eiffel Support site is a Hypermedia API supporting two media types (HTML5 and Collection+JSON). Internally the API is known as ESA. So, since here we will refer the Eiffel Support Site API as ESA.
What does it means Hypermedia API?. Basically the use of HTTP to its fullest and the responses will be served as hypermedia that manages the application state.
A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types. Any effort spent describing what methods to use on what URIs of interest should be entirely defined within the scope of the processing rules for a media type (and, in most cases, already defined by existing media types).
Roy Fielding REST APIs Must ne Hypertext Driven
Hypermedia Types are MIME media types that contain native hyper-linking semantics that induce application flow. For example, HTML is a hypermedia type; XML is not.
Mike Amundsen (2010)
{
"collection": {
"version":"1.0",
"href":URI,
"links":[ARRAY],
"items":[ARRAY],
"queries":[ARRAY],
"template":{OBJECT},
"error":{OBJECT}
}
}
Our API uses some Collection+JSON extensions:
Before to start using CJ we will describe what our API needs to do.
So, to formalize in which states our application can be, we will describe a state machine, and we will have an API root in our case HOME, and that's all we need to know to browse and use the API.
Conventions used in the diagram
Safe:are HTTP methods that do not modify resources.
Idempotent: An idempotent HTTP method is a HTTP method that can be called many times without different outcomes.
Unsafe: not Safe, i.e may modify resources.
A generic Collection+JSON client is a software that understands the Collection+JSON media type, so it's a useful tool to inspect collections while developing or debugging an application. Unfortunatelly CJ does not have an online web service tool to use. So for development purposes we have developed an stand-alone client forCJ client and we have a version supporting the extensions already mention.
As we are using CollectionJSON, this means that you can use any generic CJ client with ESA API. If you don't have one handy, it's easy!. You can use cURL or some online services
to make an HTTP request, like Hurl.eu As we have some extentions, a generic client will not handle all the cases.
Making a request to the ROOT uri:
$ curl -i -H "Accept: application/vnd.collection+json" https://www2.eiffel.com/beta/
You'll get one top-level item: a collection object. The collection has a queries array which represents queries you can make and links which represents the available next steps that you can follow. Here's a full response, with the details filled in:
{
"collection": {
"version": "1.0",
"href": "https://www2.eiffel.com/beta/",
"links": [
{
"href": "https://www2.eiffel.com/beta/profile/esa_api.xml",
"rel": "profile"
},
{
"href": "https://www2.eiffel.com/beta/reports",
"rel": "all",
"prompt": "Reports"
},
{
"href": "https://www2.eiffel.com/beta/login",
"rel": "login",
"prompt": "Login"
},
{
"href": "https://www2.eiffel.com/beta/register",
"rel": "register",
"prompt": "Register"
},
{
"href": "https://www2.eiffel.com/beta/reminder",
"rel": "reminder",
"prompt": "Recover Username/Password"
}
],
"queries": [
{
"href": "https://www2.eiffel.com/beta/report_detail/",
"rel": "search",
"prompt": "Search by Report #...",
"data": [
{
"name": "search",
"value": ""
}
]
}
]
}
}
Our collection-home does not have items which will hold the data in the list. The links arrays hold a collection level URIs, will allow us browse the API. The queries array has one object inside. This object has four keys: rel, href, prompt and data. rel is the most important: it's the 'link relation' name of the query. In this case, we have a search relation. We'll use the rel in a moment. href and data are used to construct the query itself. What you do is this: you take the URL located in href, and you append a query string made by joining the name elements of all of the objects in data as keys, and the values you want as values. For example you can try to search the report number 1, then make an HTTP GET request to the following URL https://localhost/report_detail/?search=1, and you'll get another response back. Let's make that request:
$ curl -i --insecure -H "Accept: application/vnd.collection+json" "https://www2.eiffel.com/beta/report_detail/?search=18902"
{
"collection": {
"version": "1.0",
"href": "https://www2.eiffel.com/beta/report_detail/18902",
"links": [
{
"href": "https://www2.eiffel.com/beta",
"rel": "home",
"prompt": "Home"
},
{
"href": "https://www2.eiffel.com/beta/reports",
"rel": "all",
"prompt": "Reports"
},
{
"href": "https://www2.eiffel.com/beta/profile/esa_api.xml",
"rel": "profile"
},
{
"href": "https://www2.eiffel.com/beta/login",
"rel": "login",
"prompt": "Login"
},
{
"href": "https://www2.eiffel.com/beta/register",
"rel": "register",
"prompt": "Register"
}
],
"items": [
{
"href": "https://www2.eiffel.com/beta/report_detail/18902",
"data": [
{
"name": "Group",
"prompt": "group",
"value": "Description"
},
{
"name": "Submitter",
"prompt": "submitter",
"value": "jinnydev"
},
{
"name": "Category",
"prompt": "category",
"value": "EiffelStudio"
},
{
"name": "Priority",
"prompt": "priority",
"value": "Medium"
},
{
"name": "Category",
"prompt": "category",
"value": ""
},
{
"name": "Date",
"prompt": "date",
"value": "06/26/2014 8:10:26.000 AM"
},
{
"name": "Class",
"prompt": "class",
"value": "Bug"
},
{
"name": "Severity",
"prompt": "severity",
"value": "Serious"
},
{
"name": "Number",
"prompt": "number",
"value": "18902"
},
{
"name": "Release",
"prompt": "release",
"value": "13.11.9.3542"
},
{
"name": "Confidential",
"prompt": "confidential",
"value": "False"
},
{
"name": "Status",
"prompt": "status",
"value": "Open"
},
{
"name": "Responsible",
"prompt": "responsible",
"value": ""
},
{
"name": "Environment",
"prompt": "environment",
"value": "EiffelStudio 13.11.9.3542 Enterprise Edition - windows"
},
{
"name": "Synopsys",
"prompt": "synopsis",
"value": "Floating point exception in {EV_GRID_I}.recompute_vertical_scroll_bar in EiffelStudio."
},
{
"name": "Description",
"prompt": "description",
"value": "Again -- this is a resulting of:\n\n1. Organize Desc alpha sort on \"Declared in class\" column of the Class tool in the \"Routines\" view.\n2. Once sorted, start collapsing the various classes until fully collapsed.\n3. Finally -- start expanding and collapsing the items one by one.\n4. BOOM! Breaks at some point while (apparently) attempting to recalc the scrollbar.\n\n-- Larry Rix\n\nProject loaded: True\nProject compiled: True\nIs compiling: False\nLast known class processed: DATA_EXTRACTION_TEST_SET\n\n******************************** Thread exception *****************************\nIn thread Root thread 0x0 (thread id)\n*******************************************************************************\n-------------------------------------------------------------------------------\nClass / Object Routine Nature of exception Effect\n-------------------------------------------------------------------------------\nEV_GRID_IMP recompute_vertical_scroll_bar @20\n Integer division by Zero:\n{000000000CE8FB8C} (From EV_GRID_I) Floating point exception. Fail\n-------------------------------------------------------------------------------\nEV_GRID_IMP recompute_vertical_scroll_bar @20\n{000000000CE8FB8C} (From EV_GRID_I) Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_GRID_IMP perform_vertical_computation @6\n{000000000CE8FB8C} (From EV_GRID_I) Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_GRID_IMP visible_row_count @2 \n{000000000CE8FB8C} (From EV_GRID_I) Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_GRID_IMP last_first_row_in_per_item_scrolling @6\n{000000000CE8FB8C} (From EV_GRID_I) Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_GRID_IMP recompute_vertical_scroll_bar @16\n{000000000CE8FB8C} (From EV_GRID_I) Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_GRID_IMP recompute_vertical_scroll_bar_from_once_idle_actions @2\n{000000000CE8FB8C} (From EV_GRID_I) Routine failure. Fail\n-------------------------------------------------------------------------------\nPROCEDURE call @4 \n{0000000008B42A18} Routine failure. Fail\n-------------------------------------------------------------------------------\nPROCEDURE apply @1 \n{0000000008B42A18} Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_APPLICATION_IMP call_separate_action @1\n{0000000009698B1C} (From EV_APPLICATION_I)\n Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_APPLICATION_IMP process_event_queue @48\n{0000000009698B1C} (From EV_APPLICATION_I)\n Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_APPLICATION_HANDLER\n process_application_event_queue @1\n{0000000009699458} Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_APPLICATION_HANDLER\n launch @4 \n{0000000009699458} Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_APPLICATION internal_launch_application @3\n{0000000009698D2C} Routine failure. Fail\n-------------------------------------------------------------------------------\nEV_APPLICATION launch @2 \n{0000000009698D2C} Routine failure. Fail\n-------------------------------------------------------------------------------\nES_GRAPHIC make @12 \n{0000000009698D14} Routine failure. Fail\n-------------------------------------------------------------------------------\nEB_KERNEL make @4 \n{0000000009583220} Routine failure. Fail\n-------------------------------------------------------------------------------\nEB_KERNEL root's creation \n{0000000009583220} Routine failure. Exit\n-------------------------------------------------------------------------------\n"
},
{
"name": "To reproduce",
"prompt": "to reproduce",
"value": ""
}
],
"links": [
]
}
]
}
}
What's new is that we now have an items array. This items array contain an object, with a data array inside of it. This data array have objects with a name, a value and prompt. The name is a descriptive explanation, in this case, that we're showing different report properties, the prompt is the a value to show in a GUI, and the actual value.