Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new activities to Einstein API #714

Merged
merged 24 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0a63ae0
Add new endpoints to Einstein API
vcua-mobify Sep 8, 2022
b3cd8f9
Update pages to send viewPage activity
vcua-mobify Sep 8, 2022
65f81c6
Update checkout to send beginCheckout activity
vcua-mobify Sep 9, 2022
58d7b91
Update PDP to send viewSearch and viewCategory activities
vcua-mobify Sep 12, 2022
df01004
Address duplicate activity calls
vcua-mobify Sep 13, 2022
a8f7921
Update PLP to send clickSearch and clickCategory activities
vcua-mobify Sep 13, 2022
a2ffb22
Include realm. Also include path location on viewPage
vcua-mobify Sep 13, 2022
5d35182
Begin fixing tests
vcua-mobify Sep 14, 2022
5616025
Move useEffect to before the redirect to fix an error.
vcua-mobify Sep 14, 2022
2740457
Mock Einstein in product listing test
vcua-mobify Sep 14, 2022
6c717ab
Fix lint
vcua-mobify Sep 14, 2022
f1dd3ad
Add tests for new activities
vcua-mobify Sep 15, 2022
d672a01
Merge branch 'develop' into einstein-api-update
bendvc Sep 16, 2022
2418ffa
Add a guard for setting realm and remove correlationId for now.
vcua-mobify Sep 16, 2022
1ef8fd5
Merge branch 'einstein-api-update' of github.com:SalesforceCommerceCl…
vcua-mobify Sep 16, 2022
9ed86f4
Simplify activity logic in PLP
vcua-mobify Sep 19, 2022
f57b5e8
Fix tests
vcua-mobify Sep 19, 2022
3bede9d
Fix lint
vcua-mobify Sep 19, 2022
3827852
Remove pathname from home page viewPage
vcua-mobify Sep 19, 2022
a17b93e
Re-add pathname to home page.
vcua-mobify Sep 19, 2022
a982093
Refactor: use constants to represent checkout steps
vcua-mobify Sep 20, 2022
4b58334
Add checkoutStep activity to Einstein API
vcua-mobify Sep 20, 2022
5b56eaf
Merge branch 'develop' into einstein-api-update
vcua-mobify Sep 20, 2022
52fb4fd
Merge branch 'develop' into einstein-api-update
vcua-mobify Sep 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,34 @@ class EinsteinAPI {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendViewSearch() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendClickSearch() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendViewCategory() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendClickCategory() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendViewPage() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendBeginCheckout() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendCheckoutStep() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}

async sendViewReco() {
return {requestId: 'test-req-id', uuid: 'test-uuid'}
}
Expand Down
161 changes: 161 additions & 0 deletions packages/template-retail-react-app/app/commerce-api/einstein.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ class EinsteinAPI {
console.warn('Missing `cookieId`. For optimal results this value must be defined.')
}

// The first part of the siteId is the realm
if (this.config.siteId) {
body.realm = this.config.siteId.split('-')[0]
}

return body
}

Expand Down Expand Up @@ -90,6 +95,105 @@ class EinsteinAPI {
return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user views search results.
**/
async sendViewSearch(searchText, searchResults, args) {
const endpoint = `/activities/${this.config.siteId}/viewSearch`
const method = 'POST'

const products = searchResults.hits.map((product) => {
const {productId, sku = '', altId = '', altIdType = ''} = product
return {
id: productId,
sku,
altId,
altIdType
}
})

const body = {
searchText,
products,
...args
}

return this.einsteinFetch(endpoint, method, body)
}
Comment on lines +98 to +122
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No action required on this comment. But I wanted to make sure that you notice the commonalities between all these 'send' functions. There is probably room to DRY things up, but I don't expect this to be done at the moment as this Einstein work at some point will be removed from the template. It would be nice to hear your thoughts on what we might do in the future to make things more upgradable.


/**
* Tells the Einstein engine when a user clicks on a search result.
**/
async sendClickSearch(searchText, product, args) {
const endpoint = `/activities/${this.config.siteId}/clickSearch`
const method = 'POST'
const {productId, sku = '', altId = '', altIdType = ''} = product
const body = {
searchText,
product: {
id: productId,
sku,
altId,
altIdType
},
...args
}

return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user views a category.
**/
async sendViewCategory(category, searchResults, args) {
const endpoint = `/activities/${this.config.siteId}/viewCategory`
const method = 'POST'

const products = searchResults.hits.map((product) => {
const {productId, sku = '', altId = '', altIdType = ''} = product
return {
id: productId,
sku,
altId,
altIdType
}
})

const body = {
category: {
id: category.id
},
products,
...args
}

return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user clicks a product from the category page.
* Not meant to be used when the user clicks a category from the nav bar.
**/
async sendClickCategory(category, product, args) {
const endpoint = `/activities/${this.config.siteId}/clickCategory`
const method = 'POST'
const {productId, sku = '', altId = '', altIdType = ''} = product
const body = {
category: {
id: category.id
},
product: {
id: productId,
sku,
altId,
altIdType
},
...args
}

return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user views a set of recommendations
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
Expand Down Expand Up @@ -132,6 +236,63 @@ class EinsteinAPI {
return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user views a page.
* Use this only for pages where another activity does not fit. (ie. on the PDP, use viewProduct rather than this)
**/
async sendViewPage(path, args) {
const endpoint = `/activities/${this.config.siteId}/viewPage`
const method = 'POST'
const body = {
currentLocation: path,
...args
}

return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user starts the checkout process.
**/
async sendBeginCheckout(basket, args) {
const endpoint = `/activities/${this.config.siteId}/beginCheckout`
const method = 'POST'
const products = basket.productItems.map((product) => {
const {productId, sku = '', price = '', quantity = ''} = product
return {
id: productId,
sku,
price,
quantity
}
})
const subTotal = basket.productSubTotal
const body = {
products: products,
amount: subTotal,
...args
}

return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user reaches the given step during checkout.
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
**/
async sendCheckoutStep(stepName, stepNumber, basket, args) {
const endpoint = `/activities/${this.config.siteId}/checkoutStep`
const method = 'POST'
const body = {
stepName,
stepNumber,
basketId: basket.basketId,
...args
}

return this.einsteinFetch(endpoint, method, body)
}

/**
* Tells the Einstein engine when a user adds an item to their cart.
* https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
Expand Down
133 changes: 127 additions & 6 deletions packages/template-retail-react-app/app/commerce-api/einstein.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import {
mockAddToCartProduct,
mockGetZoneRecommendationsResponse,
mockProduct,
mockCategory,
mockSearchResults,
mockBasket,
mockRecommendationsResponse,
mockRecommenderDetails
} from './mocks/einstein-mock-data'
Expand Down Expand Up @@ -55,7 +58,125 @@ describe('EinsteinAPI', () => {
'x-cq-client-id': 'test-id'
},
body:
'{"product":{"id":"56736828M","sku":"","altId":"","altIdType":""},"cookieId":"test-usid"}'
'{"product":{"id":"56736828M","sku":"","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test"}'
}
)
})

test('viewSearch sends expected api request', async () => {
const searchTerm = 'tie'
await einsteinApi.sendViewSearch(searchTerm, mockSearchResults)
expect(fetch).toHaveBeenCalledWith(
'http://localhost/test-path/v3/activities/test-site-id/viewSearch',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body:
'{"searchText":"tie","products":[{"id":"25752986M","sku":"","altId":"","altIdType":""},{"id":"25752235M","sku":"","altId":"","altIdType":""},{"id":"25752218M","sku":"","altId":"","altIdType":""},{"id":"25752981M","sku":"","altId":"","altIdType":""}],"cookieId":"test-usid","realm":"test"}'
}
)
})

test('viewCategory sends expected api request', async () => {
await einsteinApi.sendViewCategory(mockCategory, mockSearchResults)
expect(fetch).toHaveBeenCalledWith(
'http://localhost/test-path/v3/activities/test-site-id/viewCategory',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body:
'{"category":{"id":"mens-accessories-ties"},"products":[{"id":"25752986M","sku":"","altId":"","altIdType":""},{"id":"25752235M","sku":"","altId":"","altIdType":""},{"id":"25752218M","sku":"","altId":"","altIdType":""},{"id":"25752981M","sku":"","altId":"","altIdType":""}],"cookieId":"test-usid","realm":"test"}'
}
)
})

test('clickSearch sends expected api request', async () => {
const searchTerm = 'tie'
const clickedProduct = mockSearchResults.hits[0]
await einsteinApi.sendClickSearch(searchTerm, clickedProduct)
expect(fetch).toHaveBeenCalledWith(
'http://localhost/test-path/v3/activities/test-site-id/clickSearch',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body:
'{"searchText":"tie","product":{"id":"25752986M","sku":"","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test"}'
}
)
})

test('clickCategory sends expected api request', async () => {
const clickedProduct = mockSearchResults.hits[0]
await einsteinApi.sendClickCategory(mockCategory, clickedProduct)
expect(fetch).toHaveBeenCalledWith(
'http://localhost/test-path/v3/activities/test-site-id/clickCategory',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body:
'{"category":{"id":"mens-accessories-ties"},"product":{"id":"25752986M","sku":"","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test"}'
}
)
})

test('viewPage sends expected api request', async () => {
const path = '/'
await einsteinApi.sendViewPage(path)
expect(fetch).toHaveBeenCalledWith(
'http://localhost/test-path/v3/activities/test-site-id/viewPage',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body: '{"currentLocation":"/","cookieId":"test-usid","realm":"test"}'
}
)
})

test('beginCheckout sends expected api request', async () => {
await einsteinApi.sendBeginCheckout(mockBasket)
expect(fetch).toHaveBeenCalledWith(
'http://localhost/test-path/v3/activities/test-site-id/beginCheckout',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body:
'{"products":[{"id":"682875719029M","sku":"","price":29.99,"quantity":1}],"amount":29.99,"cookieId":"test-usid","realm":"test"}'
}
)
})

test('checkouStep sends expected api request', async () => {
const checkoutStepName = 'CheckoutStep'
const checkoutStep = 0
await einsteinApi.sendCheckoutStep(checkoutStepName, checkoutStep, mockBasket)
expect(fetch).toHaveBeenCalledWith(
'http://localhost/test-path/v3/activities/test-site-id/checkoutStep',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body:
'{"stepName":"CheckoutStep","stepNumber":0,"basketId":"f6bbeee30fb93c2f94213f60f8","cookieId":"test-usid","realm":"test"}'
}
)
})
Expand All @@ -71,7 +192,7 @@ describe('EinsteinAPI', () => {
'x-cq-client-id': 'test-id'
},
body:
'{"products":[{"id":"883360544021M","sku":"","price":155,"quantity":1}],"cookieId":"test-usid"}'
'{"products":[{"id":"883360544021M","sku":"","price":155,"quantity":1}],"cookieId":"test-usid","realm":"test"}'
}
)
})
Expand All @@ -87,7 +208,7 @@ describe('EinsteinAPI', () => {
'x-cq-client-id': 'test-id'
},
body:
'{"recommenderName":"testRecommender","__recoUUID":"883360544021M","product":{"id":"56736828M","sku":"","altId":"","altIdType":""},"cookieId":"test-usid"}'
'{"recommenderName":"testRecommender","__recoUUID":"883360544021M","product":{"id":"56736828M","sku":"","altId":"","altIdType":""},"cookieId":"test-usid","realm":"test"}'
}
)
})
Expand All @@ -103,7 +224,7 @@ describe('EinsteinAPI', () => {
'x-cq-client-id': 'test-id'
},
body:
'{"recommenderName":"testRecommender","__recoUUID":"883360544021M","products":{"id":"test-reco"},"cookieId":"test-usid"}'
'{"recommenderName":"testRecommender","__recoUUID":"883360544021M","products":{"id":"test-reco"},"cookieId":"test-usid","realm":"test"}'
}
)
})
Expand Down Expand Up @@ -136,7 +257,7 @@ describe('EinsteinAPI', () => {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body: '{"cookieId":"test-usid"}'
body: '{"cookieId":"test-usid","realm":"test"}'
}
)

Expand Down Expand Up @@ -189,7 +310,7 @@ describe('EinsteinAPI', () => {
'Content-Type': 'application/json',
'x-cq-client-id': 'test-id'
},
body: '{"cookieId":"test-usid"}'
body: '{"cookieId":"test-usid","realm":"test"}'
}
)

Expand Down
Loading