RESTful Integration: Part Two

In our last episode, we added a custom media type to our service.
This time around we want to add links to our schema to form a basic API.
The links will be used to tell the client what the next steps in the
workflow should be. Along with the links, we will also be adding basic
support to Rails for the HTTP OPTIONS verb.

First thing we will do is add a link field to our model. There is not a
good reason to keep links in the database, so we will add them directly
to our model.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Quotation < ActiveRecord::Base
def link
read_attribute("link")
end

def link=(uri)
write_attribute("link", uri)
end

def after_initialize
write_attribute("link", "http://localhost:3000/quotations/" + read_attribute("id").to_s)
end
end

Next we will add the link field to the new method in our quotations
controller.

1
2
3
4
5
6
7
8
9
format.rogue {
quote = params[:quotation]
@quotation.customer = quote[:customer]
@quotation.quote_amount = quote[:quote_amount]
@quotation.status = quote[:status]
@quotation.save
@quotation.link = "http://localhost:3000/quotations/" + @quotation.id.to_s
render :xml => @quotation, :status => :created
}

This is the same basic code as last time with two additions. The first
is the line that sets the link value after the quotation object has been
stored in the database. The second is the addition of the “:status =>
:created” parameter to the render method. This instructs Rails to return
the HTTP code 201 (which means resource created) instead of the standard
code 200. (Which means OK) Now when we post a quotation to our service,
it will respond with the 201 code, which tells us the resource was
successfully created, and a link to our newly created resource. If this
were a real service, we would include a link to the next step in our
workflow/process.

Now that we have a resource, it might be nice to have a way to find out
what we can do with it. For that we need add support for the HTTP
OPTIONS verb to our service. We will start by adding an “options” method
to our controller. The basic idea is while the quotation has a status of
“New”, we will permit the client to update and delete the resource. Once
the status changes, we will only permit the client to view the current
state of the resource.

1
2
3
4
5
6
7
8
9
10
11
12
13
def options
@quotation = Quotation.find(params[:id])

respond_to do |format|
format.rogue {
if @quotation.status == "New"
return head(:allow => "GET, PUT, DELETE")
else
return head(:allow => "GET")
end
}
end
end

Now we also need to add a line to config/routes.rb to map the
OPTIONS verb to our new method.

1
map.connect '/quotations/:id', :controller => "quotations", :action => :options, :conditions => { :method => :options }

Once you add the new route, be sure to bounce your Rails application for
it to take effect. Now we can use cURL to query the options on our
resource.

1
/usr/bin/curl -i -X OPTIONS -H "Accept: application/vnd.rogue+xml" -H "Content-Type: application/vnd.rogue+xml" http://localhost:3000/quotations/22

If your resource has a status of “New”, you should see an “Allow: GET,
PUT, DELETE” header value. Otherwise you should only see the “Allow:
GET” header.

That will wrap up this installment. We now have the building blocks for
a basic service interface. The next logical step would be to use these
building blocks to put together a complete process. It would be nice to
take the quotation idea and put together a service that takes quotation
requests, approves them and then turns them into sales orders. Sounds
like something to keep me busy now that the weather is starting to get
colder.