Skip to content

Commit

Permalink
Update to version v3.3.1 (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamyarz-aws authored Oct 2, 2024
1 parent 8a3d6ca commit 976c0c8
Show file tree
Hide file tree
Showing 48 changed files with 2,069 additions and 1,002 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.3.1] - 2024-10-02

### Security

- `rollup` to mitigate [CVE-2024-4067](https://github.com/advisories/GHSA-952p-6rrq-rcjv)

### Fixed

- Fixed API response to 404 NOT_FOUND from 400 BAD_REQUEST for when retrieving/deleting an invalid test

## [3.3.0] - 2024-09-16

### Added
Expand Down
11 changes: 11 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,33 @@ This software includes third party software subject to the following copyrights:
@aws-amplify/ui-react under the Apache License 2.0
@aws-cdk/assert under the Apache License 2.0
@aws-solutions-constructs/aws-cloudfront-s3 under the Apache License 2.0
@aws-solutions-constructs/aws-eventbridge-lambda under the Apache License 2.0
@aws-solutions-constructs/aws-lambda-sqs-lambda under the Apache License 2.0
@aws-sdk/client-s3 under the Apache License 2.0
@aws-sdk/client-cloudwatch under the Apache License 2.0
@aws-sdk/client-cloudwatch-logs under the Apache License 2.0
@aws-sdk/client-sqs under the Apache License 2.0
@types/jest under the Massachusetts Institute of Technology (MIT) License
@types/node under the Massachusetts Institute of Technology (MIT) License
@types/aws-lambda under the Massachusetts Institute of Technology (MIT) License
@typescript-eslint/eslint-plugin under the Massachusetts Institute of Technology (MIT) license
@typescript-eslint/parser under the BSD 2-Clause license
aws-amplify under the Apache License Version 2.0
aws-cdk under the Apache License Version 2.0
aws-cdk-lib under the Apache License 2.0
aws-sdk under the Apache License Version 2.0
aws4-axios under the Massachusetts Institute of Technology (MIT) license
axios under the Massachusetts Institute of Technology (MIT) license
axios-mock-adapter under the Massachusetts Institute of Technology (MIT) license
ajv under the Massachusetts Institute of Technology (MIT) license
bootstrap under the Massachusetts Institute of Technology (MIT) license
bootstrap-icons under the Massachusetts Institute of Technology (MIT) license
brace under the Massachusetts Institute of Technology (MIT) license
chart.js under the Massachusetts Institute of Technology (MIT) license
chartjs-adapter-date-fns under the Massachusetts Institute of Technology (MIT) license
constructs under the Apache License 2.0
cypress under the Massachusetts Institute of Technology (MIT) license
esbuild under the Massachusetts Institute of Technology (MIT) license
eslint under the Massachusetts Institute of Technology (MIT) license
eslint-config-prettier under the Massachusetts Institute of Technology (MIT) license
eslint-config-standard under the Massachusetts Institute of Technology (MIT) license
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.0
3.3.1
21 changes: 13 additions & 8 deletions source/api-services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ class APIHandler {
constructor(resource, method) {
this.resource = resource;
this.method = method;
this.errorMsg = `Method: ${method} not supported for this resource: ${resource} `;
this.errorMsg = new scenarios.ErrorException(
"METHOD_NOT_ALLOWED",
`Method: ${method} not supported for this resource: ${resource}`,
scenarios.StatusCodes.NOT_ALLOWED
);
}
async getRegions() {
let data = { regions: await scenarios.getAllRegionConfigs() };
Expand All @@ -21,7 +25,7 @@ class APIHandler {
// Handle the /regions endpoint
async handleRegions() {
if (this.method === "GET") return this.getRegions();
throw new Error(this.errorMsg);
throw this.errorMsg;
}

// Handle the /scenarios endpoint
Expand Down Expand Up @@ -63,7 +67,7 @@ class APIHandler {
}
return data;
default:
throw new Error(this.errorMsg);
throw this.errorMsg;
}
}

Expand All @@ -77,22 +81,23 @@ class APIHandler {
case "DELETE":
return scenarios.deleteTest(testId, functionName);
default:
throw new Error(this.errorMsg);
throw this.errorMsg;
}
}

// Handle the /tasks endpoint
async handleTasks() {
if (this.method === "GET") return scenarios.listTasks();
throw new Error(this.errorMsg);
throw this.errorMsg;
}

// Handle the /vCPUDetails endpoint
async handleVCPUDetails() {
if (this.method === "GET") return scenarios.getAccountFargatevCPUDetails();
throw new Error(this.errMsg);
throw this.errorMsg;
}
}

// Helper function to handle API response
const createResponse = (data, statusCode) => ({
headers: {
Expand Down Expand Up @@ -138,13 +143,13 @@ exports.handler = async (event, context) => {
data = await apiHandler.handleVCPUDetails();
break;
default:
throw new Error(apiHandler.errorMsg);
throw apiHandler.errorMsg;
}

response = createResponse(data, 200);
} catch (err) {
console.error(err);
response = createResponse(err.toString(), 400);
response = createResponse(err.toString(), err.statusCode || scenarios.StatusCodes.BAD_REQUEST);
}

console.log(response);
Expand Down
53 changes: 51 additions & 2 deletions source/api-services/lib/scenarios/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,28 @@ const stepFunctions = new AWS.StepFunctions(options);
const cloudwatchevents = new AWS.CloudWatchEvents(options);
const cloudformation = new AWS.CloudFormation(options);

const StatusCodes = {
OK: 200,
BAD_REQUEST: 400,
FORBIDDEN: 403,
NOT_FOUND: 404,
NOT_ALLOWED: 405,
REQUEST_TOO_LONG: 413,
INTERNAL_SERVER_ERROR: 500,
TIMEOUT: 503,
};

/**
* Class to throw errors
* @param {string} code
* @param {string} errMsg
*/
class ErrorException extends Error {
constructor(code, errMsg) {
super(errMsg);
constructor(code, errMsg, statusCode = StatusCodes.BAD_REQUEST) {
super(statusCode, code, errMsg);
this.code = code;
this.message = errMsg;
this.statusCode = statusCode;
}
toString() {
return `${this.code}: ${this.message}`;
Expand Down Expand Up @@ -143,6 +155,7 @@ const getRegionInfraConfigs = async (testRegion) => {
const getTestAndRegionConfigs = async (testId) => {
try {
const testEntry = await getTestEntry(testId);
if (!testEntry) throw new ErrorException("TEST_NOT_FOUND", `testId '${testId}' not found`, StatusCodes.NOT_FOUND);
if (testEntry.testTaskConfigs) {
for (let testRegionSettings of testEntry.testTaskConfigs) {
const regionInfraConfig = await getRegionInfraConfigs(testRegionSettings.region);
Expand Down Expand Up @@ -272,6 +285,8 @@ const convertLinuxCronToAwsCron = (linuxCron, cronExpiryDate) => {
const checkEnoughIntervalDiff = (cronValue, cronExpiryDate, holdFor, rampUp, testTaskConfigs) => {
if (!holdFor || !rampUp) return "";
let cronExpiry = new Date(cronExpiryDate);
const parts = cronValue.trim().split(" ");
if (parts.length !== 5) throw new ErrorException("Invalid Linux cron expression", "Expected format: 0 * * * *");

let cronInterval;
try {
Expand Down Expand Up @@ -396,6 +411,26 @@ const removeRules = async (testId, functionName, recurrence) => {
}
};

const isValidTimeString = (timeString) => {
const timeRegex = /^([01]\d|2[0-3]):([0-5]\d)$/;
if (!timeRegex.test(timeString))
throw new ErrorException("InvalidParameter", "Invalid time format. Expected format: HH:MM");
};

const isValidDateString = (dateString) => {
// Check if the dateString is in the format YYYY-MM-DD
const dateRegex = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;

if (!dateRegex.test(dateString))
throw new ErrorException("InvalidParameter", "Invalid date format. Expected format: YYYY-MM-DD");
};

const isValidDate = (date) => {
const today = new Date();
today.setHours(0, 0, 0, 0);

if (date < today) throw new ErrorException("InvalidParameter", "Date cannot be in the past");
};
/**
* Schedules test and returns a consolidated list of test scenarios
* @param {object} event test event information
Expand Down Expand Up @@ -459,7 +494,10 @@ const scheduleTest = async (event, context) => {
config.scheduleTime = scheduleTime;
config.scheduleDate = scheduleDate;
} else {
isValidTimeString(scheduleTime);
isValidDateString(scheduleDate);
createRun = new Date(year, parseInt(month, 10) - 1, day, hour, minute);
isValidDate(createRun);
} // Schedule for 1 min prior to account for time it takes to create rule
// getMonth() returns Jan with index Zero that is why months need a +1
// refrence https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMonth
Expand Down Expand Up @@ -1377,6 +1415,15 @@ const cancelTest = async (testId) => {

try {
// Get test and regional infrastructure configuration
const listTestsRes = await listTests();
const allTests = listTestsRes.Items;

// Check if the testId exists in the list of tests
const testExists = allTests.some((test) => test.testId === testId);
if (!testExists) {
throw new ErrorException("TEST_NOT_FOUND", `testId '${testId}' not found`, StatusCodes.NOT_FOUND);
}

const testAndRegionalInfraConfigs = await getTestAndRegionConfigs(testId);
if (testAndRegionalInfraConfigs.testTaskConfigs) {
for (const regionalConfig of testAndRegionalInfraConfigs.testTaskConfigs) {
Expand Down Expand Up @@ -1689,4 +1736,6 @@ module.exports = {
getCFUrl: getCFUrl,
getAccountFargatevCPUDetails: getAccountFargatevCPUDetails,
getTestDurationSeconds: getTestDurationSeconds,
ErrorException: ErrorException,
StatusCodes: StatusCodes,
};
6 changes: 6 additions & 0 deletions source/api-services/lib/scenarios/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,12 @@ describe("#SCENARIOS API:: ", () => {
});

it('should return SUCCESS when "CANCELTEST" finds running tasks and returns success', async () => {
mockDynamoDB.mockImplementationOnce(() => ({
promise() {
// scan
return Promise.resolve(listData);
},
}));
mockDynamoDB.mockImplementationOnce(() => ({
promise() {
// get
Expand Down
4 changes: 2 additions & 2 deletions source/api-services/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion source/api-services/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "api-services",
"version": "3.3.0",
"version": "3.3.1",
"description": "REST API micro services",
"repository": {
"type": "git",
Expand Down
9 changes: 5 additions & 4 deletions source/console/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion source/console/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "distributed-load-testing-on-aws-ui",
"version": "3.3.0",
"version": "3.3.1",
"private": true,
"license": "Apache-2.0",
"author": {
Expand Down
9 changes: 2 additions & 7 deletions source/console/src/Components/Create/Create.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ import {
NavLink,
TabContent,
TabPane,
Modal,
ModalBody,
ModalFooter,
} from "reactstrap";
import "brace/theme/github";
import { generateUniqueId } from "solution-utils";
Expand Down Expand Up @@ -335,7 +332,7 @@ class Create extends React.Component {
}
try {
this.setState({ isUploading: true });
await uploadData({ key: `test-scenarios/${testType}/${filename}`, data: file }).result;
await uploadData({ path: `public/test-scenarios/${testType}/${filename}`, data: file }).result;
console.log("Script uploaded successfully");
} catch (error) {
console.error("Error", error);
Expand Down Expand Up @@ -766,9 +763,7 @@ class Create extends React.Component {

const nextSixRuns = this.nextSixRuns();
let fields = JSON.parse(JSON.stringify(interval.fields));
if (fields.minute.length !== 1 && nextSixRuns && nextSixRuns.length > 1) return true;

return false;
return fields.minute.length !== 1 && nextSixRuns && nextSixRuns.length > 1;
};

checkEnoughIntervalDiff = () => {
Expand Down
2 changes: 1 addition & 1 deletion source/console/src/Components/Create/Create.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ describe("Functions Testing", () => {
let createInstance = new Create(commonProps);
createInstance.state = { file: { type: initialFileType === "zip" ? "application/zip" : "text/plain" } };
await createInstance.uploadFileToScenarioBucket("test");
expect(uploadData).toHaveBeenCalledTimes(1);
expect(uploadData).toHaveBeenCalledTimes(1); // NOSONAR
});
});
});
2 changes: 1 addition & 1 deletion source/console/src/Components/Details/Details.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class Details extends React.Component {
const extension = scriptExtensions[testType];

let filename = this.state.data.fileType === "zip" ? `${testId}.zip` : `${testId}.${extension}`;
const url = await getUrl({ key: `test-scenarios/${testType}/${filename}` });
const url = await getUrl({ path: `public/test-scenarios/${testType}/${filename}` });
window.open(url.url, "_blank");
} catch (error) {
console.error("Error", error);
Expand Down
2 changes: 1 addition & 1 deletion source/console/src/Components/Results/Results.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class Results extends React.Component {
*/
retrieveImage = async (metricS3ImageLocation) => {
try {
const { body } = await downloadData({ key: metricS3ImageLocation }).result;
const { body } = await downloadData({ path: `public/${metricS3ImageLocation}` }).result;
const imageBodyText = await body.text();
this.setState({ metricImage: imageBodyText });
} catch (error) {
Expand Down
4 changes: 2 additions & 2 deletions source/custom-resource/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion source/custom-resource/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "custom-resource",
"version": "3.3.0",
"version": "3.3.1",
"description": "cfn custom resources for distributed load testing on AWS workflow",
"repository": {
"type": "git",
Expand Down
Loading

0 comments on commit 976c0c8

Please sign in to comment.