knowledge-base's People
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
{
.......
}
@ApiOperation
example:
@ApiOperation(value = "Gets conversations for current customer", notes = "Returns the conversation list for current customer.")
public ConversationListWsDTO getConversationsForCustomer()
{
......
}
@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)
{
......
}
@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()
{
......
}
@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()
{
......
}
......
@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 {
}
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
- OAuth2 in hybris
OAuth2 is extension working as an oauth server in platform.
Oauth2 in hybris
Source Code for oathu2 extension - 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 - 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.
-
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. -
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) -
@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
- As a custom you can Build the Spartacus Storefront from libraries
- As a developer you should voiding Breaking Changes so that customer can easily upgrade the SPA libraries.
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
- Generate a new extension with template[ygroovy]
- Add new extension to localextensions.xml
- Build & startup
- This is a demo
flashbuytest.zip
clone esspock project from git(git clone ssh://[email protected]:hybris-coep/testing-extensions.git) before download this demo
Non Groovy Extention
- create a source folder named 'groovytestsrc' in your extension
- 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.
Caas - Order status
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
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.
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.
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
Data model in managing Tmall order
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
Marketplace Product Sync Data Model
Marketplace Product Sync Sequence
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
Marketplace Product Review Sync Arch Design
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
-
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 -
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 -
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 -
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 -
default is false, but there is a standard example for us:
- there is combineaccessrules="false" or not, the required extension works fine.
- 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.
Caas - Order Create and Place Flow
Caas - Order value fields and calculation
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);
Hybris - OMS
Hybris - Order prossing
TMall Integration
集成方式:第三方IT工具,需要发布至淘宝服务市场,可入驻聚石塔。
https://open.taobao.com/doc.htm?docId=101569&docType=1
主要需求点(包括上海team的实现方式)
产品数据同步
- product
用marketplaceSyncProductCronjob同步以下数据:
Product attributes Price Main product image Listing status
- staged-> online完成后该cronjob会被触发( 监听产品同步事件AfterCronJobFinishedEvent )
- cronjob也会在scheduled的时间触发,检查online version的changes并同步
- 如果上次同步失败,则会retry
- 使用deltadection来获取有changes的products
- stock
产品同步的时候会被同步,也可以由marketplaceSyncStockCronJob定时检查是否需要同步 - media
由marketplaceSyncMediaJob来同步non-main图片
订单数据同步
- 支持两种方式下载tamll订单,手动选择时间范围和自动下载(需要打开开关),两种方式均提供了通过api->datahub->hybris和聚石塔rds两种实现方式,api实现方式均会将数据发送到datahub再转换成hybris订单,rds实现方式直接从rds查询订单数据再转换成hybris订单。
- manual download
选择需要下载的订单的时间范围 - auto download
需要订阅tmc message,根据message转换成订单
- fulfill order: MarketplaceSubmitEventListener监听了AfterSaveEvent,如果有新的tamll订单创建则会启动order process
- 退货退款
首先要同步订单状态到hybris,与前面的订单下载实现方式类似,hybris端处理完成会发请求更新tamll订单。 - 同步tmall评价
marketplaceDownloadReviewCronJob下载并转换为hybris的product review
operation log?
记录数据同步过程中的操作历史
第三方应用的开发
主要用来处理调用淘宝api,所有的同步请求,包括订单下载产品数据上传,接受来自hybris的数据同步请求,并将数据发送datahub。
同步数据
目前仍然只有这两种方式
- 淘宝api
- 入驻聚石塔,为商家购买rds,推送数据到rds,访问rds获取数据(订单数据)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.