Skip to content

Commit

Permalink
Merge branch 'develop' into fix-source-maps-2
Browse files Browse the repository at this point in the history
Signed-off-by: Ben Chypak <[email protected]>
  • Loading branch information
bendvc authored Nov 22, 2023
2 parents 8fbf5c9 + e9f1534 commit 31e7237
Show file tree
Hide file tree
Showing 23 changed files with 2,406 additions and 107 deletions.
37 changes: 36 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,41 @@ module.exports = {
' * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause',
' '
]
],
// Sometimes VS Code will automatically add missing imports, but will do so via
// @salesforce/retail-react-app/node_modules/ instead of a normal import. It technically
// works inside the monorepo, but won't necessarily work in customer projects, so we want to
// avoid the pattern. (Also just because it's weird.)
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@salesforce/retail-react-app/node_modules/*'],
message: 'Did your IDE auto-complete this import? Avoid /node_modules/ and import the package directly.'
}
]
}
]
}
},
// For an import in a file to be replaced via template extensibility, the import must use the
// full package name, rather than relative imports, i.e. all retail react app imports must use
// "@salesforce/retail-react-app/" rather than relative imports.
overrides: [
{
files: ['packages/template-retail-react-app/**'],
plugins: ['no-relative-import-paths'],
rules: {
// https://github.com/MelvinVermeer/eslint-plugin-no-relative-import-paths
'no-relative-import-paths/no-relative-import-paths': [
'error',
{
allowSameFolder: false,
rootDir: 'packages/template-retail-react-app',
prefix: '@salesforce/retail-react-app'
}
]
}
}
]
}
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:
TARGET: production

- name: Push Bundle to MRT (Commerce SDK React)
if: env.IS_NOT_FORK == 'true' && env.IS_MRT_NODE == 'true' && env.DEVELOPMENT == 'true'
if: env.IS_NOT_FORK == 'true' && env.IS_MRT_NODE == 'true' && env.DEVELOP == 'true'
uses: "./.github/actions/push_to_mrt"
with:
CWD: "./packages/test-commerce-sdk-react"
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@playwright/test": "^1.36.0",
"commander": "^9.5.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-no-relative-import-paths": "^1.5.3",
"jsdom": "^22.1.0",
"lerna": "^6.6.1",
"semver": "^7.5.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = {
// Determine where the siteRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none'
// site: 'none',
// Determine where the localeRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none'
locale: 'none',
locale: 'none'
// This boolean value dictates whether or not default site or locale values are shown in the url. Defaults to: false
// showDefaults: true
},
Expand Down Expand Up @@ -78,4 +78,4 @@ module.exports = {
}{{/if}}
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ module.exports = [
]
}
}
]
]
1 change: 0 additions & 1 deletion packages/pwa-kit-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@
"@types/node": "~16.0.3",
"@types/node-fetch": "~2.6.3",
"@types/validator": "~13.7.14",
"eslint-plugin-no-relative-import-paths": "^1.5.2",
"internal-lib-build": "3.3.0-dev",
"nock": "^13.3.0",
"nodemon": "^2.0.22",
Expand Down
88 changes: 88 additions & 0 deletions packages/template-mrt-reference-app/app/isolation-actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2023, Salesforce, Inc.
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
/* eslint-disable @typescript-eslint/no-var-requires */

const {LambdaClient, InvokeCommand} = require('@aws-sdk/client-lambda')
const {S3Client, GetObjectCommand} = require('@aws-sdk/client-s3')
const {CloudWatchLogsClient, PutLogEventsCommand} = require('@aws-sdk/client-cloudwatch-logs')

export const isolationOriginLambdaTest = async (input) => {
const client = new LambdaClient()
try {
await client.send(new InvokeCommand(input))
} catch (e) {
if (e.name === 'AccessDeniedException') {
return true
}
console.error(e)
}
console.error('Lambda isolation test failed!')
return false
}

export const isolationS3Test = async (input) => {
const client = new S3Client({region: 'us-east-1'})
try {
await client.send(new GetObjectCommand(input))
} catch (e) {
if (e.name === 'AccessDenied') {
return true
}
console.error(e)
}
console.error('S3 isolation test failed!')
return false
}

export const isolationLogsTest = async (input) => {
const client = new CloudWatchLogsClient()
try {
const inputValues = {
...input,
logEvents: [
{
timestamp: Date.now(),
message: 'This is plastic'
}
]
}
await client.send(new PutLogEventsCommand(inputValues))
} catch (e) {
if (e.name === 'AccessDeniedException') {
return true
}
console.error(e)
}
console.error('Log group isolation test failed!')
return false
}

export const executeIsolationTests = async (params) => {
const tests = [
{name: 'origin', keys: ['FunctionName'], fn: isolationOriginLambdaTest},
{name: 'storage', keys: ['Bucket', 'Key'], fn: isolationS3Test},
{name: 'logs', keys: ['logGroupName', 'logStreamName'], fn: isolationLogsTest}
]
let results = {}
for (const test of tests) {
const {keys, fn, name} = test
const input = Object.keys(params)
.filter((key) => keys.includes(key))
.reduce((obj, key) => {
obj[key] = params[key]
return obj
}, {})
results[name] = await fn(input)
}
return results
}

export const isolationTests = async (req, res) => {
const results = await executeIsolationTests(req.query)
res.header('Content-Type', 'application/json')
res.send(JSON.stringify(results, null, 4))
}
2 changes: 2 additions & 0 deletions packages/template-mrt-reference-app/app/ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const {getRuntime} = require('@salesforce/pwa-kit-runtime/ssr/server/express')
const pkg = require('../package.json')
const basicAuth = require('express-basic-auth')
const fetch = require('cross-fetch')
const {isolationTests} = require('./isolation-actions')

/**
* Custom error class
Expand Down Expand Up @@ -249,6 +250,7 @@ const {handler, app, server} = runtime.createHandler(options, (app) => {
app.get('/tls', tlsVersionTest)
app.get('/cache', cacheTest)
app.get('/cookie', cookieTest)
app.get('/isolation', isolationTests)

// Add a /auth/logout path that will always send a 401 (to allow clearing
// of browser credentials)
Expand Down
60 changes: 57 additions & 3 deletions packages/template-mrt-reference-app/app/ssr.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,29 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
// Tests cannot run if this require is converted to an import
// eslint-disable-next-line @typescript-eslint/no-var-requires
/* eslint-disable @typescript-eslint/no-var-requires */
const request = require('supertest')
const {LambdaClient, InvokeCommand} = require('@aws-sdk/client-lambda')
const {S3Client, GetObjectCommand} = require('@aws-sdk/client-s3')
const {
CloudWatchLogsClient,
PutLogEventsCommand,
AccessDeniedException
} = require('@aws-sdk/client-cloudwatch-logs')
const {mockClient} = require('aws-sdk-client-mock')
const {ServiceException} = require('@smithy/smithy-client')

class AccessDenied extends ServiceException {
constructor(options) {
super({...options, name: 'AccessDenied'})
}
}

describe('server', () => {
let originalEnv, app, server
const lambdaMock = mockClient(LambdaClient)
const s3Mock = mockClient(S3Client)
const logsMock = mockClient(CloudWatchLogsClient)
beforeEach(() => {
originalEnv = process.env
process.env = Object.assign({}, process.env, {
Expand All @@ -21,21 +39,26 @@ describe('server', () => {
MOBIFY_PROPERTY_ID: 'test',
AWS_LAMBDA_FUNCTION_NAME: 'pretend-to-be-remote'
})
// eslint-disable-next-line @typescript-eslint/no-var-requires

const ssr = require('./ssr')
app = ssr.app
server = ssr.server
lambdaMock.reset()
s3Mock.reset()
logsMock.reset()
})
afterEach(() => {
process.env = originalEnv
server.close()
jest.restoreAllMocks()
})
test.each([
['/', 200, 'application/json; charset=utf-8'],
['/tls', 200, 'application/json; charset=utf-8'],
['/exception', 500, 'text/html; charset=utf-8'],
['/cache', 200, 'application/json; charset=utf-8'],
['/cookie', 200, 'application/json; charset=utf-8']
['/cookie', 200, 'application/json; charset=utf-8'],
['/isolation', 200, 'application/json; charset=utf-8']
])('Path %p should render correctly', (path, expectedStatus, expectedContentType) => {
return request(app)
.get(path)
Expand All @@ -52,4 +75,35 @@ describe('server', () => {
.get('/cookie?name=test-cookie&value=test-value')
.expect('set-cookie', 'test-cookie=test-value; Path=/')
})

test('Path "/isolation" succeeds', async () => {
jest.spyOn(console, 'error')
lambdaMock.on(InvokeCommand).rejects(new AccessDeniedException())
s3Mock.on(GetObjectCommand).rejects(new AccessDenied())
logsMock.on(PutLogEventsCommand).rejects(new AccessDeniedException())
const params = `FunctionName=name&Bucket=bucket&Key=key&logGroupName=lgName&logStreamName=lsName`
const response = await request(app).get(`/isolation?${params}`)
expect(response.body.origin).toBe(true)
expect(response.body.storage).toBe(true)
expect(response.body.logs).toBe(true)
})

test('Path "/isolation" fails', async () => {
jest.spyOn(console, 'error')
lambdaMock.on(InvokeCommand).resolves()
s3Mock.on(GetObjectCommand).resolves()
logsMock.on(PutLogEventsCommand).resolves()
const params = `FunctionName=name&Bucket=bucket&Key=key&logGroupName=lgName&logStreamName=lsName`
const response = await request(app).get(`/isolation?${params}`)
expect(response.body.origin).toBe(false)
expect(response.body.storage).toBe(false)
expect(response.body.logs).toBe(false)
const errors = [
'Lambda isolation test failed!',
'S3 isolation test failed!',
'Log group isolation test failed!'
]
const calls = console.error.mock.calls.map((call) => call[0])
expect(errors.some((error) => calls.includes(error))).toBe(true)
})
})
Loading

0 comments on commit 31e7237

Please sign in to comment.