From 5c431b22e41ec45938b4b52c948a5868ccc30bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mark=20Lis=C3=A9?= Date: Fri, 22 Apr 2022 13:47:43 -0700 Subject: [PATCH] NOBUG: Test suites completed. --- __tests__/global/data.json | 38 ++++------- __tests__/global/setup.js | 2 +- __tests__/global/teardown.js | 2 +- __tests__/park.test.js | 52 +++++++++++++++ __tests__/subarea.test.js | 102 +++++++++++++++++++++++++++- lambda/dynamoUtil.js | 126 +---------------------------------- lambda/park/GET/index.js | 8 ++- lambda/park/POST/index.js | 35 ++++++---- lambda/readConfig/index.js | 2 +- lambda/subarea/GET/index.js | 12 ++-- lambda/subarea/POST/index.js | 84 +++++++++++++---------- tools/convertParksSheet.js | 4 +- 12 files changed, 254 insertions(+), 213 deletions(-) diff --git a/__tests__/global/data.json b/__tests__/global/data.json index a4ff829..5c4bd36 100644 --- a/__tests__/global/data.json +++ b/__tests__/global/data.json @@ -35,14 +35,10 @@ { "pk": "park::0041", "sk": "Maple Bay", - "activities": { - "wrapperName": "Set", - "values": [ - "Boating", - "Day Use" - ], - "type": "String" - }, + "activities": [ + "Boating", + "Day Use" + ], "parkName": "Cultus Lake Park", "orcs": 41, "subAreaName": "Maple Bay" @@ -50,13 +46,9 @@ { "pk": "park::0041", "sk": "Teapot Hill", - "activities": { - "wrapperName": "Set", - "values": [ - "Day Use" - ], - "type": "String" - }, + "activities": [ + "Day Use" + ], "parkName": "Cultus Lake Park", "orcs": 41, "subAreaName": "Teapot Hill" @@ -64,13 +56,9 @@ { "pk": "park::6161", "sk": "Holt Creek", - "activities": { - "wrapperName": "Set", - "values": [ - "Day Use" - ], - "type": "String" - }, + "activities": [ + "Day Use" + ], "orcs": 41, "subAreaName": "Holt Creek" } @@ -125,7 +113,8 @@ "busModifier": 40 }, "orcs": "0041", - "subAreaName": "Maple Bay" + "subAreaName": "Maple Bay", + "activity": "Day Use" }, { "pk": "0041::Maple Bay::Boating", @@ -136,7 +125,8 @@ "busModifier": 40 }, "subAreaName": "Maple Bay", - "orcs": "0041" + "orcs": "0041", + "activity": "Day Use" } ] } \ No newline at end of file diff --git a/__tests__/global/setup.js b/__tests__/global/setup.js index 783f842..5922e5b 100644 --- a/__tests__/global/setup.js +++ b/__tests__/global/setup.js @@ -41,6 +41,6 @@ module.exports = async () => { }) .promise(); } catch (err) { - console.log(err); + console.error(err); } }; diff --git a/__tests__/global/teardown.js b/__tests__/global/teardown.js index 81e5a23..79846b3 100644 --- a/__tests__/global/teardown.js +++ b/__tests__/global/teardown.js @@ -15,6 +15,6 @@ module.exports = async () => { }) .promise(); } catch (err) { - console.log(err); + console.error(err); } }; diff --git a/__tests__/park.test.js b/__tests__/park.test.js index c6ba49d..47e44a7 100644 --- a/__tests__/park.test.js +++ b/__tests__/park.test.js @@ -4,6 +4,7 @@ const { REGION, ENDPOINT, TABLE_NAME } = require('./global/settings'); const { PARKSLIST, SUBAREAS, SUBAREA_INFORMATION } = require('./global/data.json'); const parkGET = require('../lambda/park/GET/index'); +const parkPOST = require('../lambda/park/POST/index'); async function setupDb() { new AWS.DynamoDB({ @@ -52,4 +53,55 @@ describe('Pass Succeeds', () => { statusCode: 200 }); }); + + test('Handler - 200 Receive park specific information', async () => { + let specificSubAreas = []; + for(const area of SUBAREAS) { + if (area.pk === "park::0041") { + specificSubAreas.push(area); + } + } + const response = await parkGET.handler({ + queryStringParameters: { + orcs: PARKSLIST[0].sk, + subAreaName: specificSubAreas[0].subAreaName + } + }, null); + + const body = JSON.parse(response.body); + + expect(body.data[0].subAreaName).toMatch(body.data[0].subAreaName); + }); + + test('Handler - 400 GET Bad Request', async () => { + const response = await parkGET.handler({ + queryStringParameters: { + badParam: "oops" + } + }, null); + + expect(response.statusCode).toBe(400); + }); + + test('Handler - 400 POST Bad Request', async () => { + const response = await parkPOST.handler({ + body: JSON.stringify({ + badParam: "{xxxxxx}" + }) + }, null); + + expect(response.statusCode).toBe(400); + }); + + test('Handler - 200 POST Park', async () => { + const response = await parkPOST.handler({ + body: JSON.stringify({ + orcs: '0000', // Not in the data.json set + someconfig: "test" + }) + }, null); + + expect(response.statusCode).toBe(200); + }); + }); diff --git a/__tests__/subarea.test.js b/__tests__/subarea.test.js index 9fa9c14..f019a46 100644 --- a/__tests__/subarea.test.js +++ b/__tests__/subarea.test.js @@ -4,6 +4,7 @@ const { REGION, ENDPOINT, TABLE_NAME } = require('./global/settings'); const { PARKSLIST, SUBAREAS, CONFIG_ENTRIES, SUBAREA_ENTRIES } = require('./global/data.json'); const subareaGET = require('../lambda/subarea/GET/index'); +const subareaPOST = require('../lambda/subarea/POST/index'); async function setupDb() { new AWS.DynamoDB({ @@ -44,7 +45,7 @@ describe('Subarea Test', () => { return await setupDb(); }); - test('Handler - 200 Get specific activity entry', async () => { + test('Handler - 200 GET specific activity entry', async () => { const obj = await subareaGET.handler( { queryStringParameters: { @@ -56,4 +57,103 @@ describe('Subarea Test', () => { }, null); expect(JSON.parse(obj.body)).toMatchObject(SUBAREA_ENTRIES[0]); }); + + test('Handler - 400 GET Bad Request', async () => { + const response = await subareaGET.handler({ + queryStringParameters: { + badParam: "oops" + } + }, null); + + expect(response.statusCode).toBe(400); + }); + + test('Handler - 200 POST handle Activity', async () => { + const response = await subareaPOST.handler( + { + queryStringParameters: { + type: "activity" + }, + body: JSON.stringify({ + orcs: SUBAREA_ENTRIES[0].orcs, + subAreaName: SUBAREA_ENTRIES[0].subAreaName, + activity: SUBAREA_ENTRIES[0].pk.split("::")[2], + date: "202201" + }) + }, null); + expect(response.statusCode).toBe(200); + }); + + test('Handler - 200 POST handle Config', async () => { + const response = await subareaPOST.handler( + { + queryStringParameters: { + type: "config" + }, + body: JSON.stringify(CONFIG_ENTRIES[0]) + }, null); + expect(response.statusCode).toBe(200); + }); + + test('Handler - 400 POST handle Activity', async () => { + const response = await subareaPOST.handler( + { + queryStringParameters: { + type: "activity" + }, + body: JSON.stringify({ + orcs: SUBAREA_ENTRIES[0].orcs, + }) + }, null); + expect(response.statusCode).toBe(400); + }); + + test('Handler - 400 POST handle Config', async () => { + const response = await subareaPOST.handler( + { + queryStringParameters: { + type: "config" + }, + body: JSON.stringify({ + orcs: SUBAREA_ENTRIES[0].orcs, + }) + }, null); + expect(response.statusCode).toBe(400); + }); + + test('Handler - 400 POST handle Activity', async () => { + const response = await subareaPOST.handler( + { + queryStringParameters: { + type: "activity" + } + }, null); + expect(response.statusCode).toBe(400); + }); + + test('Handler - 400 POST handle Activity date', async () => { + const response = await subareaPOST.handler( + { + queryStringParameters: { + type: "activity" + }, + body: JSON.stringify({ + orcs: SUBAREA_ENTRIES[0].orcs, + subAreaName: SUBAREA_ENTRIES[0].subAreaName, + activity: SUBAREA_ENTRIES[0].pk.split("::")[2], + date: "2022" // Invalid + }) + }, null); + expect(response.statusCode).toBe(400); + }); + + test('Handler - 400 POST Bad Request', async () => { + const response = await subareaPOST.handler({ + body: { + badParam: "{xxxxxx}" + } + }, null); + + expect(response.statusCode).toBe(400); + }); }); diff --git a/lambda/dynamoUtil.js b/lambda/dynamoUtil.js index 13bdde8..7d209f4 100644 --- a/lambda/dynamoUtil.js +++ b/lambda/dynamoUtil.js @@ -27,25 +27,6 @@ const dynamodb = new AWS.DynamoDB(options); exports.dynamodb = new AWS.DynamoDB(); -async function setStatus(passes, status) { - for (let i = 0; i < passes.length; i++) { - let updateParams = { - Key: { - pk: { S: passes[i].pk }, - sk: { S: passes[i].sk } - }, - ExpressionAttributeValues: { - ':statusValue': { S: status } - }, - UpdateExpression: 'SET passStatus = :statusValue', - ReturnValues: 'ALL_NEW', - TableName: TABLE_NAME - }; - - const res = await dynamodb.updateItem(updateParams).promise(); - } -} - async function runQuery(query, paginated = false) { console.log('query:', query); const data = await dynamodb.query(query).promise(); @@ -64,104 +45,6 @@ async function runQuery(query, paginated = false) { } } -async function runScan(query, paginated = false) { - console.log('query:', query); - const data = await dynamodb.scan(query).promise(); - // console.log('data:', data); - var unMarshalled = data.Items.map(item => { - return AWS.DynamoDB.Converter.unmarshall(item); - }); - // console.log(unMarshalled); - if (paginated) { - return { - LastEvaluatedKey: data.LastEvaluatedKey, - data: unMarshalled - }; - } else { - return unMarshalled; - } -} - -async function getConfig() { - const configQuery = { - TableName: TABLE_NAME, - KeyConditionExpression: 'pk = :pk AND sk = :sk', - ExpressionAttributeValues: { - ':pk': { S: 'config' }, - ':sk': { S: 'config' } - } - }; - return await runQuery(configQuery); -} - -async function getParks() { - const parksQuery = { - TableName: TABLE_NAME, - KeyConditionExpression: 'pk = :pk', - ExpressionAttributeValues: { - ':pk': { S: 'park' } - } - }; - return await runQuery(parksQuery); -} - -async function getFacilities(parkName) { - const facilitiesQuery = { - TableName: TABLE_NAME, - KeyConditionExpression: 'pk = :pk', - ExpressionAttributeValues: { - ':pk': { S: `facility::${parkName}` } - } - }; - return await runQuery(facilitiesQuery); -} - -const expressionBuilder = function (operator, existingExpression, newFilterExpression) { - if (existingExpression) { - return ` ${operator} ${newFilterExpression}`; - } else { - return newFilterExpression; - } -}; - -const getPassesByStatus = async function(status, filterExpression = undefined) { - console.log(`Loading passes`, filterExpression); - - const passesQuery = { - TableName: TABLE_NAME, - KeyConditionExpression: 'passStatus = :activeStatus', - IndexName: 'passStatus-index' - }; - - if (filterExpression && filterExpression.FilterExpression) { - passesQuery.FilterExpression = filterExpression.FilterExpression; - } - if (filterExpression && filterExpression.ExpressionAttributeValues) { - passesQuery.ExpressionAttributeValues = filterExpression.ExpressionAttributeValues; - } - if (filterExpression && filterExpression.ExpressionAttributeNames) { - passesQuery.ExpressionAttributeNames = filterExpression.ExpressionAttributeNames; - } - - if (!passesQuery.ExpressionAttributeValues) { - passesQuery.ExpressionAttributeValues = {}; - } - passesQuery.ExpressionAttributeValues[':activeStatus'] = { S: status }; - - console.log("Query:", passesQuery); - - // Grab all the results, don't skip any. - let results = []; - let passData; - do { - passData = await runQuery(passesQuery, true); - passData.data.forEach((item) => results.push(item)); - passesQuery.ExclusiveStartKey = passData.LastEvaluatedKey; - } while(typeof passData.LastEvaluatedKey !== "undefined"); - - return results; -} - module.exports = { ACTIVE_STATUS, RESERVED_STATUS, @@ -175,12 +58,5 @@ module.exports = { timeZone, TABLE_NAME, dynamodb, - setStatus, - runQuery, - runScan, - getConfig, - getParks, - getFacilities, - getPassesByStatus, - expressionBuilder + runQuery }; diff --git a/lambda/park/GET/index.js b/lambda/park/GET/index.js index 8c6ca95..19a7765 100644 --- a/lambda/park/GET/index.js +++ b/lambda/park/GET/index.js @@ -3,7 +3,7 @@ const { runQuery, TABLE_NAME } = require('../../dynamoUtil'); const { sendResponse } = require('../../responseUtil'); exports.handler = async (event, context) => { - console.log('GET: Park', event); + console.log('GET: Park', event.queryStringParameters); let queryObj = { TableName: TABLE_NAME @@ -30,7 +30,7 @@ exports.handler = async (event, context) => { queryObj.ExpressionAttributeValues[':pk'] = { S: 'park::' + event.queryStringParameters?.orcs }; queryObj.KeyConditionExpression = 'pk =:pk'; - if (event.queryStringParameters?.subAreaName) { + if (event?.queryStringParameters?.subAreaName) { // sk for month or a range queryObj.ExpressionAttributeValues[':sk'] = { S: `${event.queryStringParameters?.subAreaName}` }; queryObj.KeyConditionExpression += ' AND sk =:sk'; @@ -44,9 +44,11 @@ exports.handler = async (event, context) => { queryObj.ExclusiveStartKey = parkData.LastEvaluatedKey; } while (typeof parkData.LastEvaluatedKey !== "undefined"); return sendResponse(200, parkData, context); + } else { + throw "Invalid parameters for call."; } } catch (err) { - console.log("EEE:", err); + console.error(err); return sendResponse(400, err, context); } }; \ No newline at end of file diff --git a/lambda/park/POST/index.js b/lambda/park/POST/index.js index d52fdd6..8ee2280 100644 --- a/lambda/park/POST/index.js +++ b/lambda/park/POST/index.js @@ -3,27 +3,34 @@ const { dynamodb, TABLE_NAME } = require('../../dynamoUtil'); const { sendResponse } = require('../../responseUtil'); exports.handler = async (event, context) => { - console.log('POST: Park', event.body); + try { + let body = JSON.parse(event.body); - // Set pk/sk - event.body["pk"] = "park"; - event.body["sk"] = event.body.orcs; - event.body['lastUpdated'] = new Date().toISOString(); + console.log("BO:", body); - let newObject = AWS.DynamoDB.Converter.marshall(event.body); + if (body.orcs === undefined) { + throw "Invalid request."; + } - let putObject = { - TableName: TABLE_NAME, - ConditionExpression: 'attribute_not_exists(pk) AND attribute_not_exists(sk)', - Item: newObject - }; + // Set pk/sk + body["pk"] = "park"; + body["sk"] = body.orcs; + body['lastUpdated'] = new Date().toISOString(); - try { + let newObject = AWS.DynamoDB.Converter.marshall(body); + + let putObject = { + TableName: TABLE_NAME, + ConditionExpression: 'attribute_not_exists(pk) AND attribute_not_exists(sk)', + Item: newObject + }; + + console.log("PUT:", putObject); // Add a basic park - const addParkResponse = await dynamodb.putItem(putObject).promise(); + await dynamodb.putItem(putObject).promise(); return sendResponse(200, { msg: "Put success"}, context); } catch (err) { - console.log("Error:", err); + console.error(err); return sendResponse(400, err, context); } }; \ No newline at end of file diff --git a/lambda/readConfig/index.js b/lambda/readConfig/index.js index f17ea74..61f1c5d 100644 --- a/lambda/readConfig/index.js +++ b/lambda/readConfig/index.js @@ -17,7 +17,7 @@ exports.handler = async (event, context) => { const configData = await runQuery(queryObj); return sendResponse(200, configData[0], context); } catch (err) { - console.log(err); + console.error(err); return sendResponse(400, err, context); } diff --git a/lambda/subarea/GET/index.js b/lambda/subarea/GET/index.js index e464e09..20323db 100644 --- a/lambda/subarea/GET/index.js +++ b/lambda/subarea/GET/index.js @@ -10,10 +10,10 @@ exports.handler = async (event, context) => { }; try { - if (event.queryStringParameters?.orcs - && event.queryStringParameters?.subAreaName - && event.queryStringParameters?.activity - && event.queryStringParameters?.date) { + if (event?.queryStringParameters?.orcs + && event?.queryStringParameters?.subAreaName + && event?.queryStringParameters?.activity + && event?.queryStringParameters?.date) { // Get the subarea details const orcs = event.queryStringParameters?.orcs; const subAreaName = event.queryStringParameters?.subAreaName; @@ -45,10 +45,10 @@ exports.handler = async (event, context) => { const { pk, sk, ...otherProps } = configData; return sendResponse(200, { ...parkData, config: otherProps }, context); } else { - return sendResponse(400, { msg: 'Invalid Request' }, context); + throw "Invalid parameter call."; } } catch (err) { - console.log("E:", err); + console.error(err); return sendResponse(400, err, context); } }; \ No newline at end of file diff --git a/lambda/subarea/POST/index.js b/lambda/subarea/POST/index.js index ff00f10..730dbd3 100644 --- a/lambda/subarea/POST/index.js +++ b/lambda/subarea/POST/index.js @@ -4,62 +4,76 @@ const { sendResponse } = require('../../responseUtil'); const format = require('date-fns/format') exports.handler = async (event, context) => { - console.log('POST: subarea', event); - - if (event.queryStringParameters.type === 'config') { - return await handleConfig(JSON.parse(event.body), context); - } else if (event.queryStringParameters.type === 'activity') { - // Handle standard monthly entry for this orc::subAreaName::activity - return await handleActivity(JSON.parse(event.body)); - } else { + + try { + console.log('POST: subarea', event); + if (event?.queryStringParameters?.type === 'config') { + return await handleConfig(JSON.parse(event.body), context); + } else if (event?.queryStringParameters?.type === 'activity') { + // Handle standard monthly entry for this orc::subAreaName::activity + return await handleActivity(JSON.parse(event.body)); + } else { + throw "Invalid request"; + } + } catch (err) { + console.error(err); return sendResponse(400, { msg: 'Invalid request'}, context); } }; async function handleActivity(body, context) { -// Set pk/sk - body["pk"] = `${body.orcs}::${body.subAreaName}::${body.activity}`; - - if (body.date.length !== 6 || isNaN(body.date)) { - return sendResponse(400, { msg: "Invalid date."}, context); - } + // Set pk/sk + try { + if (!body.orcs || !body.subAreaName || !body.activity || !body.date) { + throw "Invalid request."; + } - body["sk"] = body.date; - body['lastUpdated'] = new Date().toISOString(); + body["pk"] = `${body.orcs}::${body.subAreaName}::${body.activity}`; + + if (body.date.length !== 6 || isNaN(body.date)) { + throw "Invalid date."; + } - const newObject = AWS.DynamoDB.Converter.marshall(body); + body["sk"] = body.date; + body['lastUpdated'] = new Date().toISOString(); - let putObject = { - TableName: TABLE_NAME, - Item: newObject - }; + const newObject = AWS.DynamoDB.Converter.marshall(body); + + let putObject = { + TableName: TABLE_NAME, + Item: newObject + }; - try { await dynamodb.putItem(putObject).promise(); return sendResponse(200, {}, context); } catch (err) { - console.log("Error:", err); + console.error(err); return sendResponse(400, err, context); } } async function handleConfig(body, context) { - // Set pk/sk - body["pk"] = `${body.orcs}::${body.subAreaName}::${body.activity}`; - body["sk"] = 'config'; + try { + // Set pk/sk - const newObject = AWS.DynamoDB.Converter.marshall(body); + if (!body.orcs || !body.subAreaName || !body.activity) { + throw "Invalid request."; + } - let putObject = { - TableName: TABLE_NAME, - Item: newObject - }; + body["pk"] = `${body.orcs}::${body.subAreaName}::${body.activity}`; + body["sk"] = 'config'; - try { - const res = await dynamodb.putItem(putObject).promise(); - return sendResponse(200, res, context); + const newObject = AWS.DynamoDB.Converter.marshall(body); + + let putObject = { + TableName: TABLE_NAME, + Item: newObject, + }; + + await dynamodb.putItem(putObject).promise(); + return sendResponse(200, body, context); } catch (err) { - console.log("Error:", err); + console.error(err); return sendResponse(400, err, context); } } diff --git a/tools/convertParksSheet.js b/tools/convertParksSheet.js index 158fb45..bb623ab 100644 --- a/tools/convertParksSheet.js +++ b/tools/convertParksSheet.js @@ -178,7 +178,7 @@ async function updateItem(record, subarea) { try { const res = await dynamodb.updateItem(putParkObj).promise(); } catch (err) { - console.log("Error:", err); + console.error(err); } } @@ -204,7 +204,7 @@ async function putItem(record, overwrite = false) { return true; } catch (err) { if (record.sk.S == 'Halkett Bay') { - console.log("E:", err); + console.error(err); } return false; // Fall through, it already existed.