GitHub - logandk/restful_mapper: ORM for consuming RESTful APIs in C++ (original) (raw)

restful_mapper

ORM for consuming RESTful APIs in C++

Build status

Introduction

restful_mapper connects business objects and Representational State Transfer (REST) web services. It implements object-relational mapping for REST web services to provide transparent proxying capabilities between a client (using restful_mapper) and a RESTful web service (that follows the conventions outlined below).

The ideas and design philosophy are directly adopted from Active Resource. However, the API and functionality differ in some areas, where it makes sense.

Design goals

RESTful web service conventions

RESTful web services come in many different forms, which are not all suitable for an ORM-style mapper such as restful_mapper. In order to be compatible with as many web services as possible, restful_mapper complies with the current best-practice for creating RESTful web services with JSON.

Specifically, restful_mapper is modelled against the Flask-Restlesslibrary, which provides full-featured RESTful web services that follow best- practice design methods.

The following basic conventions must be followed by the web service:

For exact details on expected request and response formats, see Format of requests and responses.

Building

restful_mapper is built using CMake.

Dependencies

The following libraries are required to build restful_mapper.

Invoking the following command will download and build these libraries.

On UNIX platforms, libcurl and libiconv are typically present on the system, and will not be built by the make command. If they are not present, they should be installed using the system package manager.

Library

After building the dependencies, invoke the following command to build restful_mapper.

This will install restful_mapper as a static library in the lib folder.

Tests

The test suite can be built and run using the following command.

Usage

API configuration

Before making any requests to the web service, it must be configured using the following methods.

The root URL of the web service is specified using the set_url method:

Api::set_url("http://localhost:5000/api");

If the web service requires authentication, provide the username and password:

Api::set_username("admin"); Api::set_password("test");

If you are using a proxy server, it can be specified through the HTTP_PROXYenvironment variable or using the set_proxy method:

Api::set_proxy("http://myproxy");

Mapper configuration

This example illustrates a complete object mapping:

using namespace std; using namespace restful_mapper;

class User; class Alert;

class Todo : public Model { public: Primary id; Field task; Field priority; Field time; Field completed; Field completed_on; Foreign user_id; BelongsTo user; HasOne alert;

virtual void map_set(Mapper &mapper) const { mapper.set("id", id); mapper.set("task", task); mapper.set("priority", priority); mapper.set("time", time); mapper.set("completed", completed); mapper.set("completed_on", completed_on); mapper.set("user_id", user_id); mapper.set("user", user); mapper.set("alert", alert); }

virtual void map_get(const Mapper &mapper) { mapper.get("id", id); mapper.get("task", task); mapper.get("priority", priority); mapper.get("time", time); mapper.get("completed", completed); mapper.get("completed_on", completed_on); mapper.get("user_id", user_id); mapper.get("user", user); mapper.get("alert", alert); }

virtual std::string endpoint() const { return "/todo"; }

virtual const Primary &primary() const { return id; } };

class User: public Model { public: Primary id; HasMany todos; ... };

An API entity is declared by creating a class that inherits from and follows the interface defined in restful_mapper::Model.

Each entity can hold a number of fields and relationships:

The interface specified the following methods, which must be overriden:

Working with objects

Using the models defined above, the following operations are made available byrestful_mapper.

Requesting data

// Find a single item by id Todo t = Todo::find(2);

// Outputting fields cout << t.task.get();

// Explicit conversions cout << (string) t.task;

// Reload data from server t.reload();

// Get all items in collection Todo::Collection todos = Todo::find_all();

// Find an item in the collection by id todos.find(4);

// Find all items in collection where task is "Do something" todos.find("task", "Do something");

// Find the first item in collection that has been completed todos.find_first("completed", true);

Saving data

// Create a new item Todo new_todo; new_todo.task = "Use restful_mapper"; new_todo.save();

// Update an existing item Todo old_todo = Todo::find(2); old_todo.completed = true; old_todo.save();

// Deleting an item old_todo.destroy();

// Create a clone with no id set (i.e. a new database object) Todo todo_clone = old_todo.clone(); todo_clone.save();

Relationships

// Find an item including related items User u = User::find(1);

// Get a related todo u.todos[2].task = "Do something else";

// Delete last item u.todos.pop_back();

// Add a new related item Todo new_todo; new_todo.task = "Use restful_mapper"; u.todos.push_back(new_todo);

// Save user including all changes to related todos - will delete one, update one and add one todo u.save();

// Objects in one-to-one and many-to-one relationships are managed pointers Todo t = Todo::find(2); cout << t.user->email.get();

Querying

Supports the query operations specified by Flask-Restless.

// Query a single item Query q; q("task").like("Do someth%"); q("completed").eq(true);

Todo todo = Todo::find(q);

// Query a collection of items Query q; q("time").gt("1.45").lt("3.0"); q.order_by_asc(q.field("priority"));

Todo::Collection todos = Todo::find_all(q);

Exceptions

Some API errors are caught using custom exceptions.

Contributing

Pull requests on GitHub are very welcome! Please try to follow these simple rules:

License

This code is copyright 2013 Logan Raarup, and is released under the revised BSD License.

For more information, see LICENSE.