API Definition
The first step in developing resources for Cortex is to define them using the API definition language. The API definition is an XML file in your project in which you describe the various resources types, their URIs, advisors, and the relationships between them. From this XML file, a set of prototype classes is generated for you to implement.
For examples of fully implemented APIs and resources for common use cases, see the Helix Pattern Catalog.
Defining Entity Resource
The most basic type of resource you can define is an Entity resource. The definition of an entity resource consists of an entity. An entity describes or provides access to data of some sort, a URI-part, which acts as an identifier for the resource, and the resource itself, which links the URI-part to the entity. You must define all three of these things.
Entity Resources support CREATE
, READ
, UPDATE
, and DELETE
operations. You must implement one of these operations in your generated prototype.
uri-part
Defining a uri-part
is an identifier for a resource’s URI, allowing us to never deal directly with the URI string. For more information, see Resource Identifiers.
To define an identifier part, use the <uri-part/>
element:
<uri-part>
<name>car-id</name>
<description>Identifier for one car</description>
<string/>
</uri-part>
In the schema, you might notice that some <uri-part>
elements are defined with a <composite/>
type, which is a complexType
element. The <composite/>
type can be a key/value pair or a map with multiple key/value pairs.
For example, in the offers.xml file, the <uri-part>
that defines the offer ID is a composite of a product ID and a scope:
<uri-part>
<name>offer-id</name>
<description><![CDATA[The offer identifier. This ID is a unique value across the scope.]]></description>
<composite/>
</uri-part>
The following Java code from the OfferForItemLinksRepositoryImpl.java file builds the offer ID:
private OfferIdentifier buildOfferIdentifier(final ItemIdentifier identifier, final String productGuid) {
return OfferIdentifier.builder()
.withScope(identifier.getScope())
.withOfferId(OfferIdIdentifierPart.of(SearchRepositoryImpl.PRODUCT_GUID_KEY, productGuid))
.build();
}
entity
Defining an Entity resources are used to provide access to some form of data. An entity resource definition consists of and <entity/>
definition and a corresponding <resource/>
definition.
The definition of an entity itself is as follows:
<entity>
<name>car</name>
<description>A car.</description>
<property>
<name>brand</name>
<description>The brand of the car</description>
<string/>
</property>
<property>
<name>model</name>
<description>The model of the car</description>
<string/>
</property>
<property>
<name>year-build</name>
<description>The year the car was build</description>
<integer/>
</property>
</entity>
An entity must have a <name>
, <description>
, and at least one <property>
. The property must have a data type; in the example above, these are defined by the <string/>
and <integer/>
elements.
Cortex provides support for a number of primitive property types, including string
, integer
, long
, boolean
and decimal
. See the XML schema for the full list of supported types.
resource
Defining an Entity The resource of an entity resource links the uri-part
to the entity
, and provides a name which can be used to access both.
To define the Entity resource
for a given entity:
<resource>
<name>car</name>
<description>A car resource.</description>
<uri>/{base.family}/{car-id}</uri>
<entity>car</entity>
</resource>
Extending Entities
You can extend entities by using <is-a/>
. There are two ways of extending an entity: inheritance and composition. You can extend an entity within its same family, or with an outside family.
In the API definition schema, entitles are defined with a <family />
element. Conceptually similar entities. For example different types of cars are defined within the 'cars’ family.
Adding properties to existing Entity
If you want to extend an existing entity by adding fields specific to a subtype, you can use <is-a/>
to add properties to an entity:
<entity>
<name>car-with-country</name>
<description>car entity with country property</description>
<is-a>car</is-a>
<property>
<name>country-built</name>
<description>The country where the car was built</description>
<string/>
</property>
</entity>
Composing New Entity Out of Existing Entity
If you want an entity that contains multiple entities build an entity out of existing entities:
<entity>
<name>composition-same-family</name>
<description>An entity with unvalidated properties</description>
<property>
<name>is-a-property</name>
<description>A property that is a reference to another entity</description>
<is-a>car</is-a>
</property>
<property>
<name>list-of-is-a</name>
<description>A list (array) of other entities</description>
<array>
<is-a>car</is-a>
</array>
</property>
</entity>
Dynamic Entities
A dynamic entity resource is an entity resource which operates on an entity containing dynamic properties. These dynamic properties can be used if the properties to be contained in an entity are not known when modeling the API definition or the entity returned by the entity resource contains different properties depending on the state.
<resource>
<name>truck</name>
<description>A truck resource with a dynamic truck entity.</description>
<uri>/{base.family}/{truck-id}</uri>
<entity>truck</entity>
</resource>
note
Make sure when injecting a dynamic property entity to validate if the property keys you are expecting are actually contained in the dynamic property map.
For more information on dynamic entities, see Dynamic Fields.
Types of Resources
In addition to the basic entity resource, there are a number of specialized resources that provide additional functionality and types of linking between difference resources.
Alias Resource
Alias resources are used to create a redirect to another resource. For example, given the following resource:
<resource>
<name>color</name>
<description>A resource that represents a color</description>
<uri>/{base.family}/{base.scope}/{color-id}</uri>
<entity>color</entity>
</resource>
An alias resource can be defined as follows:
<resource>
<name>default-color</name>
<description>A resource that represents the default color, it is an alias to the color resource</description>
<uri>/{base.family}/{base.scope}/default</uri>
<alias>color</alias>
</resource>
Alias resources only support READ
operations.
List Resources
List resources represent collections or sets of other resources.
To define a list resource:
<resource>
<name>countries</name>
<description>A list of countries resource.</description>
<uri>/{base.family}/{base.scope}</uri>
<list-of>country</list-of>
</resource>
Where the <list-of/>
element is the name of a resource.
List resources support READ
and CREATE
operations.
Paginated List Resource
List resources always return all elements of a collection of other resources. Sometimes collections can contain a lot of elements and returning the whole collection might lead to poor performance.
A paginated list resource returns a subset of the elements of a collection, with a defined number of items of the collection. To achieve this, a paginated list resource divides the collection into even sized buckets of elements. Each bucket can be accessed individually by accessing the paginated list resource parametrized.
To define a paginated list resource:
<resource>
<name>paginated-countries</name>
<description>A list of countries resource.</description>
<uri>/{countries}/pages/{page-id}</uri>
<paginates>country</paginates>
</resource>
Where the <paginates/>
element is the name of the collected resource.
Paginated list resources support the READ
operation.
When creating an element that should be added to the collection, use a list resource and perform a CREATE
operation on it (you don’t necessarily have to implement the READ
operation of the list resource for this).
Link Point Resource
A Link point resource has no representation on its own: it has a name
, description
and uri
attributes. Link point resources exist only to provide a place for links to be attached to.
The root
resource is a good example of a link point. It does not represent a particular data type itself, like a cart or an order, but provides a resource where links can be attached.
To define a link point resource:
<resource>
<name>bookmarks</name>
<description>bookmarks resource</description>
<uri>/{base.family}/bookmarks</uri>
</resource>
This snippet defines a link point resource called bookmarks. To attach links to this resource, we would define one or more <relationship/>
elements with (in this case) bookmarks as the <from/>
reference. See Relationships for more information.
Selector Resource
Selector Resources are used when a state of a resource is modifiable by selection of choices.
The different selection choices are either in the state choice or chosen. Choice reflects that a selection could be chosen and chosen reflects that a selection is chosen.
A selector resource definition consists of two resources. First, a selector resource to declare which resource it will act as a selector for, using the <selector-for/>
element:
<resource>
<name>billingaddress-info-selector</name>
<description>Selects the billing address to use for an order.</description>
<uri>{billingaddress-info}/selector</uri>
<selector-for>billingaddress-info</selector-for>
</resource>
Second, a choice resource to declare what type of resources can be chosen from, using the <choices-for/>
element:
<resource>
<name>billingaddress-info-selector-choice</name>
<description>A choice for the billing address selector.></description>
<uri>{billingaddress-info-selector}/{address}</uri>
<choices-for>billingaddress-info-selector</choices-for>
</resource>
In the example, a choice between different addresses is modeled. This is defined by the URI identifier-part {address}
, which points to the address
resource. By convention, the choice resource also to declare which selector resource it is associated with. The <choices-for>
tag serves this purpose.
Form Resource
A form resource combines an entity
, action-rel
and result
into a single resource. This provides the following functionality:
- A
READ
form prototype to retrieve form data - An action link which links a
POST
operation from theREAD
operation - A
POST
form prototype which processes the form’s posted data
<resource>
<name>create-order-form</name>
<description>Create order form resource</description>
<uri>{item}/form</uri>
<form>
<entity>create-order-form</entity>
<action-rel>submit-action</action-rel>
<result>order</result>
</form>
</resource>
Advisor
An advisor is used to give advice on a resource operation.
There are two types of advises with different semantics: Non-blocking Advisors and Blocking Advisors. For more information, see Advisors.
Non-blocking Advisor
A non-blocking advisor provides additional information when a READ
operation is performed on a resource, but does not affect the HTTP status code emitted by the READ
operation.
To define a non-blocking advisor:
<advisor>
<name>terms-for-order-form</name>
<description>Terms and Conditions NeedInfo</description>
<linked-to>terms-and-conditions</linked-to>
<advises>order</advises>
</advisor>
In the provided example the <advises/>
element references the resource on which the advisor should provide additional information on. In cases the advise operation provides information about an issue that can be resolved, like a warning that indicates a validation issue, the optional <linked-to/>
element points to another resource which can resolve the issue.
Blocking Advisor
A blocking advisor may enrich the HTTP response of a HTTP GET
request on a form in the same manner as an advisor on Read. In addition, the blocking advisor may hide the submit-action link as well as block an actual HTTP POST
to the form. If a POST
request is performed against a blocked form resource, the form prototype for handling the POST
operation is never executed. Instead, a structured error message is returned with the HTTP status code 409
.
<advisor>
<name>terms-for-order-form</name>
<description>Terms and Conditions NeedInfo</description>
<linked-to>terms-and-conditions</linked-to>
<blocks>order</blocks>
</advisor>
Resource Relationships
Relationships (links) between resources are defined as follows:
<relationship>
<name>profile-from-root</name>
<description>User Profile from root</description>
<rel>profile</rel>
<from>base.root</from>
<to>user-profile</to>
</relationship>
Here, we’ve defined a link from the root resource (base.root
) to the user-profile resource, with a rel
of "profile".
Out of Family Resources
You may have noticed in the previous example, we referenced the root resource as base.root
. This is a fully-qualified reference to a resource, which is required for referencing resources that are not in the same family as the resource you’re defining.
In order to reference resources in other families, a Maven dependency needs to be added to bring in the API for the resource:
<dependency>
<groupId>com.elasticpath.rest.definitions</groupId>
<artifactId>ep-resource-profiles-api</artifactId>
<version>${rest.definitions.version}</version>
</dependency>
This would bring in the Cortex "profiles" API, which would then allow you to define additional relationships from the profiles resource:
<relationship>
<name>bookings-for-profile</name>
<description>Link from the profile to the list of bookings for a given user</description>
<rel>bookings-for-profile</rel>
<from>profiles.profile</from>
<to>booking-list</to>
</relationship>