Coder Social home page Coder Social logo

knowledge-base's People

Watchers

Qingyu avatar

knowledge-base's Issues

Swagger doc Guidelines

SwaggerDoc guidelines and annotations

In order to generate the API documentation, swagger offers a set of annotations to declare and manipulate the output.
This page introduces the annotations provided by swagger-core. They are grouped into three - the annotation to declare the resource, the set of annotations to declare an operation, and the set of annotations that declare API models. The documentation for each annotation is meant as an overview of its usage.
Open the interactive OCC REST API documentation in your web browser by accessing the following link: https://{hostname}:{port}/rest/v2/swagger-ui.html

Quick Annotation Overview

Name Description
@Api Marks a class as a Swagger resource.
@ApiImplicitParam Represents a single parameter in an API Operation.
@ApiImplicitParams A wrapper to allow a list of multiple ApiImplicitParam objects
@ApiModel Provides additional information about Swagger models.
@ApiModelProperty Adds and manipulates data of a model property.
@ApiOperation Describes an operation or typically a HTTP method against a specific path.
@ApiParam Adds additional meta-data for operation parameters.
@ApiResponse Describes a possible response of an operation.
@ApiResponses A wrapper to allow a list of multiple ApiResponse objects.
@Authorization Declares an authorization scheme to be used on a resource or an operation.
@AuthorizationScope Describes an OAuth2 authorization scope.

Examples

General rule

  • Use 'verbs XXX' for method's value and note description and uppercase in ApiOperation
  • Use 'nouns XXXing/XXXed XXX' for ApiParam/ApiImplicitParam description and lowercase
  • Period is not needed for all description
  • Some APIs should add 'language' and 'currency' as @ApiImplicitParam

@Api

example:

@Api(tags = "MessageCenter")
public class MessageCenterCSController
{
.......
}

image

@ApiOperation

example:

@ApiOperation(value = "Gets conversations for current customer", notes = "Returns the conversation list for current customer.")
public ConversationListWsDTO getConversationsForCustomer()
{
......
}

image

@ApiParam

example:

public ConversationListWsDTO getConversationsForAgent(@ApiParam(value = "the conversation status used for getting conversation list", allowableValues = "open, unassigned") @RequestParam(value = "status", required = true) final String status)
{
......
}

image

@ApiImplicitParams

example:

@ApiImplicitParams
({
 @ApiImplicitParam(name = "pageSize", value = "The maximum number of elements in the result list.", required = true, dataType = "string", paramType = "query"),
 @ApiImplicitParam(name = "currentPage", value = "The requested page number", required = false, dataType = "string", paramType = "query"),
 @ApiImplicitParam(name = "sort", value = "The string field the results will be sorted with", required = true, dataType = "string", paramType = "query")

})
......
public CategorySearchResultWsDTO findProductCategoriesByTextOrMask()
{
......
}

image

@ApiResponses

example:

......
@ApiResponses
({
 @ApiResponse(code = 401, message = "Unauthorized"),
 @ApiResponse(code = 403, message = "Forbidden"),
 @ApiResponse(code = 404, message = "Not Found"),
 @ApiResponse(code = 200, message = "customer's addresses list")
})
public AddressListWsDTO getAddresses()
{
......
}
......

image

@ApiModel and @ApiModelProperty

Documentation for DTO can be added by using standard element in xxx-beans.xml and additional element. The @ApiModel and @ApiModelProperty will be generated automatically
example:

<bean class="de.hybris.platform.messagecentercsoccaddon.dto.conversation.ConversationWsDTO">
 <description>Conversation</description>
 <hints>
  <hint name="wsRelated"/>
  <hint name="alias">conversation</hint>
 </hints>
 <property name="id" type="String">
  <description>conversation identifier</description>
  <hints>
      <hint name="required">true</hint>
  </hints>
 </property>
 <property name="status" type="String">
  <description>the conversation status</description>
  <hints>
      <hint name="required">true</hint>
  </hints>
 </property>
 <property name="agent" type="de.hybris.platform.commercewebservicescommons.dto.user.PrincipalWsDTO">
  <description>the agent of conversation</description>
 </property>
 <property name="customer" type="de.hybris.platform.commercewebservicescommons.dto.user.PrincipalWsDTO">
  <description>the custom of conversation</description>
  <hints>
      <hint name="required">true</hint>
  </hints>
 </property>
 <property name="createDate" type="java.util.Date">
  <description>the creation date of conversation</description>
  <hints>
      <hint name="required">true</hint>
  </hints>
 </property>
 <property name="closeDate" type="java.util.Date">
  <description>the close date of conversation</description>
 </property>
 <property name="latestMessage" type="de.hybris.platform.messagecentercsoccaddon.dto.conversation.ConversationMessageWsDTO">
  <description>the latest message of the conversation</description>
  <hints>
      <hint name="required">true</hint>
  </hints>
 </property>
</bean>

The WsDTO class will be generated with swagger annotations. example:

@ApiModel(value="conversation", description="Conversation")
public  class ConversationWsDTO  implements Serializable 
{

  /** Default serialVersionUID value. */
 
  private static final long serialVersionUID = 1L;

 /** conversation identifier<br/><br/><i>Generated property</i> for <code>ConversationWsDTO.id</code> property defined at extension <code>messagecentercsoccaddon</code>. */
 @ApiModelProperty(name="id", value="conversation identifier", required=true)  
 private String id;

 /** the conversation status<br/><br/><i>Generated property</i> for <code>ConversationWsDTO.status</code> property defined at extension <code>messagecentercsoccaddon</code>. */
 @ApiModelProperty(name="status", value="the conversation status")  
 private String status;

 /** the agent of conversation<br/><br/><i>Generated property</i> for <code>ConversationWsDTO.agent</code> property defined at extension <code>messagecentercsoccaddon</code>. */
 @ApiModelProperty(name="agent", value="the agent of conversation")  
 private PrincipalWsDTO agent;
.....
}
  • hint 'wsRelated' is required if you want swagger annotation to be generated
  • hint 'alias' allow you override DTO name (default value is taken from class name) - In most cases it will be needed because marshaller from webservicescommons remove WsDTO suffix from class name (e.g. AddressWsDTO - > address , AddressListWsDTO - > addressList)
  • hint 'required' allow you to mark parameter as required.
  • When return a WsDTO, use @ApiResponse and set message="XXX data/XXX list"(see example)
  • hint 'allowedValues' let you to define list or range of values which can be used for this property.
  • In our xxx-beans.xml, use <hint name="alias">{beanNameWsDTO}</hint>
    e.g. <hint name="alias">conversation</hint>

Customized annotation

@ApiBaseSiteIdParam

example:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ApiImplicitParams({@ApiImplicitParam(name = "baseSiteId", value = "Base site identifier", required = true, dataType = "String", paramType = "path")})
public @interface ApiBaseSiteIdParam {
}

image

Reference

https://github.com/swagger-api/swagger-core/wiki/Annotations
https://wiki.hybris.com/display/prodandtech/Adding+Swagger+Documentation+To+Hybris+Web+Services
https://wiki.hybris.com/display/prodandtech/Swagger+API+Conformance+Tracking

Authorizations for all APIs

Authorizations

There are 5 roles(authorizations) in commerce. "ROLE_CLIENT", "ROLE_TRUSTED_CLIENT", "ROLE_CUSTOMERGROUP", "ROLE_CUSTOMERMANAGERGROUP" and "ROLE_GUEST".
Each role is independent, and there is no containment relationship between them.

Introduction

ROLE_CLIENT

"ROLE_CLIENT" is for anonymous user. If a OAuth client configured "client_credentials" as grant_type, an anonymous user can get token by using "client_credentials" and then he can access the api which contains "ROLE_CLIENT" in @secured annotation.

ROLE_TRUSTED_CLIENT

"ROLE_TRUSTED_CLIENT" is meant for an app that has permission to do basically everything and was meant for integrations. The reason why it has permission to do basically everything is that we agreed to use "ROLE_TRUSTED_CLIENT" on all apis which use @secured annotation. So in the current version of commerce, "ROLE_TRUSTED_CLIENT" can access everything.

ROLE_CUSTOMERGROUP

"ROLE_CUSTOMERGROUP" is for logged in customer. The API which use this in @secured annotation means logged in customer can use token("password" as grant_type) to access the API.

ROLE_CUSTOMERMANAGERGROUP

"ROLE_CUSTOMERMANAGERGROUP" is for logged in customer manager. The API which use this in @secured annotation means logged in customer manager can use token("password" as grant_type) to access the API. Besides, customer manager can also use his own token to operate on customers.

ROLE_GUEST

ROLE_GUEST is special and it is just for guest checkout where user has entered his e-mail.

How to use

There are 2 places that use thest roles, client authorities and API @secured annotation. How do we use them depends on which kind of role we use.

For "ROLE_CLIENT" and "ROLE_TRUSTED_CLIENT"

  • We must configure "client_credentials" in authorizedGrantTypes field and configure the role in authorities. e.g.
INSERT_UPDATE OAuthClientDetails;clientId[unique=true];resourceIds;scope;authorizedGrantTypes;authorities;clientSecret;registeredRedirectUri
;mobile_android;xxx;basic;client_credentials;ROLE_CLIENT,ROLE_TRUSTED_CLIENT;xxx;xxx;
  • We must add corresponding roles in @secured annotation. "ROLE_CLIENT" can't access "ROLE_TRUSTED_CLIENT" and vice versa.
  • Both of them get token through "client_credentials".
  • When to use "ROLE_CLIENT" depends on our scenario.
  • All APIs(except static data) needs "ROLE_TRUSTED_CLIENT" in @secured annotation, his is our agreement.

For "ROLE_CUSTOMERGROUP" and "ROLE_CUSTOMERMANAGERGROUP"

  • These 2 roles have nothing to do with "authorities" of client, they only need client with "password" in authorizedGrantTypes.
  • Which permissions you have depends on which user you use to get token.
  • Customer manager can't access the API which contains role "ROLE_CUSTOMERGROUP" and vice versa.
  • But customer manager can operate on customers.
    e.g. Claim coupon API /{baseSiteId}/users/[userId]/customercoupons/[couponCode]/claim uses @Secured({ "ROLE_CUSTOMERGROUP", "ROLE_CUSTOMERMANAGERGROUP","ROLE_TRUSTED_CLIENT" })
    If customer manager(CSA) want to help custmer([email protected]) to claim a coupon, CSA do not need to ask customer for his token. He can directly call /{baseSiteId}/users/[email protected]/customercoupons/[couponCode]/claim and use his CSA token. As a result he can access this API and the coupon is claimed by [email protected].

For "ROLE_GUEST"

  • "ROLE_GUEST" is only used for guest checkout.
  • If we want to use "ROLE_GUEST", our API path must contains cartId. When guest checkout, our API like createChinesePaymentRequest(/orders/[orderCode]/payment/request) can not use "ROLE_GUEST" because there is no cartId in API path.
  • Because all guest checkout related API is user/[userId]/cart/[cartId]/xxxxx, there defines a filter "GuestRoleFilter". This filter is used after spring security filters and it is responsible for setting current authentication as guest when user decided to do the checkout as a guest. During the guest checkout the userService gets current user as 'anonymous', but cartService returns dedicated user.

Additional info for OAuth2

1. Some links

  1. OAuth2 in hybris
    OAuth2 is extension working as an oauth server in platform.
    Oauth2 in hybris
    Source Code for oathu2 extension
  2. OAuth2 framework
    This is the official doc for the OAuth2 framework. You can find out how it works and wht is the work flow.
    OAuth 2.0
  3. Spring Security for OAuth2
    In Hybris, we use the Spring Security to support the OAuth2. So you can get a picture about how dose Spring Security support to OAuth2.
    Spring Security
    Spring Security Architecture

2. The main work flow in Hrbris for OAuth2.

  1. We post a request to "/authorizationserver/oauth/token" to get the access token
    ClientCredentialsTokenEndpointFilter. attemptAuthentication -> ProviderManager.authenticate -> AbstractUserDetailsAuthenticationProvider.authenticate -> DaoAuthenticationProvider.retrieveUser -> ClientDetailsUserDetailsService.loadUserByUsername -> OpenIDClientDetailsService.loadClientByClientId(Check the clientId in the DB table OAuthClientDetailsModel) -> TokenEndpoint.postAccessToken (this is the controller to map the url "/authorizationserver/oauth/token") -> OpenIDClientDetailsService.loadClientByClientId -> CompositeTokenGranter.grant (this function grant the by the grantType, ROLE_"GROUP" for password; authorities in OAuthClientDetailsModel for credential)
    Then we the the access token.

  2. We pull a request to access the API by add the token in the request head.
    OAuth2AuthenticationProcessingFilter -> OAuth2AuthenticationManager.authenticate -> DefaultTokenServices.loadAuthentication -> HybrisOAuthTokenStore.readAccessToken -> DefaultOAuthTokenService .getAccessToken and loadAuthentication -> OAuth2AuthenticationManager.authenticate ->
    UserMatchingFilter.doFilterInternal (Set the session user for backend,and the session user control the access) ->
    GuestRoleFilter.doFilterInternal(Change the auth with the ROLE_GUEST if anonymous and have session cart)

  3. @secured
    We can accsee the api with the auth.hasRole() match the ROLE_XXX on API @secured.

Develop with Angular Library

Develop with Angular Library

For SPA CSS Style

There are two ways to override the css from Spartacus lib.

One for change the Scss variable

:root {
  --cx-g-color-primary: #f0ab00;
  --cx-g-color-dark: #000;
}

y-add-to-cart {
  --cx-g-color-primary: #008fd3;
}

Another for override the css directly

You can use selectors to change the css that you want.

cx-banner{
	cx-media img {
	    max-width: 100%;
	    max-height: 100%;
	    -webkit-transform: scale(1);
	    transform: scale(1);
	    opacity: 1;
	    transition: all .6s;
	}
}

For SPA Functions

  • SPA allows customer to customize and extend Spartacus Features and customer can follow the following steps.
  • SPA supports for overwriting Component/Service but currently no support for changing existing one.

Overrides existing Angular component

You can provide a CMS component configuration to the ConfigModule, or directly to the B2cStorefrontModule. The following configuration shows how to configure a custom Angular component for the CMS UpdatePasswordComponent.
In custom-update-password.module.ts

B2cStorefrontModule.withConfig({
  cmsComponents: {
    UpdatePasswordComponent: {
      component: CustomUpdatePasswordComponent
    }
  }
});
exports: [CustomUpdatePasswordComponent],
entryComponents: [CustomUpdatePasswordComponent]

In app.module.ts

imports: [
    CustomUpdatePasswordModule,
    ...
]

Add new Angular component based on the Existing CMS data

In the case a new component(MyCouponsComponent) has been created in the backend this new component needs to be mapped to a new Angular component.
In my-coupons.module.ts

ConfigModule.withConfig({
  cmsComponents: {
    MyCouponsComponent: {
      component: MyCouponsComponen,
      guards: [AuthGuard],
    }
  }
});
exports: [MyCouponsComponent],
entryComponents: [MyCouponsComponent]

There’s an example above shows how to configure guards for CMS component
In app.module.ts

imports: [
    MyCouponsModule,
    ...
]

Add a static page with route

Another way to create a custom page with custom components is to create it statically.
In static-page.module.ts

const staticRoutes: Routes = [{
  path: 'static-page',
  component: StaticPageComponent,
  canActivate: [CmsPageGuard]
}];
@NgModule({
  declarations: [StaticPageComponent],
  imports: [
    CommonModule,
    RouterModule.forChild(staticRoutes)
  ]
})

And then you can visit this page via http://localhost:4200/static-page

Override components/slots/templates with outlets

First of all the B2cStorefrontModule must be imported. Then in html

<ng-template cxOutletRef="ProductAddToCartComponent" cxOutletPos="replace">
    <div>We replace the ProductAddToCartComponent REPLACE the original component</div>
</ng-template>
    
<ng-template cxOutletRef="Section1" cxOutletPos="before" let-model>
    <div>We replace all Section1 BEFORE the original section</div>  
</ng-template>
    
<ng-template cxOutletRef="ProductDetailsPageTemplate" cxOutletPos="after">
     <div>We replace ProductDetailsPageTemplate AFTER original template</div>
</ng-template>

    
<ng-template [cxOutletRef]="pdpOutlets.PRICE" cxOutletPos="before" let-product> 
    <div>We replace product price in PDP BEFORE original template</div>
</ng-template>

<ng-template [cxOutletRef]="pdpOutlets.PRICE" cxOutletPos="replace" let-product> 
    <div class="price" aria-label="new item price">
        {{ product.product.price|json }} 
    </div>
</ng-template>

<ng-template [cxOutletRef]="pdpOutlets.PRICE" cxOutletPos="after" let-product> 
    <div>We replace product price in PDP AFTER original template</div>
</ng-template>

Override Service in specific Component

To configure a custom component service, you can provide a service in a similar fashion.

    ConfigModule.withConfig({
      cmsComponents: {
        SearchBoxComponent: {
            providers: [
            {
              provide: SearchBoxComponentService,
              useClass: CustomerSearchBoxService,
              deps: [CmsComponentData, ProductSearchService, RoutingService]
            }
          ]
        }
      }
    })

In the example, the SearchComponent is provided with a custom SearchBoxComponentService

Override Service in global

If you want to override a service and make it work globally. For example the original CartService is injected in global and provided by ModuleWithProvider. To override CartService you need to create a CustomCartService first

@Injectable()
export class CustomerCartService extends CartService 
{
...
}

Whether extends the original class depends on your logic. And then in app.module.ts

  providers: [
    {
      provide: CartService,
      useClass: CustomerCartService
    }
  ]

Use NGRX

Just like developer develop with SPA source, you can use ngrx modules in his app. Besides, you can also use existing ngrx modules provided by SPA. But there is something to know

  • You can dispatch actions or select data in you own service, because all actions, effects, selectors and reducers are exported.
  • Currently you can't modify the exported data from SAP. Such as add action in one actions collection, change the logic of effects or add properties in state...
  • You can handle SPA actions in your own Effects to override the original code logic in original Effects. e.g.
@Injectable()
export class CustomerCartEffects  
{
@Effect()
  loadCart$: Observable<
    | CartActions.LoadCartFail
    | CartActions.LoadCartSuccess
    | CartActions.ClearCart
  > = .....
}

loadCart$ is defined in CartEffects however we can override it. After doing above, you need to config the CustomerCartEffects in module

index.ts
export const effects: any[] = [CustomerCartEffects];
customer-cart.module.ts
imports: [
EffectsModule.forFeature(effects)
]

Final summary

  • The sample code can be downloaded from here
  • More information will come when "Customizing and Extending Spartacus Features" changes

Spock Testing Framework

Background

  • For writing tests in our projects we use different techniques and approaches. Tests are written in different way which leads to worse readability and it becomes a maintenance nightmare.
  • In our tests we have also lots of boilerplate code that is not needed to understand what we test.
  • Spock framework come with few nice features that helps to improve our tests.
  • Spock is written on top of junit framework. This way its quite easy to integrate it with hybris testing mechanism. It is also easier to learn as its known approach.
  • Spock is written in groovy language and this way it reduce quantity of code we need to write to test our feature.

Introduction

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers.

Advantages

  • Common tests structure(Based on JUnit)
  • Less code in tests
  • Better maintainability
  • BDD (behavior-driven development) style of testing
  • Better tests failure handling

How to test with spock

Groovy extension

  1. Generate a new extension with template[ygroovy]
  2. Add new extension to localextensions.xml
  3. Build & startup

Non Groovy Extention

  1. create a source folder named 'groovytestsrc' in your extension
  2. add following code in buildcallback.xml:
    replace EXT_NAME with extension name
<macrodef name="EXT_NAME_before_build">
		<sequential>
			<property name="ext.EXT_NAME.native.file.extension" value="groovy" />
			<property name="ext.EXT_NAME.additional.testsrc.dir" value="groovytestsrc" />
			<outofdate>
				<sourcefiles>
					<fileset dir="${ext.EXT_NAME.path}">
						<include name="${ext.EXT_NAME.additional.testsrc.dir}/**" />
					</fileset>
				</sourcefiles>
				<targetfiles path="${HYBRIS_TEMP_DIR}/touch/EXT_NAME_testsrctouch" />
				<sequential>
					<touch mkdirs="true">
						<fileset dir="${ext.EXT_NAME.path}/${ext.EXT_NAME.additional.testsrc.dir}" />
					</touch>
				</sequential>
			</outofdate>
		</sequential>
	</macrodef>

	<macrodef name="EXT_NAME_groovynature_compile_core">
        <attribute name="extname"/>
        <sequential>
            <if>
                <isset property="ext.@{extname}.coremodule.available"/>
                <then>
                    <if>
                        <istrue value="${ext.@{extname}.extension.coremodule.sourceavailable}"/>
                        <then>
                            <groovy_compile srcdir="${ext.@{extname}.path}/groovytestsrc"
                                            destdir="${ext.@{extname}.path}/classes"
                                            extname="@{extname}"/>
                        </then>
                    </if>
                </then>
            </if>
        </sequential>
    </macrodef>

	<macrodef name="EXT_NAME_after_compile_core">
		<sequential>
			<EXT_NAME_groovynature_compile_core extname="EXT_NAME" />
		</sequential>
	</macrodef>

Reference

SAP accessibility standard in hybris

Information and Text

  • ACC-254 - Screen Titles
    Every page (screen) has to have a visible title, which describes the topic or purpose of the screen.

  • ACC-255 - Text Alternative for Non-Text Content
    All semantic non-text content (for example, graphics, videos) has to have a text alternative.

  • ACC-257 - Purpose and Target of a Reference
    The purpose and target of a reference (including links and menu buttons) have to be described in text form. The text makes it clear to the user what the target and purpose are.

  • ACC-258 - Consistent Use of Identical UI-Elements
    UI elements that have the same appearance and the same function have to be consistently labeled and used throughout the product.

Visual Perception

  • ACC-260 - Text Resizing up to 200 percent.
    Text can be resized up to 200 percent without assistive technologies and without loss of either content, affordance, or function.

  • ACC-262 - Color and Contrast Setting
    The application has to apply the display settings of the operating system or has to provide the user with a selection of color and contrast settings (High-Contrast-Black, High-Contrast-White, colors).
    Understanding

Attributes and Identification of UI Elements

  • ACC-263 - Information, Structures and Relationships
    Information, structures, and relationships conveyed by the presentation (such as tables) can be identified by assistive technologies.

  • ACC-264 - Interoperability with Assistive Technologies
    a) The name and type (properties, states, and values) of all UI elements can be identified using assistive technologies.
    b) States, properties, and values that can be set by the user can also be set using assistive technologies.
    c) Assistive technologies are informed whenever properties, states, and values of UI elements are changed.*

  • ACC-265 - Language Attributes
    Within a page, language atttributes have to be used to indicate text content correctly so the language is identified by assistive technologies.

  • ACC-266 - Correct Use of Markup Language
    If markup language is used, it has to be used in a syntactically correct way, so that assistive technologies can read and analyze the content correctly.

Orientation and Navigation Within the Application

  • ACC-269 - Group Skipping
    It has to be possible to bypass groups or repeated elements.

User Interaction Within the Application

  • ACC-272 - Tab/Reading Order
    Logically related UI elements have to receive keyboard and reading focus in an order that preserves semantics and usability.

  • ACC-273 - Robust Context
    Focusing UI elements or changing their state does not produce a context change automatically if there is no conscious interaction with the user (such as choosing Enter or the tab key).

  • ACC-274 - Error Prevention
    The application has to allow the user to review, correct, or cancel input.

  • ACC-275 - Correct Error Handling
    In case of input errors, the location of the error has to be indicated, a description of the error has to be provided and suggestions for correcting the error have to be provided.

Study Tmall Integration

Tmall Integration

The Tmall integration makes it possible for you to use SAP Commerce as a unified platform for managing your online stores running on the SAP Commerce platform and Tmall.com. You can integrate orders, return and refund orders from Tmall, process them in SAP Commerce, and synchronize processing results back to Tmall. You can manage Tmall Products info in SAP Commerce.

Archtecture

image
A Tmall App(Tmall Adapter), deployed using the Jushita service provided by Alibaba, serves as a bridge between Taobao Open Platform and the Data Hub for SAP Hybris Commerce.

Managing Tmall orders in SAP Commerce

Pull orders from Tmall

There are two approaches to syncing orders from Tmall to SAP Commerce, as follows:
Through APIs

If you choose this approach, we recommend that you use Data Hub to transfer data between the Taobao Open Platform and SAP Commerce.
image

Through Alibaba Cloud ApsaraDB for RDS (Relational Database Service)
This approach requires a Jushita environment with RDS.
The mpintgrdsservice extension is required for downloading Tmall orders and return request from RDS.
image

You can use the MarketplaceRDSSubscriptionJob cron job to download Tmall orders from RDS. The cron job is included in the mpintgrdsservice extension.

Tmall push orders to hybris

Behind the automatic download function is a message subscription service provided by Taobao. Depending on your subscriptions, this service pushes messages about transactions and products to a Tmall Adaptor deployed using Jushita. You can use this service with a developer account and select the types of messages that you want to subscribe to. For detailed information about the message subscription service, see the Taobao documentation at http://open.taobao.com/doc2/detail.htm?articleId=101663&docType=1&treeId=2Information published on non-SAP site.

When you click the Turn On Order Subscription button in the Backoffice, a request is posted to the Tmall Adaptor, asking the Tmall Adaptor to start monitoring Tmall orders. When changes occur to Tmall orders (for example, the Tmall order status has changed), the Tmall Adaptor updates the order data in SAP Commerce accordingly. When you click the Turn Off Order Subscription button, a request is posted to the Tmall Adaptor, asking the Tmall Adaptor to stop monitoring Tmall orders.

Return and refund orders

Go to https://help.hybris.com/6.6.0/hcd/f78260de9a70427c977bbae1f92c5282.html for details.

Sync results from hybris to Tmall

image

Data model in managing Tmall order

image

Managing Tmall Product info in SAP eCommerce

To synchronize a new product or product changes to Tmall, simply synchronize the product from the staged version to online version by clicking the Synchronize Action button.

Marketplace Product Sync Arch Design

image

Marketplace Product Sync Data Model

image

Marketplace Product Sync Sequence

image

The MarketplaceProductSyncEventListener will listen event of product syn from stage to online.
The MarketplaceSyncProductJob is responsible for syn product to Tmall in period.

Marketplace StockLevel Sync Arch Design

image

Marketplace Product Review Sync Arch Design

image

REFERENCES
https://wiki.hybris.com/pages/viewpage.action?pageId=410296396
https://help.hybris.com/6.6.0/hcd/98178defb2244e94bb7039946e725186.html

REST vs Messaging

RESTful interactions have become vital to enterprise computing as it enables many APIs on the web today. With that said, lets define what problems REST solves best:

  • Synchronous Request/Reply – HTTP (the network protocol on which REST is transported) itself is a request/response protocol, so REST is a great fit for request/reply interactions.
  • Publicly Facing APIs – Since HTTP is a de facto transport standard thanks to the work of the IETF, the transport layer of the APIs created using REST are interoperable with every programming language. Additionally, the message payload can be easily documented using tools such as Swagger (OpenAPI Specification). And due to the wide range of security threats present on the internet, the security ecosystem for REST is robust, from firewalls to OAUTH (Authentication/Authorization).

However, this over reliance on the use of REST and synchronous patterns have negative consequences in the communication between Microservice and some cases are at odds with the principles of proper Microservice architecture:

  • Tight Coupling –There will always be some coupling of services around the interface (specifically around the data) but when invoking a RESTful service, the developer is assuming that the message will only ever need to be delivered to one place. What happens when another service or component comes online in the future and needs the data? Sure you can update the code to add the new endpoint, but that displays the flaw: unnecessary coupling. Soon your simple Microservice has become an orchestrator which defies the Microservice’s attribute “single in purpose”.
  • Blocking – When invoking a REST service, your service is blocked waiting for a response. This hurts application performance because this thread could be processing other requests. Think of it this way: What if a bartender took a drink order, made the cocktail and waited patiently for the patron to finish the drink before moving on to the next customer? That customer would have a great experience, but all of the other customers would be thirsty and quite unhappy. The bar could add additional bartenders, but would need one for each customer. Obviously, it would be expensive for the bar, and impossible to scale up and down as patrons came and went. In many ways, these same challenges occur when threads are blocked in applications waiting for RESTful services to respond. Threads are expensive and difficult to manage, much like bartenders!
  • Error Handling – HTTP was built for the web and we have all seen our browsers get stuck trying to access a webpage. Usually we click the refresh button and the page displays. But what if it fails again? Try to refresh again? Does one start to implement a human form of exponential back off by getting a cup of coffee and trying again in a few minutes? We do not know what to do as every webpage is different and has unique behavior. The same type of issue occurs when directly invoking a RESTful service. Should this complex retry logic reside in a service’s code? If it does the service is even more tightly coupled to other services – again violating the key principle of keeping Microservices architecture single in purpose and small in size.

The benefits of messaging for event-driven Microservices are many and varied:

  • Loose Coupling – Using messaging, specifically publish/subscribe functionality, services do not have knowledge of other services. They are notified of new events, process that information and produce/publish new information. This new information then can be consumed by any number of services, thanks to publish/subscribe. Loose coupling allows Microservices to be ready for the never-ending changes that occur within enterprises.
  • Non-Blocking – Microservices should perform as efficiently as possible, and it is a waste of resources to have many threads blocked and waiting for a response. With asynchronous messaging applications can send a request and process another request instead of waiting for a response. This becomes clear when revisiting the bartender analogy. Bartenders are complex individuals and can service multiple patrons and interleave the execution of multiple tasks at the same time. They go from patron to patron and process multiple orders without blocking/waiting on any single patron so every patron has a drink and leaves a good tip!
  • Simple to scale – As applications and enterprises grow, the ability to increase capacity (or dynamically scale to optimize costs) becomes one of the most important advantages of Microservice architecture. Since each service is small and only performs one task, each service should be able to grow or shrink as needed. Event driven architecture and messaging make it easy for Microservices to scale since they’re decoupled and do not block. This also makes it easy to determine which service is the bottleneck and add additional instances of just that service, instead of blindly scaling up all services, which can be the case when Microservices are chained together with synchronous communications. The ability to scale using event driven architecture has been proven by companies such as Linked-in and Netflix so you can rest assured it will work for your enterprise.
  • Greater Resiliency and Error Handling –In the past few months major airlines have experienced data center issues that resulted a cascade of application synchronization problems. The impact of these problems was massive: flight cancellations, angry customers and the loss of millions of dollars, not counting damage to their reputations. Microservices failure scenarios become tricky when considering in-progress transactions. Messaging platforms that offer guaranteed delivery can act as the source of truth in the event of massive failures and enable rapid recovery without message loss. In the case of less massive failures (service failure) the use of messaging allows healthy services to continue processing since they are not blocked on the failed service. Once healed, the failed service will start processing the data that had accumulated during the downtime, making the system eventually consistent. Additionally, code becomes much cleaner and readable as all the cumbersome retry and error handling logic is gone. In event driven Microservices the messaging tier handles the retry of failed messages (unacknowledged messages) which frees the service to be small in size and single in purpose.

In short anything synchronous you want HTTP requests; Anything asynchronous you can use message passing:

  • HTTP requests are for when I need this info right now. If a user makes a request and needs the response to show in UI, it would be a case of making an HTTP request to our micro services.
  • Messages is for async stuff. If a create-user action needs a manual approval, then you might send the message to corresponding queues to user-service telling it you need this approved.
  • You don't know detailed implementation of the service you called, if the service needs to call another service to fulfill your request, and that communication is achieved by message, then you cannot ask a synchronous response.

.classpath clearification and rules

There are my summary of entries in .classpath that we will used in commerce.

What is the .classpath for?

What we should notice

And the rules we always break, are defined in de.hybris.platform.test.ExtensionRequirementsNotAwareAboutAddonsTest/de.hybris.platform.test.ExtensionRequirementsTest
I summary them in point 4.
For the standard way, we should refer to trail The commerce trail of Update Dependencie

Summary of .classpath entry

  1. kind="con"
    There must be a "con" entry, which stands for container, which is interpreted by eclipse as a classpath container.
    A classpath container provides a way to indirectly reference a set of classpath entries through a classpath entry of kind CPE_CONTAINER

  2. kind="src"
    if this project relay on another project.
    Like:
    which is required in extensioninfo.xml
    <requires-extension name="acceleratorcms"/>
    we should add an entry in .classpath
    <classpathentry exported="true" kind="src" path="/acceleratorcms" />
    a. exported="true" can solve the issue of Serial dependence,
    exported: Say you have Project B that depends on Project C. The dependency is defined as exported=true. Then, another Project A that depends on Project B, will have also Project C present on A'a classpath.
    b. exported="false", But all our extensions are relay on platform, so ant entry must be false.
    <classpathentry exported="false" kind="src" path="/platform"/>
    c. combineaccessrules="false" means this project can use the resource of that extension.
    <classpathentry combineaccessrules="false" kind="src" path="/addonsupport"/>
    d. output define the path of the class files

  3. kind="lib"
    define the path of the Libraries this project will use, which are not in the path or lib folder.
    almost in backoffice extension

  4. there are two tests
    ExtensionRequirementsNotAwareAboutAddonsTest
    ExtensionRequirementsTest
    summary:
    1. in one classpath only and must has one entry for platform
    <classpathentry exported="false" kind="src" path="/platform"/>
    exported="false"

    2. .classpath has an entry for required extension, required extensions defined in extensioninfo.xml
    3. Checks if the .classpath or extensioninfo.xml files require sampledata or a template

  5. default is false, but there is a standard example for us:

    1. there is combineaccessrules="false" or not, the required extension works fine.
    2. The commerce trail of Update Dependencie
      We can see that we should write the true/false explicitly. And we should not export the dependent extension in addon.

Javadoc Guidelines

General rule

  • Doc is not needed for the Get and Set method and Controller
  • Use 'verbs XXX' for class/interface/method description and uppercase
  • Follow 'Default implementation of {@link xxx}' for class which has interface
  • Use 'nouns XXXing/XXXed XXX' for parameter/return description and lowercase in method
  • Use 'throw XXX when XXX' for exception description in method
  • Period is not needed for all description

Class and interface

Facade interface
Deals with xxx related DTOs using existing service

example:
/**
 * Deals with flash buy related DTOs using existing service
 */
public interface FlashBuyFacade

Service interface
Deals with xxx related models using existing DAOs

example:
/**
 * Deals with alipay order related Models using existing DAOs
 */
public class DefaultAlipayOrderService extends DefaultOrderService implements AlipayOrderService

Dao interface
Looks up items related to {@link x1Model} {@link x2Model} {@link xNModel}

example:
/**
 * Looks up items related to {@link OrderModel}
 */
public interface AlipayOrderDao

Hook interface
Verbs xxx before/after/when xxx

example:
/**
 * Runs custom code before/after saving a cart
 */
public interface CommerceSaveCartMethodHook

Facade service hook and Dao implementation
Default implementation of {@link xxxDao}

example:
/**
 * Default implementation of {@link AlipayOrderDao}
 */
public class DefaultAlipayOrderDao extends DefaultGenericDao<OrderModel> implements AlipayOrderDao

Populator
Populates {@link A} to {@link B}

example:
/**
 * Populates {@link CustomerCouponModel} to {@link CustomerCouponData}
 */
public class CustomerCouponPopulator implements Populator<CustomerCouponModel, CustomerCouponData>

Interceptor
Verbs xxx before/after/when xxx

example:
/**
 * Runs before controllers' execution to extract the site-id from the request URI and 
 * to set the current session value
 */
public class SiteLanguageInterceptor extends HandlerInterceptorAdapter

Processor
Verbs xxx for xxx

example:
/**
 * Deals with http request for preventing CSRF attack
 */
public class CSRFRequestDataValueProcessor implements RequestDataValueProcessor

Strategy/Handler
Verbs xxx for xxx before/after/when xxx

example:
/**
 * Deals with mock alipay requests strategy
 */
public class MockAlipayCreateRequestStrategy extends DefaultAlipayCreateRequestStrategy

Manager/Resolver
Verbs xxx for xxx

example:
/**
 * Manages the CSRF token for either a given session or request
 */

public final class CSRFTokenManager

Job/Task
Verbs xxx before/after/when xxx

example:
/**
 * Sets value of max order quantity for flash buy product after flash buy expires or ends
 */
public class ResetMaxOrderQuantityJob extends AbstractJobPerformable<FlashBuyCronJobModel>

Exception
xxx exception

example:
/**
 * HTTP request exceptions
 */
public class AlipayException extends Exception

Constants
xxx (web/controller) constants

example:
/**
 * Chinesepspalipaymock constants
 */
public final class ChinesepspalipaymockConstants extends GeneratedChinesepspalipaymockConstants

Method

Description
Start with a verb

example:
/**
* Updates the flash buy status for cart when placing order
*/
@Override
public boolean beforeController(final HttpServletRequest request, final HttpServletResponse response,
			final HandlerMethod handler) throws Exception

Parameter

  • Start with a noun
  • The main content should be describing itself, not the purpose
example:
* @param params
*           the parameters used for generating notification map
* @param tradeStatus 
*           the tradeStatus used for setting alipay trade status

Exception
throw when xxx is not supported

example:
/**
* @throws UnsupportedEncodingException
     throw when encoding is not supported
*/
public Map<String, String> getRefundNotifyParams(final Map<String, String> params, final String errorCode)
throws UnsupportedEncodingException
{

Return

  • return the (list of) xxx Model
  • return the result with a detailed explanation
  • return true if xxx, and false otherwise
example:
/**	
 * @return the list of PromotionSourceRuleModel
 */
List<PromotionSourceRuleModel> getPromotionSourceRuleForCouponCode(final String couponCode);

/**	
 * @return the total number of products and categories mapped with promotion source rule
 */
int countProductOrCategoryForPromotionSourceRule(final String code);

/**	
 * @return true if the coupon is owned by the current user and false otherwise
 */
boolean isCouponOwnedByCurrentUser(String couponCode);

TMall Integration

集成方式:第三方IT工具,需要发布至淘宝服务市场,可入驻聚石塔。
https://open.taobao.com/doc.htm?docId=101569&docType=1

主要需求点(包括上海team的实现方式)

产品数据同步

  1. product
    用marketplaceSyncProductCronjob同步以下数据:
    Product attributes Price Main product image Listing status
  • staged-> online完成后该cronjob会被触发( 监听产品同步事件AfterCronJobFinishedEvent )
  • cronjob也会在scheduled的时间触发,检查online version的changes并同步
  • 如果上次同步失败,则会retry
  • 使用deltadection来获取有changes的products
  1. stock
    产品同步的时候会被同步,也可以由marketplaceSyncStockCronJob定时检查是否需要同步
  2. media
    由marketplaceSyncMediaJob来同步non-main图片

订单数据同步

  1. 支持两种方式下载tamll订单,手动选择时间范围和自动下载(需要打开开关),两种方式均提供了通过api->datahub->hybris和聚石塔rds两种实现方式,api实现方式均会将数据发送到datahub再转换成hybris订单,rds实现方式直接从rds查询订单数据再转换成hybris订单。
  • manual download
    选择需要下载的订单的时间范围
  • auto download
    需要订阅tmc message,根据message转换成订单
  1. fulfill order: MarketplaceSubmitEventListener监听了AfterSaveEvent,如果有新的tamll订单创建则会启动order process
  2. 退货退款
    首先要同步订单状态到hybris,与前面的订单下载实现方式类似,hybris端处理完成会发请求更新tamll订单。
  3. 同步tmall评价
    marketplaceDownloadReviewCronJob下载并转换为hybris的product review

operation log?

记录数据同步过程中的操作历史

第三方应用的开发

主要用来处理调用淘宝api,所有的同步请求,包括订单下载产品数据上传,接受来自hybris的数据同步请求,并将数据发送datahub。

同步数据

目前仍然只有这两种方式

  • 淘宝api
  • 入驻聚石塔,为商家购买rds,推送数据到rds,访问rds获取数据(订单数据)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.