Friday, March 18, 2016

CWXFS3201W: Another CatalogEntry indexing process is still in progress for master catalog

Often we come across a situation while running the di-preprocess and di-buildindex utilities we get the message (such as when these jobs are hard stopped for any reason) that we are already running the process which is not the case all the times.

Delta tables for catentry and category i.e. TI_DELTA_CATENTRY and TI_DELTA_CATGROUP are used by the SOLR index to know what data to update in terms of catentry and catgroup that has changed on the environment. Even UpdateSearchIndex scheduled job used to make use of these tables to update the search index. Catentry with -1 and status as P depicts that the process is pending hence user gets the message. 




Running the process with additional parameter -force true or taking care of the culprit record in delta table resolves the issue.

Thursday, March 17, 2016

Implementing Rich Snippets in WCS

Introduction
We can add Rich Snippets support to the site so that the extra information appears when the search engines like google displays the results, we need to mark up the text inside the JSP files which can be done through one of the following way-
1) Microdata specification
2) schema.org specification

The web pages in the Aurora starter store already use microdata format to mark up the content, additionally we can meta tags to represent the information in  a different format in search results, we will explore the steps required for expediting the microdata format

Markup
1) Product markup- Itemtype- http://data-vocabulary.org/Product
itemprop- name,description

2) Offer markup- Itemtype- http://data-vocabulary.org/Offer
itemprop- price

3) Ratings markup- Itemtype- http://data-vocabulary.org/Review-aggregat
itemprop- rating and count

For example
<div itemscope="itemscope" itemtype="http://data-vocabulary.org/Product">
    <span itemprop="name">Summer Shirts</span>
    <span itemprop="description">Best selling shirt in summer season</span>

    <span itemprop="offerDetails" itempscope itemtype="http://data-vocabulary.org/Offer">
        Price: $<span itemprop="price">179.99</span>
        <span itemprop="availability" content="in_stock">In stock</span>
    </span>

    <span itemprop="review" itemscope itemtype="http://data-vocabulary.org/Review-aggregate">
        <span itemprop="rating">3.4</span> stars, based on
        <span itemprop="count">11</span> reviews
    </span>
</div>

Testing
Once the page is updated with the intended mark-ups, we can test the site pages through the Google's Rich snippets testing tool to give us a flavor of the changes made-

https://developers.google.com/structured-data/testing-tool/

Wednesday, March 16, 2016

Google Map Integration in WCS


Please follow the below simple steps to have Google map visible on the UI-

Step 1) Create a container div for the google map, and give it a size as per the need-
<!DOCTYPE html>
<html>
  <head>
    <style>
      #map {
        width: 500px;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
  </body>
</html>

Step 2) Load the Google Maps JavaScript API v3.
Load the Google Maps API by adding a <script> tag to the end of the <body> section of your HTML. This script downloads the code required to display maps on your page and will not block the loading of other resources.

 <script src="https://maps.googleapis.com/maps/api/js"
     async defer></script>

Step 3) Creates and displays a Google Map in the div.
Below code creates a new Google Maps object:
var map = new google.maps.Map();

We need to make a reference to the div that the map will be loaded into.Options for the map, such as the center, zoom level, and the map type. There are many more options that can be set, but these three are required.
<script>
  function initMap() {
    var mapDiv = document.getElementById('map');
    var map = new google.maps.Map(mapDiv, {
      center: {lat: 44.540, lng: -78.546},
      zoom: 8
    });
  }
</script>

Step 4) Final Code
So the final test html will look like this which can be tested on any browser

This snippet can used as a base for integrating the google map in your site by dynamically supplying the values for longitude, latitude, zoom and other options-

<!DOCTYPE html>
<html>
  <head>
    <style>
      #map {
        width: 800px;
        height: 500px;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script>
      function initMap() {
        var mapDiv = document.getElementById('map');
        var map = new google.maps.Map(mapDiv, {
          center: {lat: 52.86098, lng: -8.21097},
          zoom: 15
        });
      }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?callback=initMap"
        async defer></script>
  </body>
</html>

Sunday, March 13, 2016

Controlling the creation of Guest Users in WCS

We might get into a situation where we find a lot many guest users end up created in the database with out having any order placed.
 
isGeneric() method of the the controller command helps to control the creation of guest users in the WCS database, here is the reason why-

By default isGeneric() method returns false which depicts that the OOB version of the controller command does not allow the user to access it with generic user mode hence the framework has to create a guest user to allow the user to access the command in question.

for example ProductDisplayCmd command can still continue having isGenric() method returning true which in return ensure that the generic user is able to access the product page and no guest user has to be created for the continued access over the PDP page on the site, whereas we would want the OrderItemAddCmdImpl to return false to block for the generic user and hence the guest user will be created once the command is invoked.

Thursday, March 10, 2016

Creating Custom REST Handler from Controller Command and Data Bean – WCS 7 FEP 8

Introduction
------------------
WCS 7 FEP 8 supports the creation of configuration based controller commands which reduces the coding effort by having the mapping file in the system.The input and output of the databean and controller command invocation is controlled by the mapping file itself.

In FEP7, the resource handler extends AbstractClassicResourceHandler to invoke controller commands and data beans whereas in FEP8, it extends the AbstractConfigBasedClassicHandler which provides the configuration support required.

Please follow the below 4 steps to create the custom REST handler-

1)  Create Handler
Create the com.mycompany.commerce.order.rest.handler.MyOrderHandler by exteding the com.ibm.commerce.rest.classic.core.AbstractConfigBasedClassicHandler class (which provides the required configuration support) under WebSphereCommerceServerExtensionsLogic project.

a) Below method retrieves the Order information using OrderDataBean-
    @GET
    @Path("/byId/{orderId}")
    public Response getOrderById(
            @PathParam("storeId") String storeId,
            @PathParam("orderId") String orderId) {
        Response finalResponse = null;
 
        String responseFormat = getParameterValue(PARAMETER_RESPONSE_FORMAT, MediaTypeHelper.FORMAT_JSON, false);
        String profileName = getParameterValue(PARAMETER_PROFILE_NAME, "MyCompany_Store_Details", false, false);
        finalResponse = prepareAndValidate(storeId, RESOURCE_NAME, GET_METHOD, request, responseFormat);
        if (finalResponse == null) {
            Map<String, Object> paramOverrideMap = new HashMap<String, Object>();
            paramOverrideMap.put(PARAMETER_ORDER_ID, orderId);
            finalResponse = executeConfigBasedBeanWithContext(OrderDataBean.class.getName(), profileName, responseFormat, paramOverrideMap);
        } 
        return finalResponse;
    }

executeConfigBasedBeanWithContext loads configuration related to the data bean involved placed under the Rest.war/WEB-INF/beanmapping/ or Rest.war/WEB-INF/beanmapping-ext/ folder.
  
b) This below method can be used to add an item-
    @Path("/@current/item")
    @Consumes("application/json")
    @PUT
    public Response addItem(@PathParam("storeId") String storeId,
            @QueryParam("responseFormat") String responseFormat)
            throws Exception {
        Response finalResponse = prepareAndValidate(storeId, "myorder", POST_METHOD, request, responseFormat);
        if (finalResponse == null) {
            try {
                throwRestExceptionIfErrorsAreDetected();
                
                String cmdRefKey = OrderItemAddCmd.NAME;
                TypedProperty requestProperties = initializeRequestPropertiesFromRequestMap(responseFormat);
                if (!requestProperties.containsKey(ECConstants.EC_URL)) {
                    requestProperties.put(ECConstants.EC_URL, ECConstants.EC_URL);
                }
    
                finalResponse = executeControllerCommandWithContext(storeId, cmdRefKey, requestProperties,responseFormat, HttpStatus.CREATED);
            }
            catch (Exception e) {
                finalResponse = handleException(responseFormat, e, "addItem");
            }
        }
        return finalResponse;
    }
  
2)  Register Handler
Register the new handler class by making an entry inside the Rest.war/WEB-INF/config/resources-ext.properties file, add the full qualified name of the custom handler class such as com.mycompany.commerce.order.rest.handler.OrderHandler

3)  Mapping
Create the beanmapping-ext folder under Rest.war/WEB-INF/config folder and place the custom configuration files inside it which is com.ibm.commerce.order.beans.OrderDataBean.xml.

For getOrderById() method, I placed the below com.ibm.commerce.order.beans.OrderDataBean.xml under Rest.war/WEB-INF/beanmapping-ext/ folder-
   
    <?xml version="1.0" encoding="UTF-8"?>
    <bean>
        <profiles>
            <profile name="MyCompany_Store_Details">
                <inputs>
                    <input methodName="setOrderId" inputName="orderId" />
                </inputs>
                <outputs>
                    <output methodName="getPaymentInfo" outputName="paymentInfo" />
                    <output methodName="getOrderItemDataBeans" outputName="orderItemDataBeans">  
                        <output methodName="getShippingMode" outputName="shippingMode">
                            <output methodName="getCarrier" outputName="carrier"/>
                            <output methodName="getCode" outputName="code"/>
                            <output methodName="getShippingModeId" outputName="shippingModeId"/>
                            <output methodName="getShippingModeIdInEJBType" outputName="shippingModeId"/>
                            <output methodName="getStoreEntityIdInEJBType" outputName="storeEntityId"/>
                        </output>
                        <output methodName="getShippingModeId" outputName="shippingModeId"/> 
                    </output>               
                </outputs>
            </profile>
        </profiles>
    </bean>

Each data bean has a separate configuration file associated with it which can be found under Rest.war/WEB-INF/beanmapping/<fullyQualifedBeanName.xml>. The framework merges the custom and default bean mappings for the same bean, giving precedence to the custom configuration.

Each configuration file defines multiple profiles for a bean. Each profile contains a set of inputs and outputs. The input maps a request parameter to a corresponding setter method on the bean. The output maps a response data to the getter method on the bean.

4) Testing
In order to test the customization, we can use any REST plugin e.g. RESTClient in firefox.
Additionally, while testing the order response please make sure that we are logged in into the site and we are querying for the order which is related to the logged in user otherwise we will be getting the user authority exception like below-
"The user does not have the authority to run this command "com.ibm.commerce.order.beans.OrderDataBean"

URLs- 
http://localhost/wcs/resources/store/{storeId}/myorder/byId/{orderId}
http://localhost/wcs/resources/store/{storeId}/myorder/@current/item

Wednesday, March 9, 2016

Customizing REST service over Solr in WCS 7 FEP 8

Introduction
---------------
REST API built in WCS 7 FEP 8 onward eliminates the need to fetch the data through BOD  mediation layer instead data can be directly mapped from the Solr.
 
Search configuration coexisting on both the WebSphere Commerce and Search EARs include the following files:

1) WebSphere Commerce search configuration file (wc-search.xml)
2) Search configuration properties in the catalog component configuration file (wc-component.xml).
3) Search configuration properties in the STORECONF table.
4) Feature Pack 8 Search configuration properties in the solrconfig.xml file

Storefront search BOD-based services and business tooling use the configurations under the WebSphere Commerce application. REST-based search services use the configurations under the WebSphere Commerce search application.

Steps to include custom data Catentry.Field4 column on PDP page REST call
-------------------------------------------------------------------------------------------------
1) Go to Path- toolkit\search\solr\home\MC_10001\en_US\CatalogEntry\conf
Modify schema.xml and wc-data-config.xml with new Solr fields

Schema.xml-
<field name="field4" type="wc_text" indexed="true" stored="true"  multiValued="false"/>

wc-data-config.xml- After Adding catentry.Field4 column in the query
<field column="FIELD4" name="field4" />

2) Run preprocess with server stopped, and then start server and build Solr index
Newly included fields can be validated using the Solr testing URL-
http://localhost/solr/MC_10001_CatalogEntry_en_US/select?q=catentry_id:<catentryId>

3) Make the new search profile in wc-search.xml by extending the existing search profile invoked in the REST service call under Search folder-
<_config:profile name="X_findCatalogEntry" extends="IBM_findProductByIds_Details">
       <_config:query inherits="true">
           <_config:postprocessor           classname="com.ibm.commerce.foundation.server.services.rest.search.postprocessor.solr.SolrRESTSearchCatalogEntryViewUserDataQueryPostprocessor" />
       </_config:query>
       <_config:result inherits="true">
               <_config:field name="field4" />
       </_config:result>    
    </_config:profile>

4) Make the new wc-component.xml under Search folder- (It takes care of the mapping we used to have it under wc-business-object-mediator.xml prior to version 7 feature pack 7)

<?xml version="1.0" encoding="UTF-8"?>
<_config:DevelopmentComponentConfiguration
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.ibm.com/xmlns/prod/commerce/foundation/config ../xsd/wc-component.xsd "
    xmlns:_config="http://www.ibm.com/xmlns/prod/commerce/foundation/config">

    <_config:valuemappingservice>
        <_config:valuemapping externalName="CatalogEntryUserDataFieldNameMapping" internalName="CatalogEntryUserDataFieldNameMapping">
                <_config:valuemap externalValue="FIELD4" internalValue="field4" />
        </_config:valuemapping>
    </_config:valuemappingservice>
</_config:DevelopmentComponentConfiguration>


5) Make the new wc-rest-resourceconfig.xml file under /Search-Rest/WebContent/WEB-INF/config/com.ibm.commerce.rest-ext/wc-rest-resourceconfig.xml
<ResourceConfig>
    <Resource name="productview">
        <GetUri uri="store/{storeId}/productview/byId/{productId}"
                description="Get product by unique ID"                 searchProfile="X_findCatalogEntry,IBM_findProductByIds_Details,IBM_findProductByIdsWithAttributesAndAttachments,IBM_findProductByIds_Summary,IBM_findProductByIds_Summary_WithNoEntitlementCheck,IBM_Admin_findProductByIds"/>
    </Resource>
</ResourceConfig>
6) Restart the server and access the new indexed fields in userdata section using rest call with new search profile.

7) Testing
http://localhost/search/resources/store/10158/productview/byId/121892?responseFormat=xml&catalogId=10158&currency=USD

Please check new post for similar imlementation in WCS 8-
Customizing REST servoce over Solr in WCS 8