Elastic Path Commerce Development

Resource Prototypes

Resource Prototypes

Resource prototypes are generated Java classes which implement the resources defined in the API definition. They perform particular operations by instantiating and routing logic for CRUD and other API operations, including create, read, update, delete, submit, info and linkto.

Resource prototypes are generated from the API definition XML as an interface. The interface is also generated with associated interfaces for the CRUD and API operations the resource can perform. To implement a resource prototype, you must create a class which implements the prototype interface itself as well as one of the operation interfaces; all other wiring is handled for the developer.

Implementing a single interface per prototype reduces the number of unneeded or out of scope data injections, as well as reducing the amount of code necessary in each resource prototype.

For example, to generate a ProfileResource interface, a developer could define the following in profiles.xml:

  <resource>
    <name>profile</name>
    <description><![CDATA[The profile for the customer.]]></description>
    <uri>{base.family}/{base.scope}/{profile-id}</uri>
    <entity>profile</entity>
  </resource>
    

This resource definition will generate a ProfileResource interface class. Generated interfaces include specific operation interfaces for the resource, such as create and read. These interfaces are extended by the prototype.

Resource prototypes function without ever communicating directly with the Commerce Engine. The prototype layer communicates directly with the repository layer, and the repositories communicate with Commerce Engine. This allows for abstraction and isolation of a resource's functionality from Commerce Engine functionality. For more information see the Cortex Architecture and Call Stack documentation.

Implementing Operation Prototypes

Responsibilities

The created resource prototype class needs a constructor and an operation method. In general, you want to create one Prototype class per operation needed.

For example, to read for a profile entity, a developer might implement ReadProfilePrototype, which implements ProfileResource.Read. To update a profile entity, developers would add a class called UpdateProfilePrototype, which implements ProfileResource.Update.

The Constructor

The constructor initializes service dependencies and injects data. The constructor should be annotated with @Inject. The parameters to the constructor should be annotated with appropriate qualifier annotations, e.g. @RequestIdentifier or @ResourceRepository, to specify assignment of injected data. See Data Injectors for more details.

For example, the constructor for a prototype designed to read a Profile entity might look like:

  @Inject
  public ReadProfilePrototype(@RequestIdentifier final ProfileIdentifier profileIdentifier,
                              @ResourceRepository final Repository<ProfileEntity, ProfileIdentifier> repository) {
    this.profileIdentifier = profileIdentifier;
    this.repository = repository;
  }
        

The Operation Method

The operation method of the prototype should correlate with the Prototype's specific responsibility (i.e. onRead, onUpdate, onLinkTo etc.). This method will be similar across an entity's individual operation prototypes, with differences corresponding to the operations.

The operation method simply delegates to the relevant operation on the injected repository.

  @Override
  public Single<ProfileEntity> onRead() {
    return repository.findOne(profileIdentifier);
  }

This implementation demonstrates the resource prototype model's decoupling and abstraction potential. Rather than find, call, or construct the appropriate repository, the @ResourceRepository annotation injects the proper repository associated with an entity.

The operation method will change for each resource prototype. For an update prototype, a developer might use update instead of findOne:

  @Override
  public Completable onUpdate() {
    return repository.update(profileEntityForm, profileIdentifier);
  }
   

Marking Your Bundle as a Prototype Bundle

To mark your bundle as a bundle that provides a Helix prototype, add the following snippet to your pom.xml.

        <plugin>
          <groupId>org.apache.felix</groupId>
          <artifactId>maven-bundle-plugin</artifactId>
          <configuration>
            <obrRepository>NONE</obrRepository>
            <instructions>
              <Require-Capability>osgi.service; filter:="(objectClass=com.elasticpath.rest.helix.client.extender.BootstrapExtender)"; effective:=active,</Require-Capability>
            </instructions>
          </configuration>
        </plugin>

        <plugin>
          <groupId>com.elasticpath.rest.helix</groupId>
          <artifactId>helix-maven-plugin</artifactId>
        </plugin>
      

The <Require-Capability/> element under the maven-bundle-plugin section, marks the prototype bundle as discoverable by the Helix runtime.

Enabling Validation

The helix-maven-plugin enables compile-time validation of the prototype. If there are validation errors, the plugin will fail the build. The error output of the build indicates which validation rules are broken and gives hints on how to fix these errors. An example output of an validation error is:

[ERROR] Read operation for the resource BookingsPrototype does not have correct return type. It should be Observable<interface com.elasticpath.rest.definition.bookings.BookingIdentifier>
      

In this case, the read operation defined in the BookingsPrototype resource class is returning the wrong return type. The return type should be of type Observable<interface com.elasticpath.rest.definition.bookings.BookingIdentifier>.