Introduction

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.

Hypermedia API

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+JSON

What's Collection+JSON?. It's a JSON-based read/write hypermedia type desinged to suport the mangement and querying of simple collections. It define the format and the semantics in a single document.The media type is designed to support full read/write for collections. The application semantics include Create, Read, Update, and Delete, it also has support for queries, using query templates. Write operations are defined using a template object send by the server.
As a simple introduction, a sample collection

            
            {
              "collection": {
                  "version":"1.0",
                  "href":URI,
                  "links":[ARRAY],
                  "items":[ARRAY],
                  "queries":[ARRAY],
                  "template":{OBJECT},
                  "error":{OBJECT}      
              }
            }
            
            
Our API uses some Collection+JSON extensions:
  1. Attachment
  2. Lookup
  3. Value Types
  4. Metadata

Before to start using CJ we will describe what our API needs to do.

Business Process

Here we define what our API need to do, workflows and what are the paths one need to follow to accomplish a particular tasks.
  • Register new users.
  • Anonymous users can browser the API/Web, retrieve public information, but they can’t execute secured actions.
  • Secured operations will require users permissions and authorization, but not every user will be able to see everything.
Bellow we describe the possible states that need to be represented (not all of them, check the state machine below)
  1. A User
  2. A User profile
  3. A Problem Report
  4. A List of Problem Report
  5. A Report connected with a report interaction/ report interactions
  6. A query by report number connected with a Report Problem
  7. A query by (synopsis | description) connected with a Report/Reports.
  8. Filters by Report properties connected with a Report/Reports
Use Cases: state transitions.
  1. A logged-in user will see the reports he has submitted
  2. A logged-in user will see the private reports if he has authorization.
  3. A logged-in user will be able to submit a new problem report
  4. A logged-in user will be able to view and edit an existing problem report if he has authorization.
  5. A logged-in user will be able to submit a new comment (interaction report)
  6. A logged-in user will be able to submit a new comment (interaction report) to private reports if he has authorization.
  7. A logged-in user admin will see the all reports (public and privates).
  8. A logged-in user admin will be able to submit a new comment (interaction report) to (public or private)
  9. A logged-in user admin will be able to view and edit an existing problem
  10. Anonymous User will only see public reports
  11. Anonymous User will see a given public report
  12. Anonymous User will see, interactions reports for a given public report.
  13. Anonymous User will be able to search for reports filtering by number
  14. Anonymous User will be able to search for reports filtering by synopsis, description
  15. Anonymous User will be able to filter reports by category, severity, priority, status, etc
  16. A logged-in user will be able to subscribe to categories
  17. A logged-in user will be able to unsubscribe to categories
  18. A logged-in user will be able to see the reports he has submitted.
  19. A logged-in user will be able to see the reports he has commented.

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.


ESA API state machine

Using the CJ ESA API

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/

Try it »

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"

Try it »

        
     {
  "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.

References

  1. CollectionJSON Documentation
  2. CollectionJSON Exaples
  3. CollectionJSON forum
  4. CollectionJSON Design
  5. Attachment
  6. Lookup
  7. Value Types
  8. Metadata
  9. Hypermedia Resources