Resource Repositories
Resource Repositories
A repository class provides information through either Commerce Engine or another repository to a resource via a consistent set of CRUD (create, read, update, delete) methods. Repositories translate between commerce domain objects and API representations, abstracting the complexity of Commerce Engine's databases and business logic from the API layer.
How Repositories Work
Repositories typically implement either the Repository or LinksRepository interface in the com.elasticpath.repository package, and one or more of its methods. A repository class may have multiple repositories implemented.
CRUD Repositories
CRUD repositories implement the Repository interface, and at least one of the following methods:
- create(): Creates an entity.
- E.g. Create a new shipping address for a customer's profile.
- findOne(): Finds (reads) a single entity.
- E.g. Find a specific shipping address for a customer's profile.
- findAll(): Finds (reads) all entities of a specified type.
- E.g. Find all shipping addresses for a customer's profile.
- delete(): Deletes an entity.
- E.g. Delete a shipping address from a customer's profile.
- update(): Updates an entity.
- E.g. Delete a shipping address from a customer's profile.
Extending Repositories
To add functionality or fields to an entity, or to change its default behaviour, you must extend a repository. To extend a repository, either override its out of the box methods or implement new ones.
Implementing Additional CRUD Functionality
A repository may not implement all CRUD methods by default. To implement additional CRUD functionality, implement the desired CRUD method in an existing repository.
For example, the ability for a user to create more than one wishlist per profile is not implemented by default. To implement this functionality, extend the out of the box repository by adding a create() method to it.
Adding Fields to a Repository
Sometimes, when extending a Commerce Engine domain object like addresses, you may need to pass extra fields to the repository and resource. To add fields to an entity that the repository returns, override the appropriate CRUD methods in a repository class to include the extra fields.
An example of this is examined in detail in the Extend a Helix Resource tutorial.
Creating New Repositories for New Resources
- Annotate the repository class with org.osgi.service.component.annotations.Component, to ensure it is exposed as an OSGi service and injectable.
- Implement the Repository or LinksRepository interface.
- Implement at least one CRUD method provided by the interfaces.
- Use either public or protected to ensure method extensibility.
Implementing CRUD Methods
Repositories implement an interface's methods based on a resource's intended functionality. To implement a CRUD method, you will typically invoke one or more Commerce Engine services or legacy repository methods using the ReactiveAdapter class. ReactiveAdapter helps to integrate CE Services and legacy repositories into reactive Rx execution chains.
To use the ReactiveAdapter methods, inject an instance of ReactiveAdapter into your repository implementation, then use one of its methods to call a Commerce Engine service or legacy repository:
@Inject CarsRepositoryImpl( @Named("reactiveAdapter") final ReactiveAdapter reactiveAdapter) { this.reactiveAdapter = reactiveAdapter; } ...
Calling Commerce Engine Services
- fromService() – returns a RxJava Observable which wraps the return value of a Commerce Engine service.
- fromServiceAsSingle() – returns a RxJava Single which wraps the return value of a Commerce Engine Service.
- fromServiceAsCompletable() - returns whether or not an operation was successful.
An example of returning an Observable from a Commerce Engine service is below:
Observable o = reactiveAdapter.fromService(() -> ceService.myMethod())
ReactiveAdapter methods handle InvalidBusinessStateException (HTTP 409) and EpValidationException (HTTP 400) exceptions automatically, and return appropriate structured error messages.
Custom Exception Handling when Calling Commerce Engine Services
If needed, you can still do custom exception handling:
Completable c = reactiveAdapter.fromServiceAsCompletable(() -> { try { return customerService.update(customer); } catch (UserIdExistException error) { throw exceptionTransformer.getResourceOperationFailure(error); } }); }
Or:
Completable c = reactiveAdapter.fromServiceAsCompletable(() -> customerService.update(customer)) .onErrorResumeNext(throwable -> { try { throw throwable; } catch (NullPointerException npe) { return Completable.error(ResourceOperationFailure.notFound()); } }); }
Calling Legacy Repositories
You may have to call a legacy resource's repository from your resource. Legacy repository methods return an ExecutionResult. ReactiveAdapter provides methods for returning this data to your repository as a RxJava type, depending on the type of data wrapped in the ExecutionResult.
If the legacy repository call returns an ExcecutionResult containing an Iterable as data use ReactiveAdapter.<String, List<String>>fromRepository():
Observable o = reactiveAdapter.<String, List<String>>fromRepository(() -> repository.getShoppingItems())
If the legacy repository call returns an ExecutionResult containing a non-Iterable, non-null Object use ReactiveAdapter.<String, List<String>>fromRepositoryAsSingle():
Single s = reactiveAdapter.<String, List<String>>fromRepositoryAsSingle(() -> repository.getDefaultCard())
If the legacy repository call returns an ExecutionResult containing a null value as data use ReactiveAdapter.<String, List<String>>fromRepositoryAsCompletable():
Completable c = reactiveAdapter.<String, List<String>>fromRepositoryAsCompletable(() -> repository.updateCustomer())
All these methods are non-blocking. Typically the legacy repository method does the exception handling.
Injecting a Repository into a Prototype
In order to inject a Repository (implementing either Repository or LinksRepository) use the @Inject annotation in combination with the @ResourceRepository annotation in the method signature:
@Inject public MyPrototype(@ResourceRepository Repository<MyEntity, MyIdentifier> repository) {
Make sure to type the generic type parameter of the Repository classes to the appropriate ResourceEntity and ResourceIdentifier.