Skip to content

Commit 306bf87

Browse files
authored
Merge branch 'feature-passwordless-social-login' into W-17458039-handle-error-states
2 parents 7f2c142 + c83473d commit 306bf87

File tree

27 files changed

+298
-44
lines changed

27 files changed

+298
-44
lines changed

packages/commerce-sdk-react/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## v3.2.0-dev (Oct 29, 2024)
2+
- Allow cookies for ShopperLogin API [#2190](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2190
3+
- Fix refresh token TTL warning from firing when override is not provided [#2114](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2114)
24

35
- Update CacheUpdateMatrix for mergeBasket mutation [#2138](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2092)
46
- Clear auth state if session has been invalidated by a password change [#2092](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2092)

packages/commerce-sdk-react/src/auth/index.test.ts

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
import Auth, {AuthData} from './'
88
import {waitFor} from '@testing-library/react'
99
import jwt from 'jsonwebtoken'
10-
import {helpers, ShopperCustomersTypes} from 'commerce-sdk-isomorphic'
10+
import {helpers, ShopperCustomersTypes, ShopperLogin} from 'commerce-sdk-isomorphic'
1111
import * as utils from '../utils'
1212
import {SLAS_SECRET_PLACEHOLDER} from '../constant'
1313
import {ShopperLoginTypes} from 'commerce-sdk-isomorphic'
1414
import {
1515
DEFAULT_SLAS_REFRESH_TOKEN_REGISTERED_TTL,
1616
DEFAULT_SLAS_REFRESH_TOKEN_GUEST_TTL
1717
} from './index'
18-
import {RequireKeys} from '../hooks/types'
18+
import {ApiClientConfigParams, RequireKeys} from '../hooks/types'
1919

2020
const baseCustomer: RequireKeys<ShopperCustomersTypes.Customer, 'login'> = {
2121
customerId: 'customerId',
@@ -803,3 +803,87 @@ describe('Auth', () => {
803803
})
804804
})
805805
})
806+
807+
describe('Auth service sends credentials fetch option to the ShopperLogin API', () => {
808+
beforeEach(() => {
809+
jest.clearAllMocks()
810+
})
811+
812+
test('Adds fetch options with credentials when not defined in config', async () => {
813+
const auth = new Auth(config)
814+
await auth.loginGuestUser()
815+
816+
// Ensure the helper method was called
817+
expect(helpers.loginGuestUser).toHaveBeenCalled()
818+
expect(helpers.loginGuestUser).toHaveBeenCalledTimes(1)
819+
820+
// Check that the correct parameters were passed to the helper
821+
const callArguments = (helpers.loginGuestUser as jest.Mock).mock.calls[0]
822+
expect(callArguments).toBeDefined()
823+
expect(callArguments.length).toBeGreaterThan(0)
824+
825+
const shopperLogin: ShopperLogin<ApiClientConfigParams> = callArguments[0]
826+
expect(shopperLogin).toBeDefined()
827+
expect(shopperLogin.clientConfig).toBeDefined()
828+
expect(shopperLogin.clientConfig.fetchOptions).toBeDefined()
829+
830+
// Ensure fetch options include the expected credentials
831+
expect(shopperLogin.clientConfig.fetchOptions.credentials).toBe('same-origin')
832+
})
833+
834+
test('Does not override the credentials in fetch options if already exists', async () => {
835+
const configWithFetchOptions = {
836+
...config,
837+
fetchOptions: {
838+
credentials: 'include'
839+
}
840+
}
841+
const auth = new Auth(configWithFetchOptions)
842+
await auth.loginGuestUser()
843+
844+
// Ensure the helper method was called
845+
expect(helpers.loginGuestUser).toHaveBeenCalled()
846+
expect(helpers.loginGuestUser).toHaveBeenCalledTimes(1)
847+
848+
// Check that the correct parameters were passed to the helper
849+
const callArguments = (helpers.loginGuestUser as jest.Mock).mock.calls[0]
850+
expect(callArguments).toBeDefined()
851+
expect(callArguments.length).toBeGreaterThan(0)
852+
853+
const shopperLogin: ShopperLogin<ApiClientConfigParams> = callArguments[0]
854+
expect(shopperLogin).toBeDefined()
855+
expect(shopperLogin.clientConfig).toBeDefined()
856+
expect(shopperLogin.clientConfig.fetchOptions).toBeDefined()
857+
858+
// Ensure fetch options include the expected credentials
859+
expect(shopperLogin.clientConfig.fetchOptions.credentials).toBe('include')
860+
})
861+
862+
test('Adds credentials to the fetch options if it is missing', async () => {
863+
const configWithFetchOptions = {
864+
...config,
865+
fetchOptions: {
866+
cache: 'no-cache'
867+
}
868+
}
869+
const auth = new Auth(configWithFetchOptions)
870+
await auth.loginGuestUser()
871+
872+
// Ensure the helper method was called
873+
expect(helpers.loginGuestUser).toHaveBeenCalled()
874+
expect(helpers.loginGuestUser).toHaveBeenCalledTimes(1)
875+
876+
// Check that the correct parameters were passed to the helper
877+
const callArguments = (helpers.loginGuestUser as jest.Mock).mock.calls[0]
878+
expect(callArguments).toBeDefined()
879+
expect(callArguments.length).toBeGreaterThan(0)
880+
881+
const shopperLogin: ShopperLogin<ApiClientConfigParams> = callArguments[0]
882+
expect(shopperLogin).toBeDefined()
883+
expect(shopperLogin.clientConfig).toBeDefined()
884+
expect(shopperLogin.clientConfig.fetchOptions).toBeDefined()
885+
886+
// Ensure fetch options include the expected credentials
887+
expect(shopperLogin.clientConfig.fetchOptions.credentials).toBe('same-origin')
888+
})
889+
})

packages/commerce-sdk-react/src/auth/index.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,13 @@ class Auth {
244244
siteId: config.siteId
245245
},
246246
throwOnBadResponse: true,
247-
fetchOptions: config.fetchOptions
247+
// We need to set credentials to 'same-origin' to allow cookies to be set.
248+
// This is required as SLAS calls return a dwsid cookie for hybrid sites.
249+
// The dwsid value is then passed to the SCAPI as a header maintain the server affinity.
250+
fetchOptions: {
251+
credentials: 'same-origin',
252+
...config.fetchOptions
253+
}
248254
})
249255
this.shopperCustomersClient = new ShopperCustomers({
250256
proxy: config.proxy,
@@ -542,14 +548,16 @@ class Auth {
542548
responseValue: number | undefined,
543549
defaultValue: number
544550
): number {
545-
let value = overrideValue
546-
547-
if (typeof value !== 'number' || value <= 0 || value > defaultValue) {
551+
// Check if overrideValue is valid
552+
// if not, log warning and fall back to responseValue or defaultValue
553+
const isOverrideValid =
554+
typeof overrideValue === 'number' && overrideValue > 0 && overrideValue <= defaultValue
555+
if (!isOverrideValid && overrideValue !== undefined) {
548556
this.logWarning(SLAS_REFRESH_TOKEN_COOKIE_TTL_OVERRIDE_MSG)
549-
value = responseValue || defaultValue
550557
}
551558

552-
return value
559+
// Return the first valid value: overrideValue (if valid), responseValue, or defaultValue
560+
return isOverrideValid ? overrideValue : responseValue || defaultValue
553561
}
554562

555563
/**

packages/pwa-kit-react-sdk/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## v3.9.0-dev (Oct 29, 2024)
2+
- Fix the performance logging util to not round duration. [#2199](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2199)
3+
- Add RedirectWithStatus component, allowing finer grained control of rediriects and their status code [#2173](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2173)
24

35
## v3.8.0 (Oct 28, 2024)
46
- [Server Affinity] - Attach dwsid to SCAPI request headers [#2090](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2090)

packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export const render = async (req, res, next) => {
239239
}
240240

241241
if (redirectUrl) {
242-
res.redirect(302, redirectUrl)
242+
res.redirect(routerContext.status || 302, redirectUrl)
243243
} else {
244244
res.status(status).send(html)
245245
}

packages/pwa-kit-react-sdk/src/ssr/server/react-rendering.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ jest.mock('../universal/routes', () => {
5858
const React = require('react')
5959
const PropTypes = require('prop-types')
6060
const errors = require('../universal/errors')
61+
const RedirectWithStatus = require('../universal/components/redirect-with-status').default
6162
const {Redirect} = require('react-router-dom')
6263
const {Helmet} = require('react-helmet')
6364
const {useQuery} = require('@tanstack/react-query')
@@ -183,6 +184,16 @@ jest.mock('../universal/routes', () => {
183184
}
184185
}
185186

187+
class RedirectWithStatusPage extends React.Component {
188+
static getProps() {
189+
return Promise.resolve()
190+
}
191+
192+
render() {
193+
return <RedirectWithStatus to="/elsewhere/" status={301} />
194+
}
195+
}
196+
186197
class HelmetPage extends React.Component {
187198
static getProps() {
188199
return Promise.resolve()
@@ -305,6 +316,10 @@ jest.mock('../universal/routes', () => {
305316
path: '/redirect/',
306317
component: RedirectPage
307318
},
319+
{
320+
path: '/redirectWithStatus/',
321+
component: RedirectWithStatusPage
322+
},
308323
{
309324
path: '/init-sets-status/',
310325
component: InitSetsStatusPage
@@ -538,6 +553,13 @@ describe('The Node SSR Environment', () => {
538553
expect(res.statusCode).toBe(302)
539554
}
540555
},
556+
{
557+
description: `can redirect with HTTP 301 status`,
558+
req: {url: '/redirectWithStatus/'},
559+
assertions: (res) => {
560+
expect(res.statusCode).toBe(301)
561+
}
562+
},
541563
{
542564
description: `500 on unknown errors in getProps`,
543565
req: {url: '/unknown-error/'},
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2024, Salesforce, Inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import React from 'react'
9+
import {Redirect, withRouter} from 'react-router-dom'
10+
import PropTypes from 'prop-types'
11+
12+
/**
13+
* The `RedirectWithStatus` component is used to specify a different status code when redirecting via
14+
* the Redirect component.
15+
* The default redirect behavior when this component is not used is to set a 302 status.
16+
*
17+
* @param {number} status - The HTTP status code. Defaults to 302 if not specified
18+
* @param {object} staticContext - The router context
19+
* @param {string} to - The redirect's target path
20+
*/
21+
const RedirectWithStatus = ({status = 302, staticContext, ...props}) => {
22+
// Handle server-side rendering
23+
if (staticContext) {
24+
staticContext.status = status
25+
}
26+
27+
return <Redirect {...props} />
28+
}
29+
30+
RedirectWithStatus.propTypes = {
31+
status: PropTypes.number,
32+
staticContext: PropTypes.object,
33+
to: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
34+
}
35+
36+
export default withRouter(RedirectWithStatus)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2024, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import React from 'react'
9+
import {render} from '@testing-library/react'
10+
import {Router, StaticRouter, Route} from 'react-router-dom'
11+
import {createMemoryHistory} from 'history'
12+
import RedirectWithStatus from './index'
13+
14+
describe('RedirectWithStatus', () => {
15+
test('Redirects if no status or context is provided', () => {
16+
const targetUrl = '/target'
17+
const history = createMemoryHistory()
18+
history.push('/redirect')
19+
render(
20+
<Router history={history}>
21+
<Route path="/redirect">
22+
<RedirectWithStatus to={targetUrl} />
23+
</Route>
24+
</Router>
25+
)
26+
expect(history.location.pathname).toBe(targetUrl)
27+
})
28+
test('Redirect renders with correct status', async () => {
29+
const context = {}
30+
const status = 303
31+
const targetUrl = '/target'
32+
33+
render(
34+
<StaticRouter location="/redirect" context={context}>
35+
<Route path="/redirect">
36+
<RedirectWithStatus status={status} to={targetUrl} />
37+
</Route>
38+
</StaticRouter>
39+
)
40+
41+
expect(context.status).toBe(status)
42+
expect(context.url).toBe(targetUrl)
43+
})
44+
})

packages/pwa-kit-react-sdk/src/utils/performance.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default class PerformanceTimer {
5353
buildServerTimingHeader() {
5454
const header = this.metrics
5555
.map((metric) => {
56-
return `${metric.name};dur=${metric.duration}`
56+
return `${metric.name};dur=${metric.duration.toFixed(2)}`
5757
})
5858
.join(', ')
5959

@@ -117,7 +117,7 @@ export default class PerformanceTimer {
117117
if (startMark) {
118118
const measurement = {
119119
name,
120-
duration: (timestamp - startMark.timestamp).toFixed(2),
120+
duration: timestamp - startMark.timestamp,
121121
detail: options.detail
122122
}
123123
this.metrics.push(measurement)

packages/template-retail-react-app/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
## v5.1.0-dev (TBD)
22

3+
- [BUG] Fixed GET /shopper-context API calls being made without the usid [#2206](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2206)
4+
- Update test data references to 2024, and unify to 01/2040 [#2196](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2197)
5+
- Fixed failing checkout tests [#2195](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2195)
36
- [BUG] Fixed "getCheckboxProps is not a function" when rendering checkout page in generated app.[#2140](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2140)
4-
- Replace transfer basket call with merge basket on checkout [#2138])(https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2138)
7+
- Replace transfer basket call with merge basket on checkout [#2138](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2138)
58

69
### Accessibility Improvements
710
- [a11y] Fix LinkList component to follow a11y practise [#2098])(https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2098)

0 commit comments

Comments
 (0)