Writing Your First Resource
Writing Your First Resource
Make sure your development environment has been properly set up as described in the Setting up your Developer Environment.
- Create a resource that returns a terms and conditions message, which can be displayed to a shopper
- Write a unit test that will help you troubleshoot basic problems with your resource
- Add this resource to your Cortex web application and verify that everything is working correctly
In subsequent tutorials, you will enhance this resource by incorporating links and ensure that the terms and conditions for an order have been accepted by the shopper before completing a purchase.
When this tutorial is complete, you will be able to retrieve the terms and conditions message by sending the following request to Cortex with Studio or another REST client:
Content-type: application/json Authorization: Bearer c7326d79-9273-4820-b45d-587f90d1dc9b GET http://localhost/cortex/terms/12345=
{ "self": { "type": "elasticpath.terms.terms-and-conditions", "uri": "/commerce-legacy/terms/12345=", "href": "http://localhost/cortex/terms/12345=", "max-age": 0 }, "links": [], "message": "An example terms and conditions string." }
Defining the API
In this tutorial, you will create a resource family named terms that contains a single entity named terms-and-conditions. The terms-and-conditions entity will have an id field, which will store a unique identifier, and a message field, which will store the full text of the terms and conditions.
In this section, you will create a new definition project for the terms resource family using the resource-api-archetype. A definition project contains an XML description of a resource family and it's entities and is required to create a new Cortex resource.
- Open a command line and navigate to the extensions/cortex/resources-api directory.
- Run the following command to create a Cortex resource API definition
project:
mvn archetype:generate -DarchetypeArtifactId=resource-api-archetype -DarchetypeGroupId=com.elasticpath.rest.archetype -DarchetypeVersion=1.10.100.20140827092242
- Enter the following information when prompted:
- Change to the terms-and-conditions-api directory
- Rename example.xml, located under src/main/resources/META-INF/rest-definitions/, to terms-and-conditions.xml
- In terms-and-conditions.xml, replace the
resource-family element with the following
XML:
src/main/resources/templates/rest-tutorials/terms-and-conditions-api/src/main/resources/META-INF/rest-definitions/terms-and-conditions.xml <resource-family> <name>terms</name> <description><![CDATA[Example terms and conditions resource.]]></description> <entity> <name>terms-and-conditions</name> <description><![CDATA[Defines the TermsAndConditions Entity.]]></description> <property> <name>terms-id</name> <description><![CDATA[The identifier representing each individual entity.]]></description> <internal/> <string/> </property> <property> <name>message</name> <description><![CDATA[Defines the terms and conditions message.]]></description> <string/> </property> </entity> </resource-family>
The XML above defines a terms and conditions resource family, which contains a single entity named terms-and-conditions. The terms-and-conditions entity has an ID and a message. You'll see later that this entity definition is used to produce the initial source code you will use to implement of the terms and conditions resource.
Generating the API Jar
Now that you've written your API definition, you can use Maven to create a jar containing the classes required to finish implementing the terms and conditions resource.
- Open a command line and navigate to the terms-and-conditions-api project
- Run the following command to build the API
definition:
mvn clean install
This maven build command will use the API generator maven plugin to generate the API source code that you will use to implement the resource. - The generated source files are placed in the sub-directory:target/generated-sources/api-generator. Verify that you can access these files from your IDE. For example, try openingTermsAndConditionsEntity.java, as you will use this class later in the tutorial.
Implementing the Resource
You will now use a Maven archetype to generate a template resource that you will later modify into the terms and conditions resource. By default, the template resource simply returns a 200 READ OK response when a GET is performed on /. You will need to enhance the resource to handle GET requests for the/terms/<terms-id> URI pattern.
- Open a command line and navigate to theextensions/cortex/resources directory.
- Run the following command to create a Cortex resource project:
mvn archetype:generate -DarchetypeArtifactId=rest-resource-extension-archetype -DarchetypeGroupId=com.elasticpath.rest.archetype -DarchetypeVersion=1.10.100.20140827092242
- Enter the following information when prompted:
- Change to the terms-and-conditions-resource directory
- Open pom.xml and add a <dependency> on
the terms-and-conditions-api artifact. This dependency will
add the TermsAndConditionsEntity to the classpath of your
resource.
src/main/resources/templates/rest-tutorials/terms-and-conditions-simple/pom.xml <dependencies> <dependency> <groupId>com.elasticpath.rest.example</groupId> <artifactId>terms-and-conditions-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
- Rename the ExampleResourceOperatorImpl class, located under src/main/java/com/elasticpath/rest/example/rest/resource/example/impl, to TermsResourceOperatorImpl.
- In TermsResourceOperatorImpl, change the @Named annotation from @Named("exampleResourceOperator") to @Named("termsResourceOperator").
- Add a method to TermsResourceOperatorImpl that will process
read requests on /terms/<terms-id>.
You will use the TermsAndConditionsEntity class in this method. Use the Builder class found within TermsAndConditionsEntity to create a TermsAndConditionsEntity instance. Afterward, encapsulate the entity in a ResourceState so that the entity can be serialized and presented to the client as JSON.
Once you've made these changes, verify that the TermsResourceOperatorImpl looks the same as the following code snippet.src/main/resources/templates/rest-tutorials/terms-and-conditions-simple/src/main/java/com/elasticpath/example/TermsResourceOperatorImpl.java import static com.elasticpath.rest.OperationResultFactory.createReadOK; import static com.elasticpath.rest.schema.SelfFactory.createSelf; import javax.inject.Named; import javax.inject.Singleton; import com.elasticpath.rest.Operation; import com.elasticpath.rest.OperationResult; import com.elasticpath.rest.ResourceOperation; import com.elasticpath.rest.definition.terms.TermsAndConditionsEntity; import com.elasticpath.rest.resource.dispatch.operator.ResourceOperator; import com.elasticpath.rest.resource.dispatch.operator.annotation.OperationType; import com.elasticpath.rest.resource.dispatch.operator.annotation.Path; import com.elasticpath.rest.resource.dispatch.operator.annotation.ResourceId; import com.elasticpath.rest.resource.dispatch.operator.annotation.ResourceName; import com.elasticpath.rest.schema.ResourceState; /** * Handles REST operations on the terms resource. */ @Singleton @Named("termsResourceOperator") @Path(ResourceName.PATH_PART) public class TermsResourceOperatorImpl implements ResourceOperator { /** * Handles the READ operations. * * @param termsId item id * @param operation the Resource Operation. * @return the result */ @Path({ResourceId.PATH_PART}) @OperationType(Operation.READ) public OperationResult processRead( @ResourceId final String termsId, final ResourceOperation operation) { String message = "An example terms and conditions string."; TermsAndConditionsEntity termsEntity = TermsAndConditionsEntity.Builder.builder() .withTermsId(termsId) .withMessage(message) .build(); ResourceState<TermsAndConditionsEntity> termsState = ResourceState.Builder .create(termsEntity) .withSelf(createSelf(operation.getUri())) .build(); return createReadOK(termsState, operation); } }
- Rename example-blueprint.xml and applicationContext-example-resource.xml, located under src/main/resources/OSGI-INF/blueprint/, to terms-blueprint.xml and applicationContext-terms-resource.xml respectively.
- In example-blueprint.xml, change <service ref="exampleResourceOperator" auto-export="interfaces"/commerce-legacy/> to <service ref="termsResourceOperator" auto-export="interfaces"/commerce-legacy/> so that the TermsResourceOperator implementation is exported as an OSGI service.
- In applicationContext-terms-resource.xml, change the
resource server name from "example" to "terms". The TermsResourceOperatorImpl class uses the @Path(ResourceName.PATH_PART) annotation to to route requests of the form /terms/<sub-uri> to the termsResourceOperator.
src/main/resources/templates/rest-tutorials/terms-and-conditions-simple/src/main/resources/OSGI-INF/blueprint/applicationContext-terms-resource.xml <bean name="resourceServerName" class="java.lang.String"> <constructor-arg value="terms"/commerce-legacy/> </bean>
- In applicationContext-terms-resource.xml, change the
persistent-id in the overrideRolePermissionsProvider bean
to termsSimpleRolePermission.
Permissions are used to control access to certains paths in a resource based on user roles and are identified by a persistent-id. Permissions for a resource can also be changed via the OSGI web console.
src/main/resources/templates/rest-tutorials/terms-and-conditions-simple/src/main/resources/OSGI-INF/blueprint/applicationContext-terms-resource.xml <bean id="overrideRolePermissionsProvider" class="com.elasticpath.rest.authorization.rolepermissions.ConfigAdminRolePermissionsProvider"> <constructor-arg ref="resourceServerName"/commerce-legacy/> <constructor-arg ref="permissionsTransformer"/commerce-legacy/> <compendium:managed-properties persistent-id="termsRolePermissions" update-method="updateProperties"/commerce-legacy/> </bean>
Also in the applicationContext-terms-resource.xml, you'll see component scanning for spring beans in the bundle. The archetype will generate component scans for the project package entered during generation and com.elasticpath.rest for EP specific beans.src/main/resources/templates/rest-tutorials/terms-and-conditions-simple/src/main/resources/OSGI-INF/blueprint/applicationContext-terms-resource.xml <context:component-scan base-package="com.elasticpath.rest" scope-resolver="org.springframework.context.annotation.Jsr330ScopeMetadataResolver"/commerce-legacy/> <context:component-scan base-package="com.elasticpath.rest.example" scope-resolver="org.springframework.context.annotation.Jsr330ScopeMetadataResolver"/commerce-legacy/>
- Write the unit test for this resource.
You will test that requests to /terms/<terms-id> route to the termsResourceOperator and verify the TermsAndConditionsEntity ID and message.
Start by renaming ExampleResourceOperatorImplTest, located under src/test/java/com/elasticpath/rest/example/rest/resource/example/impl, to TermsResourceOperatorImplTest. The existing test for the example resource uses a different URI form and does not expect a ResourceState in the response so you should update the test to use the correct URI and expect a result of type ResourceState<TermsAndConditionsEntity>.
src/main/resources/templates/rest-tutorials/terms-and-conditions-simple/src/test/java/com/elasticpath/example/TermsResourceOperatorImplTest.java import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import com.elasticpath.rest.OperationResult; import com.elasticpath.rest.ResourceOperation; import com.elasticpath.rest.schema.ResourceState; import com.elasticpath.rest.definition.terms.TermsAndConditionsEntity; /** * Tests URI-related annotations on {@link com.elasticpath.rest.example.rest.resource.example.impl.TermsResourceOperatorImpl}. */ @RunWith(MockitoJUnitRunner.class) @SuppressWarnings("unchecked") public class TermsResourceOperatorImplTest { @Mock private ResourceOperation resourceOperation; @InjectMocks private TermsResourceOperatorImpl resourceOperator; private static final String TERMS_ID = "termsId"; @Test public void shouldBuildTermsStateWithId() { OperationResult result = resourceOperator.processRead(TERMS_ID, resourceOperation); ResourceState<TermsAndConditionsEntity> termsState = (ResourceState<TermsAndConditionsEntity>) result.getRepresentation(); assertEquals(TERMS_ID, termsState.getEntity() .getTermsId()); } @Test public void shouldBuildTermsStateWithMessage() { String message = "An example terms and conditions string."; OperationResult result = resourceOperator.processRead(TERMS_ID, resourceOperation); ResourceState<TermsAndConditionsEntity> termsState = (ResourceState<TermsAndConditionsEntity>) result.getRepresentation(); assertEquals(message, termsState.getEntity() .getMessage()); } }
- Run the following command to build the terms and conditions resource JAR
file and execute the unit
tests:
mvn clean install
Verify that the build was successful.
Adding the Resource to the Cortex Web Application
In this section you will learn how to include a new resource and its definition in your Cortex web application.
- Open a command line and navigate to the extensions/cortex directory.
- Look-up the bundle symbolic name for the terms-and-conditions-resource project, found in the OSGi manifest located in terms-and-conditions-resource/target/classes/META-INF/MANIFEST.MF.
- Add the symbolic name for the terms-and-conditions-resource
bundle to the extension resource configuration's blueprint configuration,
located in
cortex/ext-cortex-resource-configuration/src/main/resources/OSGI-INF/blueprint/resource-configuration-blueprint.xml.
<blueprint> xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"> <!-- export services --> <service id="requiredResourceServerBundles" interface="java.util.List"> <bean class="java.util.ArrayList"> <argument> <array value-type="java.lang.String"> ... <value>com.elasticpath.rest.example.terms-and-conditions-resource</value> ... </array> </argument> </bean> </service> </blueprint>
- Change to the root directory of the resource configuration project and
execute the following command to rebuild it with your latest
changes:
mvn clean install
Verify that the build was successful. - Open the Cortex web application POM file: ext-cortex-webapp/pom.xml.
- In the Cortex web application POM file, add a
<dependency> on the
terms-and-conditions-api artifact and the
terms-and-conditions-resource
artifact.
<dependency> <groupId>com.elasticpath.rest.example</groupId> <artifactId>terms-and-conditions-api</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.elasticpath.rest.example</groupId> <artifactId>terms-and-conditions-resource</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency>
- In the Cortex web application POM file, go to the configuration section of
the maven-dependency-plugin build plugin and add an
<includeGroupIds> section. In the
<includeGroupIds>, section, add an entry for the groupId of the
terms-and-conditions-api and
terms-and-conditions-resource artifacts.
<includeGroupIds> com.elasticpath.rest.example </includeGroupIds>
- Change to the Cortex web application's directory and execute the following
command to rebuild it with your latest changes and to pull in the project's
dependencies:
mvn clean install -Pwith-oauth2-resource
Verify that the build was successful.
Testing the Resource
At this point you've created all the necessary components for the terms and conditions resource, and the final result can be verified.
- Open a command line and navigate to the directory containing your Cortex web application
- Run the following command to start the Cortex web
application:
mvn tomcat7:run-war
- Using Cortex Studio, retrieve the terms by sending the following
request:Request:Response:
Content-type: application/json Authorization: Bearer c7326d79-9273-4820-b45d-587f90d1dc9b GET http://localhost/cortex/terms/12345=
{ "self": { "type": "elasticpath.terms.terms-and-conditions", "uri": "/commerce-legacy/terms/12345=", "href": "http://localhost/cortex/terms/12345=", "max-age": 0 }, "links": [], "message": "An example terms and conditions string." }
Congratulations, you’ve just built your first Contex resource. Next, in Adding Links to Your Resource, you will learn how to link the orders resource to your resource so a client can view the terms and conditions from an order.