ManageIQ Blog

API Contributor's Guide

The API has been growing quickly thanks to our many contributors. In an effort to help new contributors get up to speed quickly, it was about time for a blog post to explain the process of adding in a new collection and subcollection, as well as provide some examples for commonly asked questions.

The api.yml is where a lot of the magic happens. The best way to explain it is through an example, so let’s walk through adding in a new collection and subcollection.

We’ll keep the example generic through the creation of a Coffee API. Because who doesn’t need more in their life?

Defining a new collection with basic CRUD

The new Coffee collection will need to be defined under collections in the api.yml. Note that all of the collections are in alphabetical order.
The collection will need to return details about the coffee, update them if they need some more sugar, create new ones (Pumpkin, anyone?), and delete any we’re not a fan of - basic CRUD.

Here is a glimpse into what adding the new collection with basic CRUD will look like:

Where are the methods?

Now that the routes exist, let’s take care of the methods. Once the new collection has been added to the api.yml, the corresponding controller needs to be created here. The controller will look something like:

module Api
class CoffeesController < BaseController
end
end

Because the CoffeesController inherits from the BaseController, a lot of what has been defined in the api.yml will already work . The Base Controller holds the action methods (#show, #index, #update, #destroy) that will be called if they are not overridden in the CoffeesController.

Custom Resource Actions

Once basic CRUD is established, what good is seeing a coffee if you can’t order one? A custom resource action is the solution for that. In traditional REST, this would be solved via POST /api/coffee/:c_id/order. We instead use actions, POST /api/coffee/:c_id { "action": "order" }

With the new order action, the :resource_actions: section will look as follows:

The above example demonstrates a couple of the basic methods and formats that you’ll see used throughout the API.

order_resource(type, id, data)

This is the normal method signature for a resource action, <action>_resource. You can see the target method being generated here and being called here.

collection_class(type)

Based off of the type of request (in this case, :coffee), it will return the klass defined in the api.yml (Coffee).

resource_search(id, type, class)

Resource search will return the resource corresponding to the ID that was requested, or return a NotFoundError if it was not found.

Resource search is used as opposed to a simple Coffee.find(id) because the results are filtered through access control, ensuring nobody is working with a resource they don’t have permissions for.

data

The data input is the request body that was passed in with the order request. I like my coffee {"cream":false,"sugar":false}, so that is the request body that would be passed to my order.

Subcollections

Oftentimes, we need subcollections on a resource to return child resources. Coffee is great by itself, but it’s even better with donuts. And it’s your lucky day - every coffee has a set of donuts picked to pair perfectly with it. Sounds like the perfect job for a subcollection.

Although the Donuts could also be a collection (they can still be enjoyed on their own), this example will only outline the subcollection actions for reading and deleting them.

The above configuration is very similar to that of a collection, only it defines :subcollection_actions: and :subresource_actions:. The coffee options will also need to be updated to include the Donuts subcollection:

In the above example, the object passed to the donuts_query_resource will be the Coffee object that the donuts are being queried on (the parent resource to the donuts).

The two method signatures donuts_query_resource(object) and donuts_delete_resource(object, type, id, data) are the normal naming conventions for subcollection actions, <subcollection name>_<action>_resource. Like the collection, you can see the target method being generated here.

Creating a PR

Small PRs are always easier to review, and this example would be broken down into two PRs - the Coffee CRUD PR and a Donuts subcollection PR. Examples in the PR description help reviewers to understand what was added, and also helps the users to get up to speed quickly!
Here is a snippet of what we might include as an example for ordering a coffee:

Questions and Examples

Below you’ll find some common questions and example PRs to guide you through the development process.

Should this be a subcollection?

One question seen frequently is how to determine whether something should be added as a collection, subcollection, or both. Is your resource something that you would not look at outside of the context of its parent object? If so, then a subcollection is likely the way to go.

How are asynchronous tasks handled?

Action results are used when an action creates an asynchronous task, and returns the task information back to the user. For example, the creation and deletion of Flavors

Bulk actions

Action results are also used when performing bulk actions, such as bulk tagging

Sometimes a resource may require additional attributes to be returned by default

Overriding the generic methods

In the event that your CRUD methods need to do something different than what the generic methods do, you can override them

Hopefully this guide was helpful in getting you up to speed on how to contribute to the API. As always, feel free to reach out to us on gitter, and leave feedback so we can write more useful blogs in the future.