torsdag, februar 20, 2014

Representing an issue tracker with Mason

Two weeks ago I introduced Mason - a media type for representing data with embedded hypermedia elements. Now I would like to go through an example implementation of a fictive issue tracker which uses Mason to represent its data.

At its core the issue tracker has, not surprisingly, "issues" that represent issues that needs to be solved. Issues are organized in projects and may have one or more file attachments associated with them. Issues are represented by their title, a description and a severity level (from 1 to 5).

It would be natural to include comments on issues too but I want to keep the domain as simple as possible while still being able to illustrate all the features of Mason - and comments would not add anything but clutter as they are very similar to attachments.

Please note that the issue tracker will be defined without reference to any existing implementation thus making it a "real" REST service totally independent of any specific implementation. I do have a reference implementation available but that is only used as a proof of concept - it is not part of the issue tracker definition itself.

The issue tracker is defined in terms of data types, links, link templates and actions as described in the next sections. That is not exactly interesting reading so you might want to skip it and jump to the later sections with examples of how to actually use the issue tracker.

You should try the demo issue tracker yourself with the generic Mason browser in order to see how actions and URL templates are supposed to work.

Data types, links and actions

CURIE definitions

In the following the CURIE name "is" should be expanded to "http://soabits.dk/mason/issue-tracker/reltypes.html#". So for instance "is:add-issue" becomes "http://soabits.dk/mason/issue-tracker/reltypes.html#add-issue" (you can GET that).

These URL relationship types ensures that we have no name collisions with other relationship types. In addition to this it is actually possible to dereference the URLs and GET some documentation about them.

Data types


Issue

A single issue consists of the following properties:

  Id: int
  Title: string
  Description: string
  Severity: integer

Expected links:
  • self
  • up: link to parent project
  • is:attachments: link to collection of attachments for issue
  • is:common: link to common resource data

Example resource: http://mason-issue-tracker.cbrain.net/issues/1

Issue collection

A collection of issues has one top level property:

  Issues: array of
  - Id: int
  - Title: string

Expected links:
  • self
  • up: link to parent project
  • is:common: link to common resource data
Example resource: http://mason-issue-tracker.cbrain.net/projects/1/issues

Project

A single project consists of the following properties:

  Id: int
  Code: string
  Title: string
  Description: string

Expected links:
  • self
  • is:issues: link to collection of issues for project
  • is:common: link to common resource data

Expected actions:
  • is:project-update
  • is:add-issue
  • is:project-delete
Example resource: http://mason-issue-tracker.cbrain.net/projects/1

Project collection

A collection of projects has one top level property:

  Projects: array of
  - Id: int
  - Code: string (a short code or abbreviation of the project name)
  - Title: string

Expected links:
  • self
  • is:common: link to common resource data

Example resource: http://mason-issue-tracker.cbrain.net/projects

Common resource data

A set of properties and links which are common to all resources:

  Title: string (title of the whole issue tracker)
  Description: string (description of the whole issue tracker)

Expected links:
  • self
  • is:contact: link to contact information
  • is:logo: link to issue tracker logo
  • is:projects: link to collection of all projects
  • is:common: link to common resource data

Expected link templates:
  • is:issue-query

Expected actions:
  • is:project-create

Example resource: http://mason-issue-tracker.cbrain.net/resource-common

Contact information

Contact information (related to the owner of the issue tracker) consists of:

  Name: string
  Address1: string
  Address2: string
  PostalCode: string
  City: string
  EMail: string
  Phone: string
  Country: string

Expected links:
  • self
  • alternate (alternate address representations in other formats like for instance text/vcard)
  • is:common
Example resource: http://mason-issue-tracker.cbrain.net/contact (take a close look at the "alternate" link as it shows how to have multiple targets for one link).

Link relations


is:projects

Link to collection of all projects.

is:issues

Link to collection of issues for a given project.

is:attachments

Link to collection of attachments for a given issue.

is:contact

Link to contact information.

is:logo

Link to issue tracker logo.

is:common

Link to data common for all resources in the issue tracker.

Link templates

 

is:issue-query

A link template for querying issues. Parameters:

  text: any text to look for in issues.
  severity: severity level (1-5)
  pid: project ID.

Example usage: http://mason-issue-tracker.cbrain.net/resource-common

Actions


is:project-create

Action for creating a new project. Arguments:

  Code: string
  Title: string
  Description: string

Example usage: http://mason-issue-tracker.cbrain.net/resource-common

is:project-update

Action for updating a single project. Arguments:

  Code: string
  Title: string
  Description: string

Example usage: http://mason-issue-tracker.cbrain.net/projects/1

is:project-delete

Action for deleting a project. Has no arguments.

Example usage: http://mason-issue-tracker.cbrain.net/projects/1

is:add-issue

Action for adding a new issue to a project. Arguments:

  Title: string
  Description: string
  Severity: int
  Attachment: object of
  - Title: string
  - Description: string

In addition to this it is possible to pass a file as an attachment to the issue. The file name is "attachment".

The "Attachment" object contains additional information about the attached file.

Example usage: http://mason-issue-tracker.cbrain.net/projects/1

is:issue-update

Action for updating a single issue. Arguments:

  Title: string
  Description: string
  Severity: int

Example usage: http://mason-issue-tracker.cbrain.net/issues/1

is:issue-delete

Action for deleting a single issue. Has no arguments.

Example usage: http://mason-issue-tracker.cbrain.net/issues/1

is:add-attachment

Action for adding an attachment to an issue. Arguments:

  Title: string
  Description: string

In addition to this it is possible to pass a file as the actual attachment. The file name is "attachment".

Example usage: http://mason-issue-tracker.cbrain.net/issues/1

Examples


Getting started

The first thing a client must do in order to work with the issue tracker is to GET the "common" resource that contains useful links, templates and actions for the issue tracker. The common resource can be thought of as the "home page" or "landing page" of the issue tracker.

Try it yourself: GET http://mason-issue-tracker.cbrain.net/resource-common

Creating a new project

Once we have a copy of the "common" resource we can look for the action "is:project-create". That action will tell us how to encode the project data and how to submit it to the server.

Here is an example:

"@actions": {
  "is:project-create": {
    "type": "json",
    "href": "http://mason-issue-tracker.cbrain.net/projects",
    "title": "Create new project",
    "schemaUrl": "http://mason-issue-tracker.cbrain.net/schemas/create-project"
  }
}


The most important parts are the "type" and "href" properties which tells us how to encode the data and where to send it. Mason actions also have a "method" property for identifying the HTTP method to use but it defaults to POST so its not always needed.

The type "json" tells us to encode the action arguments in plain JSON. The net result is a request like this:

POST /projects HTTP/1.1
Accept: application/vnd.mason+json
Content-Type: application/json

{
  "Code":    "SHOP",
  "Title":    "Webshop",
  "Description":    "Project for issues related to the webshop"
}


The response is a redirect to the created project:

HTTP/1.1 201 Created
Location: http://mason-issue-tracker.cbrain.net/projects/2


Adding a new issue

Now that we have a project we can start adding issues to it (with optional attachments). Each project representation contains an "is:add-issue" action for this purpose as can be seen here:

"@actions": {
  "is:add-issue": {
    "type": "json+files",
    "href": "http://mason-issue-tracker.cbrain.net/projects/2/issues",
    "title": "Add new issue to project",
    "schemaUrl": "http://mason-issue-tracker.cbrain.net/schemas/create-issue",
    "jsonFile": "args",
    "files": [
        {
            "name": "attachment",
            "description": "Attachment for issue"
        }
    ]
  }
}


This action tells us the following:
  • The type is "json+files" which means we must send the JSON data together with some files wrapped in the media type multipart/form-data.
  • The target URL is "http://mason-issue-tracker.cbrain.net/projects/2/issues".
  • The JSON data must conform to the schema definition at "http://mason-issue-tracker.cbrain.net/schemas/create-issue".
  • The JSON data must be contained in a multipart element named "args".
  • The attached file must be contained in a multipart element named "attachment".
The net result is a request like this:

POST /projects/2/issues HTTP/1.1
Accept: application/vnd.mason+json
Content-Type: multipart/form-data; boundary=d636dfda-b79f-4f29-aaf6-4b6687baebeb

--d636dfda-b79f-4f29-aaf6-4b6687baebeb
Content-Disposition: form-data; name="attachment"; filename="hogweed.jpg"
... binary data for attached image ...

--d636dfda-b79f-4f29-aaf6-4b6687baebeb
Content-Disposition: form-data; name="args"; filename="args"
Content-Type: application/json

{
  "Title":    "Hogweeds on the plaza",
  "Description":    "Could you please remove the hogweeds growing at the plaza?",
  "Severity":    5,
  "Attachment":
    {
      "Title":        "Hogweed",
      "Description":        "Photo of the hogweeds."
    }
}


Updating project details

Each project has a "is:project-update" action for updating the project details:

"@actions": {
  "is:project-update": {
    "type": "json",
    "href": "http://mason-issue-tracker.cbrain.net/projects/1",
    "title": "Update project details",
    "template": {
      "Code": "SHOP",
      "Title": "Webshop",
      "Description": "All issues related to the webshop."
    }
  }
}


This action tells us to encode project arguments in JSON and POST it to "http://mason-issue-tracker.cbrain.net/projects/1". The JSON data should be build from the JSON template in the action.

Request:

POST /projects/1 HTTP/1.1
User-Agent: API Explorer
Accept: application/vnd.mason+json
Content-Type: application/json

{
  "Code": "SHOP",
  "Title": "Web shop",
  "Description": "All issues related to the new web shop."
}


Deleting a project

Each project has a "is:project-delete" action for deleting the project and its related issues:

"@actions": {
  "is:project-delete": {
    "type": "void",
    "href": "http://mason-issue-tracker.cbrain.net/projects/1",
    "method": "DELETE",
    "title": "Delete project"
}


Request:

DELETE /projects/1 HTTP/1.1
Accept: application/vnd.mason+json


Response:

HTTP/1.1 204 No Content

Searching for issues

The "common" resource has a link template for issue queries:

"@link-templates": {
  "is:issue-query": {
    "template": "http://mason-issue-tracker.cbrain.net//issues-query?text={text}&severity={severity}&project={pid}",
    "title": "Search for issues",
    "description": "This is a simple search that do not check attachments.",
    "parameters": [
      {
        "name": "text",
        "description": "Substring search for text in title and description"
      },
      {
        "name": "severity",
        "description": "Issue severity (exact value, 1..5)"
      },
      {
        "name": "pid",
        "description": "Project ID"
      }
    ]
  }
}


This templates tells us to replace the parameters "text", "severity" and "pid" into the URL template "http://mason-issue-tracker.cbrain.net//issues-query?text={text}&severity={severity}&project={pid}".

Should we for instance want to query for issues of severity 5 in project 1 then we would get this request:

GET /issues-query?text=&severity=5&project=1 HTTP/1.1
Accept: application/vnd.mason+json


The result is a collection of issues.


Ingen kommentarer:

Send en kommentar